diff options
Diffstat (limited to 'components/feeds/FeedConverter.js')
-rw-r--r-- | components/feeds/FeedConverter.js | 591 |
1 files changed, 0 insertions, 591 deletions
diff --git a/components/feeds/FeedConverter.js b/components/feeds/FeedConverter.js deleted file mode 100644 index d0f5737..0000000 --- a/components/feeds/FeedConverter.js +++ /dev/null @@ -1,591 +0,0 @@ -/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */ -/* This Source Code Form is subject to the terms of the Mozilla Public - * License, v. 2.0. If a copy of the MPL was not distributed with this - * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ - -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/debug.js"); -Components.utils.import("resource://gre/modules/Services.jsm"); - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - -function LOG(str) { - dump("*** " + str + "\n"); -} - -const FS_CONTRACTID = "@mozilla.org/browser/feeds/result-service;1"; -const FPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=feed"; -const PCPH_CONTRACTID = "@mozilla.org/network/protocol;1?name=pcast"; - -const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed"; -const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed"; -const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed"; -const TYPE_ANY = "*/*"; - -const PREF_SELECTED_APP = "browser.feeds.handlers.application"; -const PREF_SELECTED_WEB = "browser.feeds.handlers.webservice"; -const PREF_SELECTED_ACTION = "browser.feeds.handler"; -const PREF_SELECTED_READER = "browser.feeds.handler.default"; - -const PREF_VIDEO_SELECTED_APP = "browser.videoFeeds.handlers.application"; -const PREF_VIDEO_SELECTED_WEB = "browser.videoFeeds.handlers.webservice"; -const PREF_VIDEO_SELECTED_ACTION = "browser.videoFeeds.handler"; -const PREF_VIDEO_SELECTED_READER = "browser.videoFeeds.handler.default"; - -const PREF_AUDIO_SELECTED_APP = "browser.audioFeeds.handlers.application"; -const PREF_AUDIO_SELECTED_WEB = "browser.audioFeeds.handlers.webservice"; -const PREF_AUDIO_SELECTED_ACTION = "browser.audioFeeds.handler"; -const PREF_AUDIO_SELECTED_READER = "browser.audioFeeds.handler.default"; - -function getPrefAppForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_APP; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_APP; - - default: - return PREF_SELECTED_APP; - } -} - -function getPrefWebForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_WEB; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_WEB; - - default: - return PREF_SELECTED_WEB; - } -} - -function getPrefActionForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_ACTION; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_ACTION; - - default: - return PREF_SELECTED_ACTION; - } -} - -function getPrefReaderForType(t) { - switch (t) { - case Ci.nsIFeed.TYPE_VIDEO: - return PREF_VIDEO_SELECTED_READER; - - case Ci.nsIFeed.TYPE_AUDIO: - return PREF_AUDIO_SELECTED_READER; - - default: - return PREF_SELECTED_READER; - } -} - -function safeGetCharPref(pref, defaultValue) { - var prefs = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - try { - return prefs.getCharPref(pref); - } - catch (e) { - } - return defaultValue; -} - -function FeedConverter() { -} -FeedConverter.prototype = { - classID: Components.ID("{229fa115-9412-4d32-baf3-2fc407f76fb1}"), - - /** - * This is the downloaded text data for the feed. - */ - _data: null, - - /** - * This is the object listening to the conversion, which is ultimately the - * docshell for the load. - */ - _listener: null, - - /** - * Records if the feed was sniffed - */ - _sniffed: false, - - /** - * See nsIStreamConverter.idl - */ - convert: function FC_convert(sourceStream, sourceType, destinationType, - context) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * See nsIStreamConverter.idl - */ - asyncConvertData: function FC_asyncConvertData(sourceType, destinationType, - listener, context) { - this._listener = listener; - }, - - /** - * Whether or not the preview page is being forced. - */ - _forcePreviewPage: false, - - /** - * Release our references to various things once we're done using them. - */ - _releaseHandles: function FC__releaseHandles() { - this._listener = null; - this._request = null; - this._processor = null; - }, - - /** - * See nsIFeedResultListener.idl - */ - handleResult: function FC_handleResult(result) { - // Feeds come in various content types, which our feed sniffer coerces to - // the maybe.feed type. However, feeds are used as a transport for - // different data types, e.g. news/blogs (traditional feed), video/audio - // (podcasts) and photos (photocasts, photostreams). Each of these is - // different in that there's a different class of application suitable for - // handling feeds of that type, but without a content-type differentiation - // it is difficult for us to disambiguate. - // - // The other problem is that if the user specifies an auto-action handler - // for one feed application, the fact that the content type is shared means - // that all other applications will auto-load with that handler too, - // regardless of the content-type. - // - // This means that content-type alone is not enough to determine whether - // or not a feed should be auto-handled. This means that for feeds we need - // to always use this stream converter, even when an auto-action is - // specified, not the basic one provided by WebContentConverter. This - // converter needs to consume all of the data and parse it, and based on - // that determination make a judgment about type. - // - // Since there are no content types for this content, and I'm not going to - // invent any, the upshot is that while a user can set an auto-handler for - // generic feed content, the system will prevent them from setting an auto- - // handler for other stream types. In those cases, the user will always see - // the preview page and have to select a handler. We can guess and show - // a client handler, but will not be able to show web handlers for those - // types. - // - // If this is just a feed, not some kind of specialized application, then - // auto-handlers can be set and we should obey them. - try { - var feedService = - Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - if (!this._forcePreviewPage && result.doc) { - var feed = result.doc.QueryInterface(Ci.nsIFeed); - var handler = safeGetCharPref(getPrefActionForType(feed.type), "ask"); - - if (handler != "ask") { - if (handler == "reader") - handler = safeGetCharPref(getPrefReaderForType(feed.type), "bookmarks"); - switch (handler) { - case "web": - var wccr = - Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. - getService(Ci.nsIWebContentConverterService); - if ((feed.type == Ci.nsIFeed.TYPE_FEED && - wccr.getAutoHandler(TYPE_MAYBE_FEED)) || - (feed.type == Ci.nsIFeed.TYPE_VIDEO && - wccr.getAutoHandler(TYPE_MAYBE_VIDEO_FEED)) || - (feed.type == Ci.nsIFeed.TYPE_AUDIO && - wccr.getAutoHandler(TYPE_MAYBE_AUDIO_FEED))) { - wccr.loadPreferredHandler(this._request); - return; - } - break; - - default: - LOG("unexpected handler: " + handler); - // fall through -- let feed service handle error - case "bookmarks": - case "client": - try { - var title = feed.title ? feed.title.plainText() : ""; - var desc = feed.subtitle ? feed.subtitle.plainText() : ""; - feedService.addToClientReader(result.uri.spec, title, desc, feed.type); - return; - } catch(ex) { /* fallback to preview mode */ } - } - } - } - - var ios = - Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var chromeChannel; - - // handling a redirect, hence forwarding the loadInfo from the old channel - // to the newchannel. - var oldChannel = this._request.QueryInterface(Ci.nsIChannel); - var loadInfo = oldChannel.loadInfo; - - // If there was no automatic handler, or this was a podcast, - // photostream or some other kind of application, show the preview page - // if the parser returned a document. - if (result.doc) { - - // Store the result in the result service so that the display - // page can access it. - feedService.addFeedResult(result); - - // Now load the actual XUL document. - var aboutFeedsURI = ios.newURI("about:feeds", null, null); - chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo); - chromeChannel.originalURI = result.uri; - chromeChannel.owner = - Services.scriptSecurityManager.getNoAppCodebasePrincipal(aboutFeedsURI); - } else { - chromeChannel = ios.newChannelFromURIWithLoadInfo(result.uri, loadInfo); - } - - chromeChannel.loadGroup = this._request.loadGroup; - chromeChannel.asyncOpen2(this._listener); - } - finally { - this._releaseHandles(); - } - }, - - /** - * See nsIStreamListener.idl - */ - onDataAvailable: function FC_onDataAvailable(request, context, inputStream, - sourceOffset, count) { - if (this._processor) - this._processor.onDataAvailable(request, context, inputStream, - sourceOffset, count); - }, - - /** - * See nsIRequestObserver.idl - */ - onStartRequest: function FC_onStartRequest(request, context) { - var channel = request.QueryInterface(Ci.nsIChannel); - - // Check for a header that tells us there was no sniffing - // The value doesn't matter. - try { - var httpChannel = channel.QueryInterface(Ci.nsIHttpChannel); - // Make sure to check requestSucceeded before the potentially-throwing - // getResponseHeader. - if (!httpChannel.requestSucceeded) { - // Just give up, but don't forget to cancel the channel first! - request.cancel(Cr.NS_BINDING_ABORTED); - return; - } - var noSniff = httpChannel.getResponseHeader("X-Moz-Is-Feed"); - } - catch (ex) { - this._sniffed = true; - } - - this._request = request; - - // Save and reset the forced state bit early, in case there's some kind of - // error. - var feedService = - Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - this._forcePreviewPage = feedService.forcePreviewPage; - feedService.forcePreviewPage = false; - - // Parse feed data as it comes in - this._processor = - Cc["@mozilla.org/feed-processor;1"]. - createInstance(Ci.nsIFeedProcessor); - this._processor.listener = this; - this._processor.parseAsync(null, channel.URI); - - this._processor.onStartRequest(request, context); - }, - - /** - * See nsIRequestObserver.idl - */ - onStopRequest: function FC_onStopRequest(request, context, status) { - if (this._processor) - this._processor.onStopRequest(request, context, status); - }, - - /** - * See nsISupports.idl - */ - QueryInterface: function FC_QueryInterface(iid) { - if (iid.equals(Ci.nsIFeedResultListener) || - iid.equals(Ci.nsIStreamConverter) || - iid.equals(Ci.nsIStreamListener) || - iid.equals(Ci.nsIRequestObserver)|| - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - }, -}; - -/** - * Keeps parsed FeedResults around for use elsewhere in the UI after the stream - * converter completes. - */ -function FeedResultService() { -} - -FeedResultService.prototype = { - classID: Components.ID("{2376201c-bbc6-472f-9b62-7548040a61c6}"), - - /** - * A URI spec -> [nsIFeedResult] hash. We have to keep a list as the - * value in case the same URI is requested concurrently. - */ - _results: { }, - - /** - * See nsIFeedResultService.idl - */ - forcePreviewPage: false, - - /** - * See nsIFeedResultService.idl - */ - addToClientReader: function FRS_addToClientReader(spec, title, subtitle, feedType) { - var prefs = - Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - - var handler = safeGetCharPref(getPrefActionForType(feedType), "bookmarks"); - if (handler == "ask" || handler == "reader") - handler = safeGetCharPref(getPrefReaderForType(feedType), "bookmarks"); - - switch (handler) { - case "client": - var clientApp = prefs.getComplexValue(getPrefAppForType(feedType), Ci.nsILocalFile); - - // For the benefit of applications that might know how to deal with more - // URLs than just feeds, send feed: URLs in the following format: - // - // http urls: replace scheme with feed, e.g. - // http://foo.com/index.rdf -> feed://foo.com/index.rdf - // other urls: prepend feed: scheme, e.g. - // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf - var ios = - Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - var feedURI = ios.newURI(spec, null, null); - if (feedURI.schemeIs("http")) { - feedURI.scheme = "feed"; - spec = feedURI.spec; - } - else - spec = "feed:" + spec; - - // Retrieving the shell service might fail on some systems, most - // notably systems where GNOME is not installed. - try { - var ss = - Cc["@mozilla.org/browser/shell-service;1"]. - getService(Ci.nsIShellService); - ss.openApplicationWithURI(clientApp, spec); - } catch(e) { - // If we couldn't use the shell service, fallback to using a - // nsIProcess instance - var p = - Cc["@mozilla.org/process/util;1"]. - createInstance(Ci.nsIProcess); - p.init(clientApp); - p.run(false, [spec], 1); - } - break; - - default: - // "web" should have been handled elsewhere - LOG("unexpected handler: " + handler); - // fall through - case "bookmarks": - var wm = - Cc["@mozilla.org/appshell/window-mediator;1"]. - getService(Ci.nsIWindowMediator); - var topWindow = wm.getMostRecentWindow("navigator:browser"); - topWindow.PlacesCommandHook.addLiveBookmark(spec, title, subtitle); - break; - } - }, - - /** - * See nsIFeedResultService.idl - */ - addFeedResult: function FRS_addFeedResult(feedResult) { - NS_ASSERT(feedResult.uri != null, "null URI!"); - NS_ASSERT(feedResult.uri != null, "null feedResult!"); - var spec = feedResult.uri.spec; - if(!this._results[spec]) - this._results[spec] = []; - this._results[spec].push(feedResult); - }, - - /** - * See nsIFeedResultService.idl - */ - getFeedResult: function RFS_getFeedResult(uri) { - NS_ASSERT(uri != null, "null URI!"); - var resultList = this._results[uri.spec]; - for (var i in resultList) { - if (resultList[i].uri == uri) - return resultList[i]; - } - return null; - }, - - /** - * See nsIFeedResultService.idl - */ - removeFeedResult: function FRS_removeFeedResult(uri) { - NS_ASSERT(uri != null, "null URI!"); - var resultList = this._results[uri.spec]; - if (!resultList) - return; - var deletions = 0; - for (var i = 0; i < resultList.length; ++i) { - if (resultList[i].uri == uri) { - delete resultList[i]; - ++deletions; - } - } - - // send the holes to the end - resultList.sort(); - // and trim the list - resultList.splice(resultList.length - deletions, deletions); - if (resultList.length == 0) - delete this._results[uri.spec]; - }, - - createInstance: function FRS_createInstance(outer, iid) { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.QueryInterface(iid); - }, - - QueryInterface: function FRS_QueryInterface(iid) { - if (iid.equals(Ci.nsIFeedResultService) || - iid.equals(Ci.nsIFactory) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, -}; - -/** - * A protocol handler that attempts to deal with the variant forms of feed: - * URIs that are actually either http or https. - */ -function GenericProtocolHandler() { -} -GenericProtocolHandler.prototype = { - _init: function GPH_init(scheme) { - var ios = - Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - this._http = ios.getProtocolHandler("http"); - this._scheme = scheme; - }, - - get scheme() { - return this._scheme; - }, - - get protocolFlags() { - return this._http.protocolFlags; - }, - - get defaultPort() { - return this._http.defaultPort; - }, - - allowPort: function GPH_allowPort(port, scheme) { - return this._http.allowPort(port, scheme); - }, - - newURI: function GPH_newURI(spec, originalCharset, baseURI) { - // Feed URIs can be either nested URIs of the form feed:realURI (in which - // case we create a nested URI for the realURI) or feed://example.com, in - // which case we create a nested URI for the real protocol which is http. - - var scheme = this._scheme + ":"; - if (spec.substr(0, scheme.length) != scheme) - throw Cr.NS_ERROR_MALFORMED_URI; - - var prefix = spec.substr(scheme.length, 2) == "//" ? "http:" : ""; - var inner = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService).newURI(spec.replace(scheme, prefix), - originalCharset, baseURI); - var netutil = Cc["@mozilla.org/network/util;1"].getService(Ci.nsINetUtil); - const URI_INHERITS_SECURITY_CONTEXT = Ci.nsIProtocolHandler - .URI_INHERITS_SECURITY_CONTEXT; - if (netutil.URIChainHasFlags(inner, URI_INHERITS_SECURITY_CONTEXT)) - throw Cr.NS_ERROR_MALFORMED_URI; - - var uri = netutil.newSimpleNestedURI(inner); - uri.spec = inner.spec.replace(prefix, scheme); - return uri; - }, - - newChannel2: function GPH_newChannel(aUri, aLoadInfo) { - var inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI; - var channel = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService). - newChannelFromURIWithLoadInfo(inner, aLoadInfo); - - if (channel instanceof Components.interfaces.nsIHttpChannel) - // Set this so we know this is supposed to be a feed - channel.setRequestHeader("X-Moz-Is-Feed", "1", false); - channel.originalURI = aUri; - return channel; - }, - - - QueryInterface: function GPH_QueryInterface(iid) { - if (iid.equals(Ci.nsIProtocolHandler) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -function FeedProtocolHandler() { - this._init('feed'); -} -FeedProtocolHandler.prototype = new GenericProtocolHandler(); -FeedProtocolHandler.prototype.classID = Components.ID("{4f91ef2e-57ba-472e-ab7a-b4999e42d6c0}"); - -function PodCastProtocolHandler() { - this._init('pcast'); -} -PodCastProtocolHandler.prototype = new GenericProtocolHandler(); -PodCastProtocolHandler.prototype.classID = Components.ID("{1c31ed79-accd-4b94-b517-06e0c81999d5}"); - -var components = [FeedConverter, - FeedResultService, - FeedProtocolHandler, - PodCastProtocolHandler]; - - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory(components); |