diff options
author | wolfbeast <mcwerewolf@gmail.com> | 2018-07-18 08:24:24 +0200 |
---|---|---|
committer | wolfbeast <mcwerewolf@gmail.com> | 2018-07-18 08:24:24 +0200 |
commit | fc61780b35af913801d72086456f493f63197da6 (patch) | |
tree | f85891288a7bd988da9f0f15ae64e5c63f00d493 /browser/components/feeds | |
parent | 69f7f9e5f1475891ce11cc4f431692f965b0cd30 (diff) | |
parent | 50d3e596bbe89c95615f96eb71f6bc5be737a1db (diff) | |
download | UXP-fc61780b35af913801d72086456f493f63197da6.tar UXP-fc61780b35af913801d72086456f493f63197da6.tar.gz UXP-fc61780b35af913801d72086456f493f63197da6.tar.lz UXP-fc61780b35af913801d72086456f493f63197da6.tar.xz UXP-fc61780b35af913801d72086456f493f63197da6.zip |
Merge commit '50d3e596bbe89c95615f96eb71f6bc5be737a1db' into Basilisk-releasev2018.07.18
# Conflicts:
# browser/app/profile/firefox.js
# browser/components/preferences/jar.mn
Diffstat (limited to 'browser/components/feeds')
-rw-r--r-- | browser/components/feeds/BrowserFeeds.manifest | 25 | ||||
-rw-r--r-- | browser/components/feeds/FeedConverter.js | 568 | ||||
-rw-r--r-- | browser/components/feeds/FeedWriter.js | 1007 | ||||
-rw-r--r-- | browser/components/feeds/WebContentConverter.js | 1071 | ||||
-rw-r--r-- | browser/components/feeds/content/subscribe.js | 25 | ||||
-rw-r--r-- | browser/components/feeds/content/subscribe.xhtml | 74 | ||||
-rw-r--r-- | browser/components/feeds/jar.mn | 7 | ||||
-rw-r--r-- | browser/components/feeds/moz.build | 34 | ||||
-rw-r--r-- | browser/components/feeds/nsFeedSniffer.cpp | 363 | ||||
-rw-r--r-- | browser/components/feeds/nsFeedSniffer.h | 37 | ||||
-rw-r--r-- | browser/components/feeds/nsIFeedResultService.idl | 70 | ||||
-rw-r--r-- | browser/components/feeds/nsIWebContentConverterRegistrar.idl | 117 |
12 files changed, 0 insertions, 3398 deletions
diff --git a/browser/components/feeds/BrowserFeeds.manifest b/browser/components/feeds/BrowserFeeds.manifest deleted file mode 100644 index ac5c299fa..000000000 --- a/browser/components/feeds/BrowserFeeds.manifest +++ /dev/null @@ -1,25 +0,0 @@ -# This component must restrict its registration for the app-startup category -# to the specific list of apps that use it so it doesn't get loaded in xpcshell. -# Thus we restrict it to these apps: -# -# b2g: {3c2e2abc-06d4-11e1-ac3b-374f68613e61} -# browser: {ec8030f7-c20a-464f-9b0e-13a3a9e97384} -# mobile/android: {aa3c5121-dab2-40e2-81ca-7ea25febc110} -# mobile/xul: {a23983c0-fd0e-11dc-95ff-0800200c9a66} -# graphene: {d1bfe7d9-c01e-4237-998b-7b5f960a4314} - -component {229fa115-9412-4d32-baf3-2fc407f76fb1} FeedConverter.js -contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1} -contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.video.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1} -contract @mozilla.org/streamconv;1?from=application/vnd.mozilla.maybe.audio.feed&to=*/* {229fa115-9412-4d32-baf3-2fc407f76fb1} -component {2376201c-bbc6-472f-9b62-7548040a61c6} FeedConverter.js -contract @mozilla.org/browser/feeds/result-service;1 {2376201c-bbc6-472f-9b62-7548040a61c6} -component {4f91ef2e-57ba-472e-ab7a-b4999e42d6c0} FeedConverter.js -contract @mozilla.org/network/protocol;1?name=feed {4f91ef2e-57ba-472e-ab7a-b4999e42d6c0} -component {1c31ed79-accd-4b94-b517-06e0c81999d5} FeedConverter.js -contract @mozilla.org/network/protocol;1?name=pcast {1c31ed79-accd-4b94-b517-06e0c81999d5} -component {49bb6593-3aff-4eb3-a068-2712c28bd58e} FeedWriter.js -contract @mozilla.org/browser/feeds/result-writer;1 {49bb6593-3aff-4eb3-a068-2712c28bd58e} -component {792a7e82-06a0-437c-af63-b2d12e808acc} WebContentConverter.js -contract @mozilla.org/embeddor.implemented/web-content-handler-registrar;1 {792a7e82-06a0-437c-af63-b2d12e808acc} -category app-startup WebContentConverter service,@mozilla.org/embeddor.implemented/web-content-handler-registrar;1 application={3c2e2abc-06d4-11e1-ac3b-374f68613e61} application={ec8030f7-c20a-464f-9b0e-13a3a9e97384} application={aa3c5121-dab2-40e2-81ca-7ea25febc110} application={a23983c0-fd0e-11dc-95ff-0800200c9a66} application={d1bfe7d9-c01e-4237-998b-7b5f960a4314} diff --git a/browser/components/feeds/FeedConverter.js b/browser/components/feeds/FeedConverter.js deleted file mode 100644 index c2c565608..000000000 --- a/browser/components/feeds/FeedConverter.js +++ /dev/null @@ -1,568 +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(sourceStream, sourceType, destinationType, - context) { - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * See nsIStreamConverter.idl - */ - 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() { - this._listener = null; - this._request = null; - this._processor = null; - }, - - /** - * See nsIFeedResultListener.idl - */ - 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 { - let feedService = - Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - if (!this._forcePreviewPage && result.doc) { - let feed = result.doc.QueryInterface(Ci.nsIFeed); - let handler = safeGetCharPref(getPrefActionForType(feed.type), "ask"); - - if (handler != "ask") { - if (handler == "reader") - handler = safeGetCharPref(getPrefReaderForType(feed.type), "bookmarks"); - switch (handler) { - case "web": - let 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": - case "default": - try { - let title = feed.title ? feed.title.plainText() : ""; - let desc = feed.subtitle ? feed.subtitle.plainText() : ""; - feedService.addToClientReader(result.uri.spec, title, desc, feed.type, handler); - return; - } catch (ex) { /* fallback to preview mode */ } - } - } - } - - let ios = - Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - let chromeChannel; - - // handling a redirect, hence forwarding the loadInfo from the old channel - // to the newchannel. - let oldChannel = this._request.QueryInterface(Ci.nsIChannel); - let 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. - let aboutFeedsURI = ios.newURI("about:feeds", null, null); - chromeChannel = ios.newChannelFromURIWithLoadInfo(aboutFeedsURI, loadInfo); - chromeChannel.originalURI = result.uri; - - // carry the origin attributes from the channel that loaded the feed. - chromeChannel.owner = - Services.scriptSecurityManager.createCodebasePrincipal(aboutFeedsURI, - loadInfo.originAttributes); - } else { - chromeChannel = ios.newChannelFromURIWithLoadInfo(result.uri, loadInfo); - } - - chromeChannel.loadGroup = this._request.loadGroup; - chromeChannel.asyncOpen2(this._listener); - } - finally { - this._releaseHandles(); - } - }, - - /** - * See nsIStreamListener.idl - */ - onDataAvailable(request, context, inputStream, - sourceOffset, count) { - if (this._processor) - this._processor.onDataAvailable(request, context, inputStream, - sourceOffset, count); - }, - - /** - * See nsIRequestObserver.idl - */ - onStartRequest(request, context) { - let channel = request.QueryInterface(Ci.nsIChannel); - - // Check for a header that tells us there was no sniffing - // The value doesn't matter. - try { - let 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; - } - - // Note: this throws if the header is not set. - 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. - let 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(request, context, status) { - if (this._processor) - this._processor.onStopRequest(request, context, status); - }, - - /** - * See nsISupports.idl - */ - 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(spec, title, subtitle, feedType, feedReader) { - if (!feedReader) { - feedReader = "default"; - } - - let handler = safeGetCharPref(getPrefActionForType(feedType), "bookmarks"); - if (handler == "ask" || handler == "reader") - handler = feedReader; - - switch (handler) { - case "client": - Services.cpmm.sendAsyncMessage("FeedConverter:ExecuteClientApp", - { spec, - title, - subtitle, - feedHandler: getPrefAppForType(feedType) }); - break; - case "default": - // Default system feed reader - Services.cpmm.sendAsyncMessage("FeedConverter:ExecuteClientApp", - { spec, - title, - subtitle, - feedHandler: "default" }); - break; - default: - // "web" should have been handled elsewhere - LOG("unexpected handler: " + handler); - // fall through - case "bookmarks": - Services.cpmm.sendAsyncMessage("FeedConverter:addLiveBookmark", - { spec, title, subtitle }); - break; - } - }, - - /** - * See nsIFeedResultService.idl - */ - addFeedResult(feedResult) { - NS_ASSERT(feedResult.uri != null, "null URI!"); - NS_ASSERT(feedResult.uri != null, "null feedResult!"); - let spec = feedResult.uri.spec; - if (!this._results[spec]) - this._results[spec] = []; - this._results[spec].push(feedResult); - }, - - /** - * See nsIFeedResultService.idl - */ - getFeedResult(uri) { - NS_ASSERT(uri != null, "null URI!"); - let resultList = this._results[uri.spec]; - for (let result of resultList) { - if (result.uri == uri) - return result; - } - return null; - }, - - /** - * See nsIFeedResultService.idl - */ - removeFeedResult(uri) { - NS_ASSERT(uri != null, "null URI!"); - let resultList = this._results[uri.spec]; - if (!resultList) - return; - let deletions = 0; - for (let 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(outer, iid) { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.QueryInterface(iid); - }, - - 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(scheme) { - let 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() { - let {URI_DANGEROUS_TO_LOAD, ALLOWS_PROXY_HTTP, ALLOWS_PROXY} = - Ci.nsIProtocolHandler; - return URI_DANGEROUS_TO_LOAD | ALLOWS_PROXY | ALLOWS_PROXY_HTTP; - }, - - get defaultPort() { - return this._http.defaultPort; - }, - - allowPort(port, scheme) { - return this._http.allowPort(port, scheme); - }, - - 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. - - let scheme = this._scheme + ":"; - if (spec.substr(0, scheme.length) != scheme) - throw Cr.NS_ERROR_MALFORMED_URI; - - let prefix = spec.substr(scheme.length, 2) == "//" ? "http:" : ""; - let inner = Services.io.newURI(spec.replace(scheme, prefix), - originalCharset, baseURI); - - if (!["http", "https"].includes(inner.scheme)) - throw Cr.NS_ERROR_MALFORMED_URI; - - let uri = Services.io.QueryInterface(Ci.nsINetUtil).newSimpleNestedURI(inner); - uri.spec = inner.spec.replace(prefix, scheme); - return uri; - }, - - newChannel2(aUri, aLoadInfo) { - let inner = aUri.QueryInterface(Ci.nsINestedURI).innerURI; - let 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(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); diff --git a/browser/components/feeds/FeedWriter.js b/browser/components/feeds/FeedWriter.js deleted file mode 100644 index 20f1399b0..000000000 --- a/browser/components/feeds/FeedWriter.js +++ /dev/null @@ -1,1007 +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/. */ - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; -const Cu = Components.utils; - -Cu.import("resource://gre/modules/XPCOMUtils.jsm"); -Cu.import("resource://gre/modules/Services.jsm"); -Cu.import("resource://gre/modules/NetUtil.jsm"); - -const FEEDWRITER_CID = Components.ID("{49bb6593-3aff-4eb3-a068-2712c28bd58e}"); -const FEEDWRITER_CONTRACTID = "@mozilla.org/browser/feeds/result-writer;1"; - -function LOG(str) { - let prefB = Cc["@mozilla.org/preferences-service;1"]. - getService(Ci.nsIPrefBranch); - - let shouldLog = false; - try { - shouldLog = prefB.getBoolPref("feeds.log"); - } - catch (ex) { - } - - if (shouldLog) - dump("*** Feeds: " + str + "\n"); -} - -/** - * Wrapper function for nsIIOService::newURI. - * @param aURLSpec - * The URL string from which to create an nsIURI. - * @returns an nsIURI object, or null if the creation of the URI failed. - */ -function makeURI(aURLSpec, aCharset) { - let ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - try { - return ios.newURI(aURLSpec, aCharset, null); - } catch (ex) { } - - return null; -} - -const XML_NS = "http://www.w3.org/XML/1998/namespace"; -const HTML_NS = "http://www.w3.org/1999/xhtml"; -const XUL_NS = "http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"; -const URI_BUNDLE = "chrome://browser/locale/feeds/subscribe.properties"; - -const TITLE_ID = "feedTitleText"; -const SUBTITLE_ID = "feedSubtitleText"; - -/** - * Converts a number of bytes to the appropriate unit that results in a - * number that needs fewer than 4 digits - * - * @return a pair: [new value with 3 sig. figs., its unit] - */ -function convertByteUnits(aBytes) { - let units = ["bytes", "kilobyte", "megabyte", "gigabyte"]; - let unitIndex = 0; - - // convert to next unit if it needs 4 digits (after rounding), but only if - // we know the name of the next unit - while ((aBytes >= 999.5) && (unitIndex < units.length - 1)) { - aBytes /= 1024; - unitIndex++; - } - - // Get rid of insignificant bits by truncating to 1 or 0 decimal points - // 0 -> 0; 1.2 -> 1.2; 12.3 -> 12.3; 123.4 -> 123; 234.5 -> 235 - aBytes = aBytes.toFixed((aBytes > 0) && (aBytes < 100) ? 1 : 0); - - return [aBytes, units[unitIndex]]; -} - -function FeedWriter() { - this._selectedApp = undefined; - this._selectedAppMenuItem = null; - this._subscribeCallback = null; - this._defaultHandlerMenuItem = null; -} - -FeedWriter.prototype = { - _getPropertyAsBag(container, property) { - return container.fields.getProperty(property). - QueryInterface(Ci.nsIPropertyBag2); - }, - - _getPropertyAsString(container, property) { - try { - return container.fields.getPropertyAsAString(property); - } - catch (e) { - } - return ""; - }, - - _setContentText(id, text) { - let element = this._document.getElementById(id); - let textNode = text.createDocumentFragment(element); - while (element.hasChildNodes()) - element.removeChild(element.firstChild); - element.appendChild(textNode); - if (text.base) { - element.setAttributeNS(XML_NS, 'base', text.base.spec); - } - }, - - /** - * Safely sets the href attribute on an anchor tag, providing the URI - * specified can be loaded according to rules. - * @param element - * The element to set a URI attribute on - * @param attribute - * The attribute of the element to set the URI to, e.g. href or src - * @param uri - * The URI spec to set as the href - */ - _safeSetURIAttribute(element, attribute, uri) { - let secman = Cc["@mozilla.org/scriptsecuritymanager;1"]. - getService(Ci.nsIScriptSecurityManager); - const flags = Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL; - try { - // TODO Is this necessary? - secman.checkLoadURIStrWithPrincipal(this._feedPrincipal, uri, flags); - // checkLoadURIStrWithPrincipal will throw if the link URI should not be - // loaded, either because our feedURI isn't allowed to load it or per - // the rules specified in |flags|, so we'll never "linkify" the link... - } - catch (e) { - // Not allowed to load this link because secman.checkLoadURIStr threw - return; - } - - element.setAttribute(attribute, uri); - }, - - __bundle: null, - get _bundle() { - if (!this.__bundle) { - this.__bundle = Cc["@mozilla.org/intl/stringbundle;1"]. - getService(Ci.nsIStringBundleService). - createBundle(URI_BUNDLE); - } - return this.__bundle; - }, - - _getFormattedString(key, params) { - return this._bundle.formatStringFromName(key, params, params.length); - }, - - _getString(key) { - return this._bundle.GetStringFromName(key); - }, - - _setCheckboxCheckedState(aValue) { - let checkbox = this._document.getElementById("alwaysUse"); - if (checkbox) { - // see checkbox.xml, xbl bindings are not applied within the sandbox! TODO - let change = (aValue != (checkbox.getAttribute("checked") == "true")); - if (aValue) - checkbox.setAttribute("checked", "true"); - else - checkbox.removeAttribute("checked"); - - if (change) { - let event = this._document.createEvent("Events"); - event.initEvent("CheckboxStateChange", true, true); - checkbox.dispatchEvent(event); - } - } - }, - - /** - * Returns a date suitable for displaying in the feed preview. - * If the date cannot be parsed, the return value is "false". - * @param dateString - * A date as extracted from a feed entry. (entry.updated) - */ - _parseDate(dateString) { - // Convert the date into the user's local time zone - let dateObj = new Date(dateString); - - // Make sure the date we're given is valid. - if (!dateObj.getTime()) - return false; - - return this._dateFormatter.format(dateObj); - }, - - __dateFormatter: null, - get _dateFormatter() { - if (!this.__dateFormatter) { - const locale = Cc["@mozilla.org/chrome/chrome-registry;1"] - .getService(Ci.nsIXULChromeRegistry) - .getSelectedLocale("global", true); - const dtOptions = { year: 'numeric', month: 'long', day: 'numeric', - hour: 'numeric', minute: 'numeric' }; - this.__dateFormatter = new Intl.DateTimeFormat(locale, dtOptions); - } - return this.__dateFormatter; - }, - - /** - * Returns the feed type. - */ - __feedType: null, - _getFeedType() { - if (this.__feedType != null) - return this.__feedType; - - try { - // grab the feed because it's got the feed.type in it. - let container = this._getContainer(); - let feed = container.QueryInterface(Ci.nsIFeed); - this.__feedType = feed.type; - return feed.type; - } catch (ex) { } - - return Ci.nsIFeed.TYPE_FEED; - }, - - /** - * Writes the feed title into the preview document. - * @param container - * The feed container - */ - _setTitleText(container) { - if (container.title) { - let title = container.title.plainText(); - this._setContentText(TITLE_ID, container.title); - this._document.title = title; - } - - let feed = container.QueryInterface(Ci.nsIFeed); - if (feed && feed.subtitle) - this._setContentText(SUBTITLE_ID, container.subtitle); - }, - - /** - * Writes the title image into the preview document if one is present. - * @param container - * The feed container - */ - _setTitleImage(container) { - try { - let parts = container.image; - - // Set up the title image (supplied by the feed) - let feedTitleImage = this._document.getElementById("feedTitleImage"); - this._safeSetURIAttribute(feedTitleImage, "src", - parts.getPropertyAsAString("url")); - - // Set up the title image link - let feedTitleLink = this._document.getElementById("feedTitleLink"); - - let titleText = this._getFormattedString("linkTitleTextFormat", - [parts.getPropertyAsAString("title")]); - let feedTitleText = this._document.getElementById("feedTitleText"); - let titleImageWidth = parseInt(parts.getPropertyAsAString("width")) + 15; - - // Fix the margin on the main title, so that the image doesn't run over - // the underline - feedTitleLink.setAttribute('title', titleText); - feedTitleText.style.marginRight = titleImageWidth + 'px'; - - this._safeSetURIAttribute(feedTitleLink, "href", - parts.getPropertyAsAString("link")); - } - catch (e) { - LOG("Failed to set Title Image (this is benign): " + e); - } - }, - - /** - * Writes all entries contained in the feed. - * @param container - * The container of entries in the feed - */ - _writeFeedContent(container) { - // Build the actual feed content - let feed = container.QueryInterface(Ci.nsIFeed); - if (feed.items.length == 0) - return; - - let feedContent = this._document.getElementById("feedContent"); - - for (let i = 0; i < feed.items.length; ++i) { - let entry = feed.items.queryElementAt(i, Ci.nsIFeedEntry); - entry.QueryInterface(Ci.nsIFeedContainer); - - let entryContainer = this._document.createElementNS(HTML_NS, "div"); - entryContainer.className = "entry"; - - // If the entry has a title, make it a link - if (entry.title) { - let a = this._document.createElementNS(HTML_NS, "a"); - let span = this._document.createElementNS(HTML_NS, "span"); - a.appendChild(span); - if (entry.title.base) - span.setAttributeNS(XML_NS, "base", entry.title.base.spec); - span.appendChild(entry.title.createDocumentFragment(a)); - - // Entries are not required to have links, so entry.link can be null. - if (entry.link) - this._safeSetURIAttribute(a, "href", entry.link.spec); - - let title = this._document.createElementNS(HTML_NS, "h3"); - title.appendChild(a); - - let lastUpdated = this._parseDate(entry.updated); - if (lastUpdated) { - let dateDiv = this._document.createElementNS(HTML_NS, "div"); - dateDiv.className = "lastUpdated"; - dateDiv.textContent = lastUpdated; - title.appendChild(dateDiv); - } - - entryContainer.appendChild(title); - } - - let body = this._document.createElementNS(HTML_NS, "div"); - let summary = entry.summary || entry.content; - let docFragment = null; - if (summary) { - if (summary.base) - body.setAttributeNS(XML_NS, "base", summary.base.spec); - else - LOG("no base?"); - docFragment = summary.createDocumentFragment(body); - if (docFragment) - body.appendChild(docFragment); - - // If the entry doesn't have a title, append a # permalink - // See http://scripting.com/rss.xml for an example - if (!entry.title && entry.link) { - let a = this._document.createElementNS(HTML_NS, "a"); - a.appendChild(this._document.createTextNode("#")); - this._safeSetURIAttribute(a, "href", entry.link.spec); - body.appendChild(this._document.createTextNode(" ")); - body.appendChild(a); - } - - } - body.className = "feedEntryContent"; - entryContainer.appendChild(body); - - if (entry.enclosures && entry.enclosures.length > 0) { - let enclosuresDiv = this._buildEnclosureDiv(entry); - entryContainer.appendChild(enclosuresDiv); - } - - let clearDiv = this._document.createElementNS(HTML_NS, "div"); - clearDiv.style.clear = "both"; - - feedContent.appendChild(entryContainer); - feedContent.appendChild(clearDiv); - } - }, - - /** - * Takes a url to a media item and returns the best name it can come up with. - * Frequently this is the filename portion (e.g. passing in - * http://example.com/foo.mpeg would return "foo.mpeg"), but in more complex - * cases, this will return the entire url (e.g. passing in - * http://example.com/somedirectory/ would return - * http://example.com/somedirectory/). - * @param aURL - * The URL string from which to create a display name - * @returns a string - */ - _getURLDisplayName(aURL) { - let url = makeURI(aURL); - url.QueryInterface(Ci.nsIURL); - if (url == null || url.fileName.length == 0) - return decodeURIComponent(aURL); - - return decodeURIComponent(url.fileName); - }, - - /** - * Takes a FeedEntry with enclosures, generates the HTML code to represent - * them, and returns that. - * @param entry - * FeedEntry with enclosures - * @returns element - */ - _buildEnclosureDiv(entry) { - let enclosuresDiv = this._document.createElementNS(HTML_NS, "div"); - enclosuresDiv.className = "enclosures"; - - enclosuresDiv.appendChild(this._document.createTextNode(this._getString("mediaLabel"))); - - for (let i_enc = 0; i_enc < entry.enclosures.length; ++i_enc) { - let enc = entry.enclosures.queryElementAt(i_enc, Ci.nsIWritablePropertyBag2); - - if (!(enc.hasKey("url"))) - continue; - - let enclosureDiv = this._document.createElementNS(HTML_NS, "div"); - enclosureDiv.setAttribute("class", "enclosure"); - - let mozicon = "moz-icon://.txt?size=16"; - let type_text = null; - let size_text = null; - - if (enc.hasKey("type")) { - type_text = enc.get("type"); - if (enc.hasKey("typeDesc")) - type_text = enc.get("typeDesc"); - - if (type_text && type_text.length > 0) - mozicon = "moz-icon://goat?size=16&contentType=" + enc.get("type"); - } - - if (enc.hasKey("length") && /^[0-9]+$/.test(enc.get("length"))) { - let enc_size = convertByteUnits(parseInt(enc.get("length"))); - - size_text = this._getFormattedString("enclosureSizeText", - [enc_size[0], - this._getString(enc_size[1])]); - } - - let iconimg = this._document.createElementNS(HTML_NS, "img"); - iconimg.setAttribute("src", mozicon); - iconimg.setAttribute("class", "type-icon"); - enclosureDiv.appendChild(iconimg); - - enclosureDiv.appendChild(this._document.createTextNode( " " )); - - let enc_href = this._document.createElementNS(HTML_NS, "a"); - enc_href.appendChild(this._document.createTextNode(this._getURLDisplayName(enc.get("url")))); - this._safeSetURIAttribute(enc_href, "href", enc.get("url")); - enclosureDiv.appendChild(enc_href); - - if (type_text && size_text) - enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ", " + size_text + ")")); - - else if (type_text) - enclosureDiv.appendChild(this._document.createTextNode( " (" + type_text + ")")) - - else if (size_text) - enclosureDiv.appendChild(this._document.createTextNode( " (" + size_text + ")")) - - enclosuresDiv.appendChild(enclosureDiv); - } - - return enclosuresDiv; - }, - - /** - * Gets a valid nsIFeedContainer object from the parsed nsIFeedResult. - * Displays error information if there was one. - * @returns A valid nsIFeedContainer object containing the contents of - * the feed. - */ - _getContainer() { - let feedService = - Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - - let result; - try { - result = - feedService.getFeedResult(this._getOriginalURI(this._window)); - } - catch (e) { - LOG("Subscribe Preview: feed not available?!"); - } - - if (result.bozo) { - LOG("Subscribe Preview: feed result is bozo?!"); - } - - let container; - try { - container = result.doc; - } - catch (e) { - LOG("Subscribe Preview: no result.doc? Why didn't the original reload?"); - return null; - } - return container; - }, - - /** - * Get moz-icon url for a file - * @param file - * A nsIFile object for which the moz-icon:// is returned - * @returns moz-icon url of the given file as a string - */ - _getFileIconURL(file) { - let ios = Cc["@mozilla.org/network/io-service;1"]. - getService(Ci.nsIIOService); - let fph = ios.getProtocolHandler("file") - .QueryInterface(Ci.nsIFileProtocolHandler); - let urlSpec = fph.getURLSpecFromFile(file); - return "moz-icon://" + urlSpec + "?size=16"; - }, - - /** - * Displays a prompt from which the user may choose a (client) feed reader. - * @param aCallback the callback method, passes in true if a feed reader was - * selected, false otherwise. - */ - _chooseClientApp(aCallback) { - this._subscribeCallback = aCallback; - this._mm.sendAsyncMessage("FeedWriter:ChooseClientApp", - { title: this._getString("chooseApplicationDialogTitle"), - feedType: this._getFeedType() }); - }, - - _setSubscribeUsingLabel() { - let stringLabel = "subscribeFeedUsing"; - switch (this._getFeedType()) { - case Ci.nsIFeed.TYPE_VIDEO: - stringLabel = "subscribeVideoPodcastUsing"; - break; - - case Ci.nsIFeed.TYPE_AUDIO: - stringLabel = "subscribeAudioPodcastUsing"; - break; - } - - let subscribeUsing = this._document.getElementById("subscribeUsingDescription"); - let textNode = this._document.createTextNode(this._getString(stringLabel)); - subscribeUsing.insertBefore(textNode, subscribeUsing.firstChild); - }, - - _setAlwaysUseLabel() { - let checkbox = this._document.getElementById("alwaysUse"); - if (checkbox && this._handlersList) { - let handlerName = this._handlersList.selectedOptions[0] - .textContent; - let stringLabel = "alwaysUseForFeeds"; - switch (this._getFeedType()) { - case Ci.nsIFeed.TYPE_VIDEO: - stringLabel = "alwaysUseForVideoPodcasts"; - break; - - case Ci.nsIFeed.TYPE_AUDIO: - stringLabel = "alwaysUseForAudioPodcasts"; - break; - } - - let label = this._getFormattedString(stringLabel, [handlerName]); - - let checkboxText = this._document.getElementById("checkboxText"); - if (checkboxText.lastChild.nodeType == checkboxText.TEXT_NODE) { - checkboxText.lastChild.textContent = label; - } else { - LOG("FeedWriter._setAlwaysUseLabel: Expected textNode as lastChild of alwaysUse label"); - let textNode = this._document.createTextNode(label); - checkboxText.appendChild(textNode); - } - } - }, - - // nsIDomEventListener - handleEvent(event) { - if (event.target.ownerDocument != this._document) { - LOG("FeedWriter.handleEvent: Someone passed the feed writer as a listener to the events of another document!"); - return; - } - - switch (event.type) { - case "click": - if (event.target.id == "subscribeButton") { - this.subscribe(); - } - break; - case "change": - LOG("Change fired"); - if (event.target.selectedOptions[0].id == "chooseApplicationMenuItem") { - this._chooseClientApp(() => { - // Select the (per-prefs) selected handler if no application - // was selected - LOG("Selected handler after callback"); - this._setAlwaysUseLabel(); - }); - } else { - this._setAlwaysUseLabel(); - } - break; - } - }, - - _getWebHandlerElementsForURL(aURL) { - return this._handlersList.querySelectorAll('[webhandlerurl="' + aURL + '"]'); - }, - - _setSelectedHandlerResponse(handler, url) { - LOG(`Selecting handler response ${handler} ${url}`); - switch (handler) { - case "web": { - if (this._handlersList) { - let handlers = - this._getWebHandlerElementsForURL(url); - if (handlers.length == 0) { - LOG(`Selected web handler isn't in the menulist ${url}`); - return; - } - - handlers[0].selected = true; - } - break; - } - case "client": - case "default": - // do nothing, these are handled by the onchange event - break; - case "bookmarks": - default: { - let liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem"); - if (liveBookmarksMenuItem) - liveBookmarksMenuItem.selected = true; - } - } - }, - - _initSubscriptionUI(setupMessage) { - if (!this._handlersList) - return; - LOG("UI init"); - - let feedType = this._getFeedType(); - - // change the background - let header = this._document.getElementById("feedHeader"); - switch (feedType) { - case Ci.nsIFeed.TYPE_VIDEO: - header.className = 'videoPodcastBackground'; - break; - - case Ci.nsIFeed.TYPE_AUDIO: - header.className = 'audioPodcastBackground'; - break; - - default: - header.className = 'feedBackground'; - } - - let liveBookmarksMenuItem = this._document.getElementById("liveBookmarksMenuItem"); - - // Last-selected application - let menuItem = liveBookmarksMenuItem.cloneNode(false); - menuItem.removeAttribute("selected"); - menuItem.setAttribute("id", "selectedAppMenuItem"); - menuItem.setAttribute("handlerType", "client"); - - // Hide the menuitem until we select an app - menuItem.style.display = "none"; - this._selectedAppMenuItem = menuItem; - - this._handlersList.appendChild(this._selectedAppMenuItem); - - // Create the menuitem for the default reader, but don't show/populate it until - // we get confirmation of what it is from the parent - menuItem = liveBookmarksMenuItem.cloneNode(false); - menuItem.removeAttribute("selected"); - menuItem.setAttribute("id", "defaultHandlerMenuItem"); - menuItem.setAttribute("handlerType", "client"); - menuItem.style.display = "none"; - - this._defaultHandlerMenuItem = menuItem; - this._handlersList.appendChild(this._defaultHandlerMenuItem); - - // "Choose Application..." menuitem - menuItem = liveBookmarksMenuItem.cloneNode(false); - menuItem.removeAttribute("selected"); - menuItem.setAttribute("id", "chooseApplicationMenuItem"); - menuItem.textContent = this._getString("chooseApplicationMenuItem"); - - this._handlersList.appendChild(menuItem); - - // separator - let chooseAppSep = liveBookmarksMenuItem.nextElementSibling.cloneNode(false); - chooseAppSep.textContent = liveBookmarksMenuItem.nextElementSibling.textContent; - this._handlersList.appendChild(chooseAppSep); - - for (let handler of setupMessage.handlers) { - if (!handler.uri) { - LOG("Handler with name " + handler.name + " has no URI!? Skipping..."); - continue; - } - menuItem = liveBookmarksMenuItem.cloneNode(false); - menuItem.removeAttribute("selected"); - menuItem.className = "menuitem-iconic"; - menuItem.textContent = handler.name; - menuItem.setAttribute("handlerType", "web"); - menuItem.setAttribute("webhandlerurl", handler.uri); - this._handlersList.appendChild(menuItem); - } - - this._setSelectedHandlerResponse(setupMessage.reader.handler, setupMessage.reader.url); - - if (setupMessage.defaultMenuItem) { - LOG(`Setting default menu item ${setupMessage.defaultMenuItem}`); - this._setApplicationLauncherMenuItem(this._defaultHandlerMenuItem, setupMessage.defaultMenuItem); - } - if (setupMessage.selectedMenuItem) { - LOG(`Setting selected menu item ${setupMessage.selectedMenuItem}`); - this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, setupMessage.selectedMenuItem); - } - - // "Subscribe using..." - this._setSubscribeUsingLabel(); - - // "Always use..." checkbox initial state - this._setCheckboxCheckedState(setupMessage.reader.alwaysUse); - this._setAlwaysUseLabel(); - - // We update the "Always use.." checkbox label whenever the selected item - // in the list is changed - this._handlersList.addEventListener("change", this); - - // Set up the "Subscribe Now" button - this._document.getElementById("subscribeButton") - .addEventListener("click", this); - - // first-run ui - if (setupMessage.showFirstRunUI) { - let textfeedinfo1, textfeedinfo2; - switch (feedType) { - case Ci.nsIFeed.TYPE_VIDEO: - textfeedinfo1 = "feedSubscriptionVideoPodcast1"; - textfeedinfo2 = "feedSubscriptionVideoPodcast2"; - break; - case Ci.nsIFeed.TYPE_AUDIO: - textfeedinfo1 = "feedSubscriptionAudioPodcast1"; - textfeedinfo2 = "feedSubscriptionAudioPodcast2"; - break; - default: - textfeedinfo1 = "feedSubscriptionFeed1"; - textfeedinfo2 = "feedSubscriptionFeed2"; - } - - let feedinfo1 = this._document.getElementById("feedSubscriptionInfo1"); - let feedinfo1Str = this._getString(textfeedinfo1); - let feedinfo2 = this._document.getElementById("feedSubscriptionInfo2"); - let feedinfo2Str = this._getString(textfeedinfo2); - - feedinfo1.textContent = feedinfo1Str; - feedinfo2.textContent = feedinfo2Str; - - header.setAttribute('firstrun', 'true'); - - this._mm.sendAsyncMessage("FeedWriter:ShownFirstRun"); - } - }, - - /** - * Returns the original URI object of the feed and ensures that this - * component is only ever invoked from the preview document. - * @param aWindow - * The window of the document invoking the BrowserFeedWriter - */ - _getOriginalURI(aWindow) { - let docShell = aWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell); - let chan = docShell.currentDocumentChannel; - - // We probably need to call InheritFromDocShellToDoc for this, but right now - // we can't call it from JS. - let attrs = docShell.getOriginAttributes(); - let ssm = Services.scriptSecurityManager; - let nullPrincipal = ssm.createNullPrincipal(attrs); - - // this channel is not going to be openend, use a nullPrincipal - // and the most restrctive securityFlag. - let resolvedURI = NetUtil.newChannel({ - uri: "about:feeds", - loadingPrincipal: nullPrincipal, - securityFlags: Ci.nsILoadInfo.SEC_REQUIRE_SAME_ORIGIN_DATA_IS_BLOCKED, - contentPolicyType: Ci.nsIContentPolicy.TYPE_OTHER - }).URI; - - if (resolvedURI.equals(chan.URI)) - return chan.originalURI; - - return null; - }, - - _window: null, - _document: null, - _feedURI: null, - _feedPrincipal: null, - _handlersList: null, - - // BrowserFeedWriter WebIDL methods - init(aWindow) { - let window = aWindow; - this._feedURI = this._getOriginalURI(window); - if (!this._feedURI) - return; - - this._window = window; - this._document = window.document; - this._handlersList = this._document.getElementById("handlersMenuList"); - - let secman = Cc["@mozilla.org/scriptsecuritymanager;1"]. - getService(Ci.nsIScriptSecurityManager); - this._feedPrincipal = secman.createCodebasePrincipal(this._feedURI, {}); - - LOG("Subscribe Preview: feed uri = " + this._window.location.href); - - - this._mm.addMessageListener("FeedWriter:PreferenceUpdated", this); - this._mm.addMessageListener("FeedWriter:SetApplicationLauncherMenuItem", this); - this._mm.addMessageListener("FeedWriter:GetSubscriptionUIResponse", this); - this._mm.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribeResponse", this); - - const feedType = this._getFeedType(); - this._mm.sendAsyncMessage("FeedWriter:GetSubscriptionUI", - { feedType }); - }, - - receiveMessage(msg) { - if (!this._window) { - // this._window is null unless this.init was called with a trusted - // window object. - return; - } - LOG(`received message from parent ${msg.name}`); - switch (msg.name) { - case "FeedWriter:PreferenceUpdated": - // This is called when browser-feeds.js spots a pref change - // This will happen when - // - about:preferences#applications changes - // - another feed reader page changes the preference - // - when this page itself changes the select and there isn't a redirect - // bookmarks and launching an external app means the page stays open after subscribe - const feedType = this._getFeedType(); - LOG(`Got prefChange! ${JSON.stringify(msg.data)} current type: ${feedType}`); - let feedTypePref = msg.data.default; - if (feedType in msg.data) { - feedTypePref = msg.data[feedType]; - } - LOG(`Got pref ${JSON.stringify(feedTypePref)}`); - this._setCheckboxCheckedState(feedTypePref.alwaysUse); - this._setSelectedHandlerResponse(feedTypePref.handler, feedTypePref.url); - this._setAlwaysUseLabel(); - break; - case "FeedWriter:SetFeedPrefsAndSubscribeResponse": - LOG(`FeedWriter:SetFeedPrefsAndSubscribeResponse - Redirecting ${msg.data.redirect}`); - this._window.location.href = msg.data.redirect; - break; - case "FeedWriter:GetSubscriptionUIResponse": - // Set up the subscription UI - this._initSubscriptionUI(msg.data); - break; - case "FeedWriter:SetApplicationLauncherMenuItem": - LOG(`FeedWriter:SetApplicationLauncherMenuItem - picked ${msg.data.name}`); - this._setApplicationLauncherMenuItem(this._selectedAppMenuItem, msg.data.name); - // Potentially a bit racy, but I don't think we can get into a state where this callback is set and - // we're not coming back from ChooseClientApp in browser-feeds.js - if (this._subscribeCallback) { - this._subscribeCallback(); - this._subscribeCallback = null; - } - break; - } - }, - - _setApplicationLauncherMenuItem(menuItem, aName) { - /* unselect all handlers */ - [...this._handlersList.children].forEach((option) => { - option.removeAttribute("selected"); - }); - menuItem.textContent = aName; - menuItem.style.display = ""; - menuItem.selected = true; - }, - - writeContent() { - if (!this._window) - return; - - try { - // Set up the feed content - let container = this._getContainer(); - if (!container) - return; - - this._setTitleText(container); - this._setTitleImage(container); - this._writeFeedContent(container); - } - finally { - this._removeFeedFromCache(); - } - }, - - close() { - this._document.getElementById("subscribeButton") - .removeEventListener("click", this, false); - this._handlersList - .removeEventListener("change", this, false); - this._document = null; - this._window = null; - this._handlersList = null; - - this._removeFeedFromCache(); - this.__bundle = null; - this._feedURI = null; - - this._selectedApp = undefined; - this._selectedAppMenuItem = null; - this._defaultHandlerMenuItem = null; - }, - - _removeFeedFromCache() { - if (this._feedURI) { - let feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - feedService.removeFeedResult(this._feedURI); - this._feedURI = null; - } - }, - - subscribe() { - let feedType = this._getFeedType(); - - // Subscribe to the feed using the selected handler and save prefs - let defaultHandler = "reader"; - let useAsDefault = this._document.getElementById("alwaysUse").getAttribute("checked"); - - let selectedItem = this._handlersList.selectedOptions[0]; - let subscribeCallback = () => { - let feedReader = null; - let settings = { - feedType, - useAsDefault, - // Pull the title and subtitle out of the document - feedTitle: this._document.getElementById(TITLE_ID).textContent, - feedSubtitle: this._document.getElementById(SUBTITLE_ID).textContent, - feedLocation: this._window.location.href - }; - if (selectedItem.hasAttribute("webhandlerurl")) { - feedReader = "web"; - settings.uri = selectedItem.getAttribute("webhandlerurl"); - } else { - switch (selectedItem.id) { - case "selectedAppMenuItem": - feedReader = "client"; - break; - case "defaultHandlerMenuItem": - feedReader = "default"; - break; - case "liveBookmarksMenuItem": - defaultHandler = "bookmarks"; - feedReader = "bookmarks"; - break; - } - } - settings.reader = feedReader; - - // If "Always use..." is checked, we should set PREF_*SELECTED_ACTION - // to either "reader" (If a web reader or if an application is selected), - // or to "bookmarks" (if the live bookmarks option is selected). - // Otherwise, we should set it to "ask" - if (!useAsDefault) { - defaultHandler = "ask"; - } - settings.action = defaultHandler; - LOG(`FeedWriter:SetFeedPrefsAndSubscribe - ${JSON.stringify(settings)}`); - this._mm.sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribe", - settings); - } - - // Show the file picker before subscribing if the - // choose application menuitem was chosen using the keyboard - if (selectedItem.id == "chooseApplicationMenuItem") { - this._chooseClientApp(function(aResult) { - if (aResult) { - selectedItem = - this._handlersList.selectedOptions[0]; - subscribeCallback(); - } - }.bind(this)); - } else { - subscribeCallback(); - } - }, - - get _mm() { - let mm = this._window.QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIDocShell). - QueryInterface(Ci.nsIInterfaceRequestor). - getInterface(Ci.nsIContentFrameMessageManager); - delete this._mm; - return this._mm = mm; - }, - - classID: FEEDWRITER_CID, - QueryInterface: XPCOMUtils.generateQI([Ci.nsIDOMEventListener, Ci.nsIObserver, - Ci.nsINavHistoryObserver, - Ci.nsIDOMGlobalPropertyInitializer]) -}; - -this.NSGetFactory = XPCOMUtils.generateNSGetFactory([FeedWriter]); diff --git a/browser/components/feeds/WebContentConverter.js b/browser/components/feeds/WebContentConverter.js deleted file mode 100644 index 2cb5cd145..000000000 --- a/browser/components/feeds/WebContentConverter.js +++ /dev/null @@ -1,1071 +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/Services.jsm"); -Components.utils.import("resource://gre/modules/XPCOMUtils.jsm"); -Components.utils.import("resource://gre/modules/PrivateBrowsingUtils.jsm"); - -const Cc = Components.classes; -const Ci = Components.interfaces; -const Cr = Components.results; - -function LOG(str) { - dump("*** " + str + "\n"); -} - -const WCCR_CONTRACTID = "@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"; -const WCCR_CLASSID = Components.ID("{792a7e82-06a0-437c-af63-b2d12e808acc}"); - -const WCC_CLASSID = Components.ID("{db7ebf28-cc40-415f-8a51-1b111851df1e}"); -const WCC_CLASSNAME = "Web Service Handler"; - -const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed"; -const TYPE_ANY = "*/*"; - -const PREF_CONTENTHANDLERS_AUTO = "browser.contentHandlers.auto."; -const PREF_CONTENTHANDLERS_BRANCH = "browser.contentHandlers.types."; -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_HANDLER_EXTERNAL_PREFIX = "network.protocol-handler.external"; -const PREF_ALLOW_DIFFERENT_HOST = "gecko.handlerService.allowRegisterFromDifferentHost"; - -const STRING_BUNDLE_URI = "chrome://browser/locale/feeds/subscribe.properties"; - -const NS_ERROR_MODULE_DOM = 2152923136; -const NS_ERROR_DOM_SYNTAX_ERR = NS_ERROR_MODULE_DOM + 12; - -function WebContentConverter() { -} -WebContentConverter.prototype = { - convert() { }, - asyncConvertData() { }, - onDataAvailable() { }, - onStopRequest() { }, - - onStartRequest(request, context) { - let wccr = - Cc[WCCR_CONTRACTID]. - getService(Ci.nsIWebContentConverterService); - wccr.loadPreferredHandler(request); - }, - - QueryInterface(iid) { - if (iid.equals(Ci.nsIStreamConverter) || - iid.equals(Ci.nsIStreamListener) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -let WebContentConverterFactory = { - createInstance(outer, iid) { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - return new WebContentConverter().QueryInterface(iid); - }, - - QueryInterface(iid) { - if (iid.equals(Ci.nsIFactory) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -function ServiceInfo(contentType, uri, name) { - this._contentType = contentType; - this._uri = uri; - this._name = name; -} -ServiceInfo.prototype = { - /** - * See nsIHandlerApp - */ - get name() { - return this._name; - }, - - /** - * See nsIHandlerApp - */ - equals(aHandlerApp) { - if (!aHandlerApp) - throw Cr.NS_ERROR_NULL_POINTER; - - if (aHandlerApp instanceof Ci.nsIWebContentHandlerInfo && - aHandlerApp.contentType == this.contentType && - aHandlerApp.uri == this.uri) - return true; - - return false; - }, - - /** - * See nsIWebContentHandlerInfo - */ - get contentType() { - return this._contentType; - }, - - /** - * See nsIWebContentHandlerInfo - */ - get uri() { - return this._uri; - }, - - /** - * See nsIWebContentHandlerInfo - */ - getHandlerURI(uri) { - return this._uri.replace(/%s/gi, encodeURIComponent(uri)); - }, - - QueryInterface(iid) { - if (iid.equals(Ci.nsIWebContentHandlerInfo) || - iid.equals(Ci.nsISupports)) - return this; - throw Cr.NS_ERROR_NO_INTERFACE; - } -}; - -const Utils = { - makeURI(aURL, aOriginCharset, aBaseURI) { - return Services.io.newURI(aURL, aOriginCharset, aBaseURI); - }, - - checkAndGetURI(aURIString, aContentWindow) { - let uri; - try { - let baseURI = aContentWindow.document.baseURIObject; - uri = this.makeURI(aURIString, null, baseURI); - } catch (ex) { - throw NS_ERROR_DOM_SYNTAX_ERR; - } - - // For security reasons we reject non-http(s) urls (see bug 354316), - // we may need to revise this once we support more content types - if (uri.scheme != "http" && uri.scheme != "https") { - throw this.getSecurityError( - "Permission denied to add " + uri.spec + " as a content or protocol handler", - aContentWindow); - } - - // We also reject handlers registered from a different host (see bug 402287) - // The pref allows us to test the feature - let pb = Services.prefs; - if (!pb.getBoolPref(PREF_ALLOW_DIFFERENT_HOST) && - (!["http:", "https:"].includes(aContentWindow.location.protocol) || - aContentWindow.location.hostname != uri.host)) { - throw this.getSecurityError( - "Permission denied to add " + uri.spec + " as a content or protocol handler", - aContentWindow); - } - - // If the uri doesn't contain '%s', it won't be a good handler - if (uri.spec.indexOf("%s") < 0) - throw NS_ERROR_DOM_SYNTAX_ERR; - - return uri; - }, - - // NB: Throws if aProtocol is not allowed. - checkProtocolHandlerAllowed(aProtocol, aURIString, aWindowOrNull) { - // First, check to make sure this isn't already handled internally (we don't - // want to let them take over, say "chrome"). - let handler = Services.io.getProtocolHandler(aProtocol); - if (!(handler instanceof Ci.nsIExternalProtocolHandler)) { - // This is handled internally, so we don't want them to register - throw this.getSecurityError( - `Permission denied to add ${aURIString} as a protocol handler`, - aWindowOrNull); - } - - // check if it is in the black list - let pb = Services.prefs; - let allowed; - try { - allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "." + aProtocol); - } - catch (e) { - allowed = pb.getBoolPref(PREF_HANDLER_EXTERNAL_PREFIX + "-default"); - } - if (!allowed) { - throw this.getSecurityError( - `Not allowed to register a protocol handler for ${aProtocol}`, - aWindowOrNull); - } - }, - - // Return a SecurityError exception from the given Window if one is given. If - // none is given, just return the given error string, for lack of anything - // better. - getSecurityError(errorString, aWindowOrNull) { - if (!aWindowOrNull) { - return errorString; - } - - return new aWindowOrNull.DOMException(errorString, "SecurityError"); - }, - - /** - * Mappings from known feed types to our internal content type. - */ - _mappings: { - "application/rss+xml": TYPE_MAYBE_FEED, - "application/atom+xml": TYPE_MAYBE_FEED, - }, - - resolveContentType(aContentType) { - if (aContentType in this._mappings) - return this._mappings[aContentType]; - return aContentType; - } -}; - -function WebContentConverterRegistrar() { - this._contentTypes = {}; - this._autoHandleContentTypes = {}; -} - -WebContentConverterRegistrar.prototype = { - get stringBundle() { - let sb = Services.strings.createBundle(STRING_BUNDLE_URI); - delete WebContentConverterRegistrar.prototype.stringBundle; - return WebContentConverterRegistrar.prototype.stringBundle = sb; - }, - - _getFormattedString(key, params) { - return this.stringBundle.formatStringFromName(key, params, params.length); - }, - - _getString(key) { - return this.stringBundle.GetStringFromName(key); - }, - - /** - * See nsIWebContentConverterService - */ - getAutoHandler(contentType) { - contentType = Utils.resolveContentType(contentType); - if (contentType in this._autoHandleContentTypes) - return this._autoHandleContentTypes[contentType]; - return null; - }, - - /** - * See nsIWebContentConverterService - */ - setAutoHandler(contentType, handler) { - if (handler && !this._typeIsRegistered(contentType, handler.uri)) - throw Cr.NS_ERROR_NOT_AVAILABLE; - - contentType = Utils.resolveContentType(contentType); - this._setAutoHandler(contentType, handler); - - let ps = Services.prefs; - let autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO); - if (handler) - autoBranch.setCharPref(contentType, handler.uri); - else if (autoBranch.prefHasUserValue(contentType)) - autoBranch.clearUserPref(contentType); - - ps.savePrefFile(null); - }, - - /** - * Update the internal data structure (not persistent) - */ - _setAutoHandler(contentType, handler) { - if (handler) - this._autoHandleContentTypes[contentType] = handler; - else if (contentType in this._autoHandleContentTypes) - delete this._autoHandleContentTypes[contentType]; - }, - - /** - * See nsIWebContentConverterService - */ - getWebContentHandlerByURI(contentType, uri) { - return this.getContentHandlers(contentType) - .find(e => e.uri == uri) || null; - }, - - /** - * See nsIWebContentConverterService - */ - loadPreferredHandler(request) { - let channel = request.QueryInterface(Ci.nsIChannel); - let contentType = Utils.resolveContentType(channel.contentType); - let handler = this.getAutoHandler(contentType); - if (handler) { - request.cancel(Cr.NS_ERROR_FAILURE); - - let webNavigation = - channel.notificationCallbacks.getInterface(Ci.nsIWebNavigation); - webNavigation.loadURI(handler.getHandlerURI(channel.URI.spec), - Ci.nsIWebNavigation.LOAD_FLAGS_NONE, - null, null, null); - } - }, - - /** - * See nsIWebContentConverterService - */ - removeProtocolHandler(aProtocol, aURITemplate) { - let eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Ci.nsIExternalProtocolService); - let handlerInfo = eps.getProtocolHandlerInfo(aProtocol); - let handlers = handlerInfo.possibleApplicationHandlers; - for (let i = 0; i < handlers.length; i++) { - try { // We only want to test web handlers - let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp); - if (handler.uriTemplate == aURITemplate) { - handlers.removeElementAt(i); - let hs = Cc["@mozilla.org/uriloader/handler-service;1"]. - getService(Ci.nsIHandlerService); - hs.store(handlerInfo); - return; - } - } catch (e) { /* it wasn't a web handler */ } - } - }, - - /** - * See nsIWebContentConverterService - */ - removeContentHandler(contentType, uri) { - function notURI(serviceInfo) { - return serviceInfo.uri != uri; - } - - if (contentType in this._contentTypes) { - this._contentTypes[contentType] = - this._contentTypes[contentType].filter(notURI); - } - }, - - /** - * These are types for which there is a separate content converter aside - * from our built in generic one. We should not automatically register - * a factory for creating a converter for these types. - */ - _blockedTypes: { - "application/vnd.mozilla.maybe.feed": true, - }, - - /** - * Determines if a web handler is already registered. - * - * @param aProtocol - * The scheme of the web handler we are checking for. - * @param aURITemplate - * The URI template that the handler uses to handle the protocol. - * @return true if it is already registered, false otherwise. - */ - _protocolHandlerRegistered(aProtocol, aURITemplate) { - let eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Ci.nsIExternalProtocolService); - let handlerInfo = eps.getProtocolHandlerInfo(aProtocol); - let handlers = handlerInfo.possibleApplicationHandlers; - for (let i = 0; i < handlers.length; i++) { - try { // We only want to test web handlers - let handler = handlers.queryElementAt(i, Ci.nsIWebHandlerApp); - if (handler.uriTemplate == aURITemplate) - return true; - } catch (e) { /* it wasn't a web handler */ } - } - return false; - }, - - /** - * See nsIWebContentHandlerRegistrar - */ - registerProtocolHandler(aProtocol, aURIString, aTitle, aBrowserOrWindow) { - LOG("registerProtocolHandler(" + aProtocol + "," + aURIString + "," + aTitle + ")"); - let haveWindow = (aBrowserOrWindow instanceof Ci.nsIDOMWindow); - let uri; - if (haveWindow) { - uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow); - } else { - // aURIString must not be a relative URI. - uri = Utils.makeURI(aURIString, null); - } - - // If the protocol handler is already registered, just return early. - if (this._protocolHandlerRegistered(aProtocol, uri.spec)) { - return; - } - - let browser; - if (haveWindow) { - let browserWindow = - this._getBrowserWindowForContentWindow(aBrowserOrWindow); - browser = this._getBrowserForContentWindow(browserWindow, - aBrowserOrWindow); - } else { - browser = aBrowserOrWindow; - } - if (PrivateBrowsingUtils.isBrowserPrivate(browser)) { - // Inside the private browsing mode, we don't want to alert the user to save - // a protocol handler. We log it to the error console so that web developers - // would have some way to tell what's going wrong. - Services.console. - logStringMessage("Web page denied access to register a protocol handler inside private browsing mode"); - return; - } - - Utils.checkProtocolHandlerAllowed(aProtocol, aURIString, - haveWindow ? aBrowserOrWindow : null); - - // Now Ask the user and provide the proper callback - let message = this._getFormattedString("addProtocolHandler", - [aTitle, uri.host, aProtocol]); - - let notificationIcon = uri.prePath + "/favicon.ico"; - let notificationValue = "Protocol Registration: " + aProtocol; - let addButton = { - label: this._getString("addProtocolHandlerAddButton"), - accessKey: this._getString("addProtocolHandlerAddButtonAccesskey"), - protocolInfo: { protocol: aProtocol, uri: uri.spec, name: aTitle }, - - callback(aNotification, aButtonInfo) { - let protocol = aButtonInfo.protocolInfo.protocol; - let uri = aButtonInfo.protocolInfo.uri; - let name = aButtonInfo.protocolInfo.name; - - let handler = Cc["@mozilla.org/uriloader/web-handler-app;1"]. - createInstance(Ci.nsIWebHandlerApp); - handler.name = name; - handler.uriTemplate = uri; - - let eps = Cc["@mozilla.org/uriloader/external-protocol-service;1"]. - getService(Ci.nsIExternalProtocolService); - let handlerInfo = eps.getProtocolHandlerInfo(protocol); - handlerInfo.possibleApplicationHandlers.appendElement(handler, false); - - // Since the user has agreed to add a new handler, chances are good - // that the next time they see a handler of this type, they're going - // to want to use it. Reset the handlerInfo to ask before the next - // use. - handlerInfo.alwaysAskBeforeHandling = true; - - let hs = Cc["@mozilla.org/uriloader/handler-service;1"]. - getService(Ci.nsIHandlerService); - hs.store(handlerInfo); - } - }; - let notificationBox = browser.getTabBrowser().getNotificationBox(browser); - notificationBox.appendNotification(message, - notificationValue, - notificationIcon, - notificationBox.PRIORITY_INFO_LOW, - [addButton]); - }, - - /** - * See nsIWebContentHandlerRegistrar - * If a DOM window is provided, then the request came from content, so we - * prompt the user to confirm the registration. - */ - registerContentHandler(aContentType, aURIString, aTitle, aWindowOrBrowser) { - LOG("registerContentHandler(" + aContentType + "," + aURIString + "," + aTitle + ")"); - - // Make sure to do our URL checks up front, before our content type check, - // just like the WebContentConverterRegistrarContent does. - let haveWindow = aWindowOrBrowser && - (aWindowOrBrowser instanceof Ci.nsIDOMWindow); - let uri; - if (haveWindow) { - uri = Utils.checkAndGetURI(aURIString, aWindowOrBrowser); - } else if (aWindowOrBrowser) { - // uri was vetted in the content process. - uri = Utils.makeURI(aURIString, null); - } - - // We only support feed types at present. - let contentType = Utils.resolveContentType(aContentType); - // XXX We should be throwing a Utils.getSecurityError() here in at least - // some cases. See bug 1266492. - if (contentType != TYPE_MAYBE_FEED) { - return; - } - - if (aWindowOrBrowser) { - let notificationBox; - if (haveWindow) { - let browserWindow = this._getBrowserWindowForContentWindow(aWindowOrBrowser); - let browserElement = this._getBrowserForContentWindow(browserWindow, aWindowOrBrowser); - notificationBox = browserElement.getTabBrowser().getNotificationBox(browserElement); - } else { - notificationBox = aWindowOrBrowser.getTabBrowser() - .getNotificationBox(aWindowOrBrowser); - } - - this._appendFeedReaderNotification(uri, aTitle, notificationBox); - } - else { - this._registerContentHandler(contentType, aURIString, aTitle); - } - }, - - /** - * Returns the browser chrome window in which the content window is in - */ - _getBrowserWindowForContentWindow(aContentWindow) { - return aContentWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShellTreeItem) - .rootTreeItem - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIDOMWindow) - .wrappedJSObject; - }, - - /** - * Returns the <xul:browser> element associated with the given content - * window. - * - * @param aBrowserWindow - * The browser window in which the content window is in. - * @param aContentWindow - * The content window. It's possible to pass a child content window - * (i.e. the content window of a frame/iframe). - */ - _getBrowserForContentWindow(aBrowserWindow, aContentWindow) { - // This depends on pseudo APIs of browser.js and tabbrowser.xml - aContentWindow = aContentWindow.top; - return aBrowserWindow.gBrowser.browsers.find((browser) => - browser.contentWindow == aContentWindow); - }, - - /** - * Appends a notifcation for the given feed reader details. - * - * The notification could be either a pseudo-dialog which lets - * the user to add the feed reader: - * [ [icon] Add %feed-reader-name% (%feed-reader-host%) as a Feed Reader? (Add) [x] ] - * - * or a simple message for the case where the feed reader is already registered: - * [ [icon] %feed-reader-name% is already registered as a Feed Reader [x] ] - * - * A new notification isn't appended if the given notificationbox has a - * notification for the same feed reader. - * - * @param aURI - * The url of the feed reader as a nsIURI object - * @param aName - * The feed reader name as it was passed to registerContentHandler - * @param aNotificationBox - * The notification box to which a notification might be appended - * @return true if a notification has been appended, false otherwise. - */ - _appendFeedReaderNotification(aURI, aName, aNotificationBox) { - let uriSpec = aURI.spec; - let notificationValue = "feed reader notification: " + uriSpec; - let notificationIcon = aURI.prePath + "/favicon.ico"; - - // Don't append a new notification if the notificationbox - // has a notification for the given feed reader already - if (aNotificationBox.getNotificationWithValue(notificationValue)) - return false; - - let buttons; - let message; - if (this.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uriSpec)) - message = this._getFormattedString("handlerRegistered", [aName]); - else { - message = this._getFormattedString("addHandler", [aName, aURI.host]); - let self = this; - let addButton = { - _outer: self, - label: self._getString("addHandlerAddButton"), - accessKey: self._getString("addHandlerAddButtonAccesskey"), - feedReaderInfo: { uri: uriSpec, name: aName }, - - /* static */ - callback(aNotification, aButtonInfo) { - let uri = aButtonInfo.feedReaderInfo.uri; - let name = aButtonInfo.feedReaderInfo.name; - let outer = aButtonInfo._outer; - - // The reader could have been added from another window mean while - if (!outer.getWebContentHandlerByURI(TYPE_MAYBE_FEED, uri)) - outer._registerContentHandler(TYPE_MAYBE_FEED, uri, name); - - // avoid reference cycles - aButtonInfo._outer = null; - - return false; - } - }; - buttons = [addButton]; - } - - aNotificationBox.appendNotification(message, - notificationValue, - notificationIcon, - aNotificationBox.PRIORITY_INFO_LOW, - buttons); - return true; - }, - - /** - * Save Web Content Handler metadata to persistent preferences. - * @param contentType - * The content Type being handled - * @param uri - * The uri of the web service - * @param title - * The human readable name of the web service - * - * This data is stored under: - * - * browser.contentHandlers.type0 = content/type - * browser.contentHandlers.uri0 = http://www.foo.com/q=%s - * browser.contentHandlers.title0 = Foo 2.0alphr - */ - _saveContentHandlerToPrefs(contentType, uri, title) { - let ps = Services.prefs; - let i = 0; - let typeBranch = null; - while (true) { - typeBranch = - ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + i + "."); - try { - typeBranch.getCharPref("type"); - ++i; - } - catch (e) { - // No more handlers - break; - } - } - if (typeBranch) { - typeBranch.setCharPref("type", contentType); - let pls = - Cc["@mozilla.org/pref-localizedstring;1"]. - createInstance(Ci.nsIPrefLocalizedString); - pls.data = uri; - typeBranch.setComplexValue("uri", Ci.nsIPrefLocalizedString, pls); - pls.data = title; - typeBranch.setComplexValue("title", Ci.nsIPrefLocalizedString, pls); - - ps.savePrefFile(null); - } - }, - - /** - * Determines if there is a type with a particular uri registered for the - * specified content type already. - * @param contentType - * The content type that the uri handles - * @param uri - * The uri of the content type - */ - _typeIsRegistered(contentType, uri) { - if (!(contentType in this._contentTypes)) - return false; - - return this._contentTypes[contentType] - .some(t => t.uri == uri); - }, - - /** - * Gets a stream converter contract id for the specified content type. - * @param contentType - * The source content type for the conversion. - * @returns A contract id to construct a converter to convert between the - * contentType and *\/*. - */ - _getConverterContractID(contentType) { - const template = "@mozilla.org/streamconv;1?from=%s&to=*/*"; - return template.replace(/%s/, contentType); - }, - - /** - * Register a web service handler for a content type. - * - * @param contentType - * the content type being handled - * @param uri - * the URI of the web service - * @param title - * the human readable name of the web service - */ - _registerContentHandler(contentType, uri, title) { - this._updateContentTypeHandlerMap(contentType, uri, title); - this._saveContentHandlerToPrefs(contentType, uri, title); - - if (contentType == TYPE_MAYBE_FEED) { - // Make the new handler the last-selected reader in the preview page - // and make sure the preview page is shown the next time a feed is visited - let pb = Services.prefs.getBranch(null); - pb.setCharPref(PREF_SELECTED_READER, "web"); - - let supportsString = - Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - supportsString.data = uri; - pb.setComplexValue(PREF_SELECTED_WEB, Ci.nsISupportsString, - supportsString); - pb.setCharPref(PREF_SELECTED_ACTION, "ask"); - this._setAutoHandler(TYPE_MAYBE_FEED, null); - } - }, - - /** - * Update the content type -> handler map. This mapping is not persisted, use - * registerContentHandler or _saveContentHandlerToPrefs for that purpose. - * @param contentType - * The content Type being handled - * @param uri - * The uri of the web service - * @param title - * The human readable name of the web service - */ - _updateContentTypeHandlerMap(contentType, uri, title) { - if (!(contentType in this._contentTypes)) - this._contentTypes[contentType] = []; - - // Avoid adding duplicates - if (this._typeIsRegistered(contentType, uri)) - return; - - this._contentTypes[contentType].push(new ServiceInfo(contentType, uri, title)); - - if (!(contentType in this._blockedTypes)) { - let converterContractID = this._getConverterContractID(contentType); - let cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - cr.registerFactory(WCC_CLASSID, WCC_CLASSNAME, converterContractID, - WebContentConverterFactory); - } - }, - - /** - * See nsIWebContentConverterService - */ - getContentHandlers(contentType, countRef) { - if (countRef) { - countRef.value = 0; - } - if (!(contentType in this._contentTypes)) - return []; - - let handlers = this._contentTypes[contentType]; - if (countRef) { - countRef.value = handlers.length; - } - return handlers; - }, - - /** - * See nsIWebContentConverterService - */ - resetHandlersForType(contentType) { - // currently unused within the tree, so only useful for extensions; previous - // impl. was buggy (and even infinite-looped!), so I argue that this is a - // definite improvement - throw Cr.NS_ERROR_NOT_IMPLEMENTED; - }, - - /** - * Registers a handler from the settings on a preferences branch. - * - * Since we support up to six predefined readers, we need to handle gaps - * better, since the first branch with user-added values will be .6 - * - * How we deal with that is to check to see if there's no prefs in the - * branch and stop cycling once that's true. This doesn't fix the case - * where a user manually removes a reader, but that's not supported yet! - * - * @param branch - * an nsIPrefBranch containing "type", "uri", and "title" preferences - * corresponding to the content handler to be registered - */ - _registerContentHandlerHavingBranch(branch) { - let vals = branch.getChildList(""); - if (vals.length == 0) - return; - - let type = branch.getCharPref("type"); - let uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data; - let title = branch.getComplexValue("title", - Ci.nsIPrefLocalizedString).data; - this._updateContentTypeHandlerMap(type, uri, title); - }, - - /** - * Load the auto handler, content handler and protocol tables from - * preferences. - */ - _init() { - let ps = Services.prefs; - - let children = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH) - .getChildList(""); - - // first get the numbers of the providers by getting all ###.uri prefs - let nums = children.map((child) => { - let match = /^(\d+)\.uri$/.exec(child); - return match ? match[1] : ""; - }).filter(child => !!child) - .sort(); - - - // now register them - for (let num of nums) { - let branch = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + num + "."); - try { - this._registerContentHandlerHavingBranch(branch); - } catch (ex) { - // do nothing, the next branch might have values - } - } - - // We need to do this _after_ registering all of the available handlers, - // so that getWebContentHandlerByURI can return successfully. - let autoBranch; - try { - autoBranch = ps.getBranch(PREF_CONTENTHANDLERS_AUTO); - } catch (e) { - // No auto branch yet, that's fine - // LOG("WCCR.init: There is no auto branch, benign"); - } - - if (autoBranch) { - for (let type of autoBranch.getChildList("")) { - let uri = autoBranch.getCharPref(type); - if (uri) { - let handler = this.getWebContentHandlerByURI(type, uri); - if (handler) { - this._setAutoHandler(type, handler); - } - } - } - } - }, - - /** - * See nsIObserver - */ - observe(subject, topic, data) { - let os = Services.obs; - switch (topic) { - case "app-startup": - os.addObserver(this, "browser-ui-startup-complete", false); - break; - case "browser-ui-startup-complete": - os.removeObserver(this, "browser-ui-startup-complete"); - this._init(); - break; - } - }, - - /** - * See nsIFactory - */ - createInstance(outer, iid) { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.QueryInterface(iid); - }, - - classID: WCCR_CLASSID, - - /** - * See nsISupports - */ - QueryInterface: XPCOMUtils.generateQI( - [Ci.nsIWebContentConverterService, - Ci.nsIWebContentHandlerRegistrar, - Ci.nsIObserver, - Ci.nsIFactory]), - - _xpcom_categories: [{ - category: "app-startup", - service: true - }] -}; - -function WebContentConverterRegistrarContent() { - this._contentTypes = {}; -} - -WebContentConverterRegistrarContent.prototype = { - - /** - * Load the auto handler, content handler and protocol tables from - * preferences. - */ - _init() { - let ps = Services.prefs; - - let children = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH) - .getChildList(""); - - // first get the numbers of the providers by getting all ###.uri prefs - let nums = children.map((child) => { - let match = /^(\d+)\.uri$/.exec(child); - return match ? match[1] : ""; - }).filter(child => !!child) - .sort(); - - // now register them - for (num of nums) { - let branch = ps.getBranch(PREF_CONTENTHANDLERS_BRANCH + num + "."); - try { - this._registerContentHandlerHavingBranch(branch); - } catch (ex) { - // do nothing, the next branch might have values - } - } - }, - - _typeIsRegistered(contentType, uri) { - return this._contentTypes[contentType] - .some(e => e.uri == uri); - }, - - /** - * Since we support up to six predefined readers, we need to handle gaps - * better, since the first branch with user-added values will be .6 - * - * How we deal with that is to check to see if there's no prefs in the - * branch and stop cycling once that's true. This doesn't fix the case - * where a user manually removes a reader, but that's not supported yet! - * - * @param branch - * The pref branch to register the content handler under - * - */ - _registerContentHandlerHavingBranch(branch) { - let vals = branch.getChildList(""); - if (vals.length == 0) - return; - - let type = branch.getCharPref("type"); - let uri = branch.getComplexValue("uri", Ci.nsIPrefLocalizedString).data; - let title = branch.getComplexValue("title", - Ci.nsIPrefLocalizedString).data; - this._updateContentTypeHandlerMap(type, uri, title); - }, - - _updateContentTypeHandlerMap(contentType, uri, title) { - if (!(contentType in this._contentTypes)) - this._contentTypes[contentType] = []; - - // Avoid adding duplicates - if (this._typeIsRegistered(contentType, uri)) - return; - - this._contentTypes[contentType].push(new ServiceInfo(contentType, uri, title)); - - if (!(contentType in this._blockedTypes)) { - let converterContractID = this._getConverterContractID(contentType); - let cr = Components.manager.QueryInterface(Ci.nsIComponentRegistrar); - cr.registerFactory(WCC_CLASSID, WCC_CLASSNAME, converterContractID, - WebContentConverterFactory); - } - }, - - /** - * See nsIWebContentConverterService - */ - getContentHandlers(contentType, countRef) { - this._init(); - if (countRef) { - countRef.value = 0; - } - - if (!(contentType in this._contentTypes)) - return []; - - let handlers = this._contentTypes[contentType]; - if (countRef) { - countRef.value = handlers.length; - } - return handlers; - }, - - setAutoHandler(contentType, handler) { - Services.cpmm.sendAsyncMessage("WCCR:setAutoHandler", - { contentType, handler }); - }, - - getWebContentHandlerByURI(contentType, uri) { - return this.getContentHandlers(contentType) - .find(e => e.uri == uri) || null; - }, - - /** - * See nsIWebContentHandlerRegistrar - */ - registerContentHandler(aContentType, aURIString, aTitle, aBrowserOrWindow) { - // aBrowserOrWindow must be a window. - let messageManager = aBrowserOrWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsITabChild) - .messageManager; - - let uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow); - // XXX We should be throwing a Utils.getSecurityError() here in at least - // some cases. See bug 1266492. - if (Utils.resolveContentType(aContentType) != TYPE_MAYBE_FEED) { - return; - } - - messageManager.sendAsyncMessage("WCCR:registerContentHandler", - { contentType: aContentType, - uri: uri.spec, - title: aTitle }); - }, - - registerProtocolHandler(aProtocol, aURIString, aTitle, aBrowserOrWindow) { - // aBrowserOrWindow must be a window. - let messageManager = aBrowserOrWindow.QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsIWebNavigation) - .QueryInterface(Ci.nsIDocShell) - .QueryInterface(Ci.nsIInterfaceRequestor) - .getInterface(Ci.nsITabChild) - .messageManager; - - let uri = Utils.checkAndGetURI(aURIString, aBrowserOrWindow); - Utils.checkProtocolHandlerAllowed(aProtocol, aURIString, aBrowserOrWindow); - - messageManager.sendAsyncMessage("WCCR:registerProtocolHandler", - { protocol: aProtocol, - uri: uri.spec, - title: aTitle }); - }, - - /** - * See nsIFactory - */ - createInstance(outer, iid) { - if (outer != null) - throw Cr.NS_ERROR_NO_AGGREGATION; - return this.QueryInterface(iid); - }, - - classID: WCCR_CLASSID, - - /** - * See nsISupports - */ - QueryInterface: XPCOMUtils.generateQI( - [Ci.nsIWebContentHandlerRegistrar, - Ci.nsIWebContentConverterService, - Ci.nsIFactory]) -}; - -this.NSGetFactory = - (Services.appinfo.processType === Services.appinfo.PROCESS_TYPE_CONTENT) ? - XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrarContent]) : - XPCOMUtils.generateNSGetFactory([WebContentConverterRegistrar]); diff --git a/browser/components/feeds/content/subscribe.js b/browser/components/feeds/content/subscribe.js deleted file mode 100644 index 05a564bf1..000000000 --- a/browser/components/feeds/content/subscribe.js +++ /dev/null @@ -1,25 +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/. */ - -/* global BrowserFeedWriter */ - -var SubscribeHandler = { - /** - * The nsIFeedWriter object that produces the UI - */ - _feedWriter: null, - - init: function SH_init() { - this._feedWriter = new BrowserFeedWriter(); - }, - - writeContent: function SH_writeContent() { - this._feedWriter.writeContent(); - }, - - uninit: function SH_uninit() { - this._feedWriter.close(); - } -}; diff --git a/browser/components/feeds/content/subscribe.xhtml b/browser/components/feeds/content/subscribe.xhtml deleted file mode 100644 index a55c80053..000000000 --- a/browser/components/feeds/content/subscribe.xhtml +++ /dev/null @@ -1,74 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- 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/. --> - - -<!DOCTYPE html [ - <!ENTITY % htmlDTD - PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" - "DTD/xhtml1-strict.dtd"> - %htmlDTD; - <!ENTITY % globalDTD - SYSTEM "chrome://global/locale/global.dtd"> - %globalDTD; - <!ENTITY % feedDTD - SYSTEM "chrome://browser/locale/feeds/subscribe.dtd"> - %feedDTD; -]> - -<?xml-stylesheet href="chrome://global/skin/" type="text/css"?> - -<html id="feedHandler" - xmlns="http://www.w3.org/1999/xhtml"> - <head> - <title>&feedPage.title;</title> - <link rel="stylesheet" - href="chrome://browser/skin/feeds/subscribe.css" - type="text/css" - media="all"/> - <script type="application/javascript" - src="chrome://browser/content/feeds/subscribe.js"/> - </head> - <body onload="SubscribeHandler.writeContent();" onunload="SubscribeHandler.uninit();"> - <div id="feedHeaderContainer"> - <div id="feedHeader" dir="&locale.dir;"> - <div id="feedIntroText"> - <p id="feedSubscriptionInfo1" /> - <p id="feedSubscriptionInfo2" /> - </div> - <div id="feedSubscribeLine"> - <label id="subscribeUsingDescription"> - <select id="handlersMenuList"> - <option id="liveBookmarksMenuItem" selected="true">&feedLiveBookmarks;</option> - <option disabled="true">━━━━━━━</option> - </select> - </label> - <label id="checkboxText"> - <input type="checkbox" id="alwaysUse" class="alwaysUse" checked="false"/> - </label> - <button id="subscribeButton">&feedSubscribeNow;</button> - </div> - </div> - <div id="feedHeaderContainerSpacer"/> - </div> - - <script type="application/javascript"> - /* import-globals-from subscribe.js */ - SubscribeHandler.init(); - </script> - - <div id="feedBody"> - <div id="feedTitle"> - <a id="feedTitleLink"> - <img id="feedTitleImage"/> - </a> - <div id="feedTitleContainer"> - <h1 id="feedTitleText"/> - <h2 id="feedSubtitleText"/> - </div> - </div> - <div id="feedContent"/> - </div> - </body> -</html> diff --git a/browser/components/feeds/jar.mn b/browser/components/feeds/jar.mn deleted file mode 100644 index 8570112c1..000000000 --- a/browser/components/feeds/jar.mn +++ /dev/null @@ -1,7 +0,0 @@ -# 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/. - -browser.jar: - content/browser/feeds/subscribe.xhtml (content/subscribe.xhtml) - content/browser/feeds/subscribe.js (content/subscribe.js) diff --git a/browser/components/feeds/moz.build b/browser/components/feeds/moz.build deleted file mode 100644 index b8755fcc9..000000000 --- a/browser/components/feeds/moz.build +++ /dev/null @@ -1,34 +0,0 @@ -# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- -# vim: set filetype=python: -# 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/. - -JAR_MANIFESTS += ['jar.mn'] - -XPIDL_SOURCES += [ - 'nsIFeedResultService.idl', - 'nsIWebContentConverterRegistrar.idl', -] - -XPIDL_MODULE = 'browser-feeds' - -SOURCES += [ - 'nsFeedSniffer.cpp', -] - -EXTRA_COMPONENTS += [ - 'BrowserFeeds.manifest', - 'FeedConverter.js', - 'FeedWriter.js', - 'WebContentConverter.js', -] - -FINAL_LIBRARY = 'browsercomps' - -for var in ('MOZ_APP_NAME', 'MOZ_MACBUNDLE_NAME'): - DEFINES[var] = CONFIG[var] - -LOCAL_INCLUDES += [ - '../build', -] diff --git a/browser/components/feeds/nsFeedSniffer.cpp b/browser/components/feeds/nsFeedSniffer.cpp deleted file mode 100644 index f314d3d3b..000000000 --- a/browser/components/feeds/nsFeedSniffer.cpp +++ /dev/null @@ -1,363 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#include "nsFeedSniffer.h" - - -#include "nsNetCID.h" -#include "nsXPCOM.h" -#include "nsCOMPtr.h" -#include "nsStringStream.h" - -#include "nsBrowserCompsCID.h" - -#include "nsICategoryManager.h" -#include "nsIServiceManager.h" -#include "nsComponentManagerUtils.h" -#include "nsServiceManagerUtils.h" - -#include "nsIStreamConverterService.h" -#include "nsIStreamConverter.h" - -#include "nsIStreamListener.h" - -#include "nsIHttpChannel.h" -#include "nsIMIMEHeaderParam.h" - -#include "nsMimeTypes.h" -#include "nsIURI.h" -#include <algorithm> - -#define TYPE_ATOM "application/atom+xml" -#define TYPE_RSS "application/rss+xml" -#define TYPE_MAYBE_FEED "application/vnd.mozilla.maybe.feed" - -#define NS_RDF "http://www.w3.org/1999/02/22-rdf-syntax-ns#" -#define NS_RSS "http://purl.org/rss/1.0/" - -#define MAX_BYTES 512u - -NS_IMPL_ISUPPORTS(nsFeedSniffer, - nsIContentSniffer, - nsIStreamListener, - nsIRequestObserver) - -nsresult -nsFeedSniffer::ConvertEncodedData(nsIRequest* request, - const uint8_t* data, - uint32_t length) -{ - nsresult rv = NS_OK; - - mDecodedData = ""; - nsCOMPtr<nsIHttpChannel> httpChannel(do_QueryInterface(request)); - if (!httpChannel) - return NS_ERROR_NO_INTERFACE; - - nsAutoCString contentEncoding; - httpChannel->GetResponseHeader(NS_LITERAL_CSTRING("Content-Encoding"), - contentEncoding); - if (!contentEncoding.IsEmpty()) { - nsCOMPtr<nsIStreamConverterService> converterService(do_GetService(NS_STREAMCONVERTERSERVICE_CONTRACTID)); - if (converterService) { - ToLowerCase(contentEncoding); - - nsCOMPtr<nsIStreamListener> converter; - rv = converterService->AsyncConvertData(contentEncoding.get(), - "uncompressed", this, nullptr, - getter_AddRefs(converter)); - NS_ENSURE_SUCCESS(rv, rv); - - converter->OnStartRequest(request, nullptr); - - nsCOMPtr<nsIStringInputStream> rawStream = - do_CreateInstance(NS_STRINGINPUTSTREAM_CONTRACTID); - if (!rawStream) - return NS_ERROR_FAILURE; - - rv = rawStream->SetData((const char*)data, length); - NS_ENSURE_SUCCESS(rv, rv); - - rv = converter->OnDataAvailable(request, nullptr, rawStream, 0, length); - NS_ENSURE_SUCCESS(rv, rv); - - converter->OnStopRequest(request, nullptr, NS_OK); - } - } - return rv; -} - -template<int N> -static bool -StringBeginsWithLowercaseLiteral(nsAString& aString, - const char (&aSubstring)[N]) -{ - return StringHead(aString, N).LowerCaseEqualsLiteral(aSubstring); -} - -bool -HasAttachmentDisposition(nsIHttpChannel* httpChannel) -{ - if (!httpChannel) - return false; - - uint32_t disp; - nsresult rv = httpChannel->GetContentDisposition(&disp); - - if (NS_SUCCEEDED(rv) && disp == nsIChannel::DISPOSITION_ATTACHMENT) - return true; - - return false; -} - -/** - * @return the first occurrence of a character within a string buffer, - * or nullptr if not found - */ -static const char* -FindChar(char c, const char *begin, const char *end) -{ - for (; begin < end; ++begin) { - if (*begin == c) - return begin; - } - return nullptr; -} - -/** - * - * Determine if a substring is the "documentElement" in the document. - * - * All of our sniffed substrings: <rss, <feed, <rdf:RDF must be the "document" - * element within the XML DOM, i.e. the root container element. Otherwise, - * it's possible that someone embedded one of these tags inside a document of - * another type, e.g. a HTML document, and we don't want to show the preview - * page if the document isn't actually a feed. - * - * @param start - * The beginning of the data being sniffed - * @param end - * The end of the data being sniffed, right before the substring that - * was found. - * @returns true if the found substring is the documentElement, false - * otherwise. - */ -static bool -IsDocumentElement(const char *start, const char* end) -{ - // For every tag in the buffer, check to see if it's a PI, Doctype or - // comment, our desired substring or something invalid. - while ( (start = FindChar('<', start, end)) ) { - ++start; - if (start >= end) - return false; - - // Check to see if the character following the '<' is either '?' or '!' - // (processing instruction or doctype or comment)... these are valid nodes - // to have in the prologue. - if (*start != '?' && *start != '!') - return false; - - // Now advance the iterator until the '>' (We do this because we don't want - // to sniff indicator substrings that are embedded within other nodes, e.g. - // comments: <!-- <rdf:RDF .. > --> - start = FindChar('>', start, end); - if (!start) - return false; - - ++start; - } - return true; -} - -/** - * Determines whether or not a string exists as the root element in an XML data - * string buffer. - * @param dataString - * The data being sniffed - * @param substring - * The substring being tested for existence and root-ness. - * @returns true if the substring exists and is the documentElement, false - * otherwise. - */ -static bool -ContainsTopLevelSubstring(nsACString& dataString, const char *substring) -{ - int32_t offset = dataString.Find(substring); - if (offset == -1) - return false; - - const char *begin = dataString.BeginReading(); - - // Only do the validation when we find the substring. - return IsDocumentElement(begin, begin + offset); -} - -NS_IMETHODIMP -nsFeedSniffer::GetMIMETypeFromContent(nsIRequest* request, - const uint8_t* data, - uint32_t length, - nsACString& sniffedType) -{ - nsCOMPtr<nsIHttpChannel> channel(do_QueryInterface(request)); - if (!channel) - return NS_ERROR_NO_INTERFACE; - - // Check that this is a GET request, since you can't subscribe to a POST... - nsAutoCString method; - channel->GetRequestMethod(method); - if (!method.EqualsLiteral("GET")) { - sniffedType.Truncate(); - return NS_OK; - } - - // We need to find out if this is a load of a view-source document. In this - // case we do not want to override the content type, since the source display - // does not need to be converted from feed format to XUL. More importantly, - // we don't want to change the content type from something - // nsContentDLF::CreateInstance knows about (e.g. application/xml, text/html - // etc) to something that only the application fe knows about (maybe.feed) - // thus deactivating syntax highlighting. - nsCOMPtr<nsIURI> originalURI; - channel->GetOriginalURI(getter_AddRefs(originalURI)); - - nsAutoCString scheme; - originalURI->GetScheme(scheme); - if (scheme.EqualsLiteral("view-source")) { - sniffedType.Truncate(); - return NS_OK; - } - - // Check the Content-Type to see if it is set correctly. If it is set to - // something specific that we think is a reliable indication of a feed, don't - // bother sniffing since we assume the site maintainer knows what they're - // doing. - nsAutoCString contentType; - channel->GetContentType(contentType); - bool noSniff = contentType.EqualsLiteral(TYPE_RSS) || - contentType.EqualsLiteral(TYPE_ATOM); - - // Check to see if this was a feed request from the location bar or from - // the feed: protocol. This is also a reliable indication. - // The value of the header doesn't matter. - if (!noSniff) { - nsAutoCString sniffHeader; - nsresult foundHeader = - channel->GetRequestHeader(NS_LITERAL_CSTRING("X-Moz-Is-Feed"), - sniffHeader); - noSniff = NS_SUCCEEDED(foundHeader); - } - - if (noSniff) { - // check for an attachment after we have a likely feed. - if(HasAttachmentDisposition(channel)) { - sniffedType.Truncate(); - return NS_OK; - } - - // set the feed header as a response header, since we have good metadata - // telling us that the feed is supposed to be RSS or Atom - channel->SetResponseHeader(NS_LITERAL_CSTRING("X-Moz-Is-Feed"), - NS_LITERAL_CSTRING("1"), false); - sniffedType.AssignLiteral(TYPE_MAYBE_FEED); - return NS_OK; - } - - // Don't sniff arbitrary types. Limit sniffing to situations that - // we think can reasonably arise. - if (!contentType.EqualsLiteral(TEXT_HTML) && - !contentType.EqualsLiteral(APPLICATION_OCTET_STREAM) && - // Same criterion as XMLHttpRequest. Should we be checking for "+xml" - // and check for text/xml and application/xml by hand instead? - contentType.Find("xml") == -1) { - sniffedType.Truncate(); - return NS_OK; - } - - // Now we need to potentially decompress data served with - // Content-Encoding: gzip - nsresult rv = ConvertEncodedData(request, data, length); - if (NS_FAILED(rv)) - return rv; - - // We cap the number of bytes to scan at MAX_BYTES to prevent picking up - // false positives by accidentally reading document content, e.g. a "how to - // make a feed" page. - const char* testData; - if (mDecodedData.IsEmpty()) { - testData = (const char*)data; - length = std::min(length, MAX_BYTES); - } else { - testData = mDecodedData.get(); - length = std::min(mDecodedData.Length(), MAX_BYTES); - } - - // The strategy here is based on that described in: - // http://blogs.msdn.com/rssteam/articles/PublishersGuide.aspx - // for interoperarbility purposes. - - // Thus begins the actual sniffing. - nsDependentCSubstring dataString((const char*)testData, length); - - bool isFeed = false; - - // RSS 0.91/0.92/2.0 - isFeed = ContainsTopLevelSubstring(dataString, "<rss"); - - // Atom 1.0 - if (!isFeed) - isFeed = ContainsTopLevelSubstring(dataString, "<feed"); - - // RSS 1.0 - if (!isFeed) { - isFeed = ContainsTopLevelSubstring(dataString, "<rdf:RDF") && - dataString.Find(NS_RDF) != -1 && - dataString.Find(NS_RSS) != -1; - } - - // If we sniffed a feed, coerce our internal type - if (isFeed && !HasAttachmentDisposition(channel)) - sniffedType.AssignLiteral(TYPE_MAYBE_FEED); - else - sniffedType.Truncate(); - return NS_OK; -} - -NS_IMETHODIMP -nsFeedSniffer::OnStartRequest(nsIRequest* request, nsISupports* context) -{ - return NS_OK; -} - -nsresult -nsFeedSniffer::AppendSegmentToString(nsIInputStream* inputStream, - void* closure, - const char* rawSegment, - uint32_t toOffset, - uint32_t count, - uint32_t* writeCount) -{ - nsCString* decodedData = static_cast<nsCString*>(closure); - decodedData->Append(rawSegment, count); - *writeCount = count; - return NS_OK; -} - -NS_IMETHODIMP -nsFeedSniffer::OnDataAvailable(nsIRequest* request, nsISupports* context, - nsIInputStream* stream, uint64_t offset, - uint32_t count) -{ - uint32_t read; - return stream->ReadSegments(AppendSegmentToString, &mDecodedData, count, - &read); -} - -NS_IMETHODIMP -nsFeedSniffer::OnStopRequest(nsIRequest* request, nsISupports* context, - nsresult status) -{ - return NS_OK; -} diff --git a/browser/components/feeds/nsFeedSniffer.h b/browser/components/feeds/nsFeedSniffer.h deleted file mode 100644 index a0eb9862c..000000000 --- a/browser/components/feeds/nsFeedSniffer.h +++ /dev/null @@ -1,37 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */ - - -#include "nsIContentSniffer.h" -#include "nsIStreamListener.h" -#include "nsStringAPI.h" -#include "mozilla/Attributes.h" - -class nsFeedSniffer final : public nsIContentSniffer, - nsIStreamListener -{ -public: - NS_DECL_ISUPPORTS - NS_DECL_NSICONTENTSNIFFER - NS_DECL_NSIREQUESTOBSERVER - NS_DECL_NSISTREAMLISTENER - - static nsresult AppendSegmentToString(nsIInputStream* inputStream, - void* closure, - const char* rawSegment, - uint32_t toOffset, - uint32_t count, - uint32_t* writeCount); - -protected: - ~nsFeedSniffer() {} - - nsresult ConvertEncodedData(nsIRequest* request, const uint8_t* data, - uint32_t length); - -private: - nsCString mDecodedData; -}; - diff --git a/browser/components/feeds/nsIFeedResultService.idl b/browser/components/feeds/nsIFeedResultService.idl deleted file mode 100644 index f745fa693..000000000 --- a/browser/components/feeds/nsIFeedResultService.idl +++ /dev/null @@ -1,70 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#include "nsISupports.idl" -interface nsIURI; -interface nsIRequest; -interface nsIFeedResult; - -/** - * nsIFeedResultService provides a globally-accessible object for retrieving - * the results of feed processing. - */ -[scriptable, uuid(95309fd2-7b3a-47fb-97f3-5c460d9473cd)] -interface nsIFeedResultService : nsISupports -{ - /** - * When set to true, forces the preview page to be displayed, regardless - * of the user's preferences. - */ - attribute boolean forcePreviewPage; - - /** - * Adds a URI to the user's specified external feed handler, or live - * bookmarks. - * @param uri - * The uri of the feed to add. - * @param title - * The title of the feed to add. - * @param subtitle - * The subtitle of the feed to add. - * @param feedType - * The nsIFeed type of the feed. See nsIFeed.idl - * @param feedReader - * The type of feed reader we're using (client, bookmarks, default) - * If this parameter is null, the type is set to default - */ - void addToClientReader(in AUTF8String uri, - in AString title, - in AString subtitle, - in unsigned long feedType, - [optional] in AString feedReader); - - /** - * Registers a Feed Result object with a globally accessible service - * so that it can be accessed by a singleton method outside the usual - * flow of control in document loading. - * - * @param feedResult - * An object implementing nsIFeedResult representing the feed. - */ - void addFeedResult(in nsIFeedResult feedResult); - - /** - * Gets a Feed Handler object registered using addFeedResult. - * - * @param uri - * The URI of the feed a handler is being requested for - */ - nsIFeedResult getFeedResult(in nsIURI uri); - - /** - * Unregisters a Feed Handler object registered using addFeedResult. - * @param uri - * The feed URI the handler was registered under. This must be - * the same *instance* the feed was registered under. - */ - void removeFeedResult(in nsIURI uri); -}; diff --git a/browser/components/feeds/nsIWebContentConverterRegistrar.idl b/browser/components/feeds/nsIWebContentConverterRegistrar.idl deleted file mode 100644 index 08ce2f4ae..000000000 --- a/browser/components/feeds/nsIWebContentConverterRegistrar.idl +++ /dev/null @@ -1,117 +0,0 @@ -/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 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/. */ - -#include "nsIMIMEInfo.idl" -#include "nsIWebContentHandlerRegistrar.idl" - -interface nsIRequest; - -[scriptable, uuid(eb361098-5158-4b21-8f98-50b445f1f0b2)] -interface nsIWebContentHandlerInfo : nsIHandlerApp -{ - /** - * The content type handled by the handler - */ - readonly attribute AString contentType; - - /** - * The uri of the handler, with an embedded %s where the URI of the loaded - * document will be encoded. - */ - readonly attribute AString uri; - - /** - * Gets the service URL Spec, with the loading document URI encoded in it. - * @param uri - * The URI of the document being loaded - * @returns The URI of the service with the loading document URI encoded in - * it. - */ - AString getHandlerURI(in AString uri); -}; - -[scriptable, uuid(de7cc06e-e778-45cb-b7db-7a114e1e75b1)] -interface nsIWebContentConverterService : nsIWebContentHandlerRegistrar -{ - /** - * Specifies the handler to be used to automatically handle all links of a - * certain content type from now on. - * @param contentType - * The content type to automatically load with the specified handler - * @param handler - * A web service handler. If this is null, no automatic action is - * performed and the user must choose. - * @throws NS_ERROR_NOT_AVAILABLE if the service refered to by |handler| is - * not already registered. - */ - void setAutoHandler(in AString contentType, in nsIWebContentHandlerInfo handler); - - /** - * Gets the auto handler specified for a particular content type - * @param contentType - * The content type to look up an auto handler for. - * @returns The web service handler that will automatically handle all - * documents of the specified type. null if there is no automatic - * handler. (Handlers may be registered, just none of them specified - * as "automatic"). - */ - nsIWebContentHandlerInfo getAutoHandler(in AString contentType); - - /** - * Gets a web handler for the specified service URI - * @param contentType - * The content type of the service being located - * @param uri - * The service URI of the handler to locate. - * @returns A web service handler that uses the specified uri. - */ - nsIWebContentHandlerInfo getWebContentHandlerByURI(in AString contentType, - in AString uri); - - /** - * Loads the preferred handler when content of a registered type is about - * to be loaded. - * @param request - * The nsIRequest for the load of the content - */ - void loadPreferredHandler(in nsIRequest request); - - /** - * Removes a registered protocol handler - * @param protocol - * The protocol scheme to remove a service handler for - * @param uri - * The uri of the service handler to remove - */ - void removeProtocolHandler(in AString protocol, in AString uri); - - /** - * Removes a registered content handler - * @param contentType - * The content type to remove a service handler for - * @param uri - * The uri of the service handler to remove - */ - void removeContentHandler(in AString contentType, in AString uri); - - /** - * Gets the list of content handlers for a particular type. - * @param contentType - * The content type to get handlers for - * @returns An array of nsIWebContentHandlerInfo objects - */ - void getContentHandlers(in AString contentType, - [optional] out unsigned long count, - [retval,array,size_is(count)] out nsIWebContentHandlerInfo handlers); - - /** - * Resets the list of available content handlers to the default set from - * the distribution. - * @param contentType - * The content type to reset handlers for - */ - void resetHandlersForType(in AString contentType); -}; - |