diff options
Diffstat (limited to 'browser/components/feeds/WebContentConverter.js')
-rw-r--r-- | browser/components/feeds/WebContentConverter.js | 1071 |
1 files changed, 0 insertions, 1071 deletions
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]); |