diff options
Diffstat (limited to 'browser/base/content/browser-feeds.js')
-rw-r--r-- | browser/base/content/browser-feeds.js | 646 |
1 files changed, 0 insertions, 646 deletions
diff --git a/browser/base/content/browser-feeds.js b/browser/base/content/browser-feeds.js deleted file mode 100644 index 6f29d8915..000000000 --- a/browser/base/content/browser-feeds.js +++ /dev/null @@ -1,646 +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/. */ - -XPCOMUtils.defineLazyModuleGetter(this, "DeferredTask", - "resource://gre/modules/DeferredTask.jsm"); - -const TYPE_MAYBE_FEED = "application/vnd.mozilla.maybe.feed"; -const TYPE_MAYBE_AUDIO_FEED = "application/vnd.mozilla.maybe.audio.feed"; -const TYPE_MAYBE_VIDEO_FEED = "application/vnd.mozilla.maybe.video.feed"; - -const PREF_SHOW_FIRST_RUN_UI = "browser.feeds.showFirstRunUI"; - -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"; - -const PREF_UPDATE_DELAY = 2000; - -const SETTABLE_PREFS = new Set([ - PREF_VIDEO_SELECTED_ACTION, - PREF_AUDIO_SELECTED_ACTION, - PREF_SELECTED_ACTION, - PREF_VIDEO_SELECTED_READER, - PREF_AUDIO_SELECTED_READER, - PREF_SELECTED_READER, - PREF_VIDEO_SELECTED_WEB, - PREF_AUDIO_SELECTED_WEB, - PREF_SELECTED_WEB -]); - -const EXECUTABLE_PREFS = new Set([ - PREF_SELECTED_APP, - PREF_VIDEO_SELECTED_APP, - PREF_AUDIO_SELECTED_APP -]); - -const VALID_ACTIONS = new Set(["ask", "reader", "bookmarks"]); -const VALID_READERS = new Set(["web", "client", "default", "bookmarks"]); - -XPCOMUtils.defineLazyPreferenceGetter(this, "SHOULD_LOG", - "feeds.log", false); - -function LOG(str) { - if (SHOULD_LOG) - dump("*** Feeds: " + str + "\n"); -} - -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 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 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; - } -} - -/** - * Maps a feed type to a maybe-feed mimetype. - */ -function getMimeTypeForFeedType(aFeedType) { - switch (aFeedType) { - case Ci.nsIFeed.TYPE_VIDEO: - return TYPE_MAYBE_VIDEO_FEED; - - case Ci.nsIFeed.TYPE_AUDIO: - return TYPE_MAYBE_AUDIO_FEED; - - default: - return TYPE_MAYBE_FEED; - } -} - -/** - * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages - * and shows UI when they are discovered. - */ -var FeedHandler = { - _prefChangeCallback: null, - - /** Called when the user clicks on the Subscribe to This Page... menu item, - * or when the user clicks the feed button when the page contains multiple - * feeds. - * Builds a menu of unique feeds associated with the page, and if there - * is only one, shows the feed inline in the browser window. - * @param container - * The feed list container (menupopup or subview) to be populated. - * @param isSubview - * Whether we're creating a subview (true) or menu (false/undefined) - * @return true if the menu/subview should be shown, false if there was only - * one feed and the feed should be shown inline in the browser - * window (do not show the menupopup/subview). - */ - buildFeedList(container, isSubview) { - let feeds = gBrowser.selectedBrowser.feeds; - if (!isSubview && feeds == null) { - // XXX hack -- menu opening depends on setting of an "open" - // attribute, and the menu refuses to open if that attribute is - // set (because it thinks it's already open). onpopupshowing gets - // called after the attribute is unset, and it doesn't get unset - // if we return false. so we unset it here; otherwise, the menu - // refuses to work past this point. - container.parentNode.removeAttribute("open"); - return false; - } - - for (let i = container.childNodes.length - 1; i >= 0; --i) { - let node = container.childNodes[i]; - if (isSubview && node.localName == "label") - continue; - container.removeChild(node); - } - - if (!feeds || feeds.length <= 1) - return false; - - // Build the menu showing the available feed choices for viewing. - let itemNodeType = isSubview ? "toolbarbutton" : "menuitem"; - for (let feedInfo of feeds) { - let item = document.createElement(itemNodeType); - let baseTitle = feedInfo.title || feedInfo.href; - item.setAttribute("label", baseTitle); - item.setAttribute("feed", feedInfo.href); - item.setAttribute("tooltiptext", feedInfo.href); - item.setAttribute("crop", "center"); - let className = "feed-" + itemNodeType; - if (isSubview) { - className += " subviewbutton"; - } - item.setAttribute("class", className); - container.appendChild(item); - } - return true; - }, - - /** - * Subscribe to a given feed. Called when - * 1. Page has a single feed and user clicks feed icon in location bar - * 2. Page has a single feed and user selects Subscribe menu item - * 3. Page has multiple feeds and user selects from feed icon popup (or subview) - * 4. Page has multiple feeds and user selects from Subscribe submenu - * @param href - * The feed to subscribe to. May be null, in which case the - * event target's feed attribute is examined. - * @param event - * The event this method is handling. Used to decide where - * to open the preview UI. (Optional, unless href is null) - */ - subscribeToFeed(href, event) { - // Just load the feed in the content area to either subscribe or show the - // preview UI - if (!href) - href = event.target.getAttribute("feed"); - urlSecurityCheck(href, gBrowser.contentPrincipal, - Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL); - let feedURI = makeURI(href, document.characterSet); - // Use the feed scheme so X-Moz-Is-Feed will be set - // The value doesn't matter - if (/^https?$/.test(feedURI.scheme)) - href = "feed:" + href; - this.loadFeed(href, event); - }, - - loadFeed(href, event) { - let feeds = gBrowser.selectedBrowser.feeds; - try { - openUILink(href, event, { ignoreAlt: true }); - } - finally { - // We might default to a livebookmarks modal dialog, - // so reset that if the user happens to click it again - gBrowser.selectedBrowser.feeds = feeds; - } - }, - - get _feedMenuitem() { - delete this._feedMenuitem; - return this._feedMenuitem = document.getElementById("singleFeedMenuitemState"); - }, - - get _feedMenupopup() { - delete this._feedMenupopup; - return this._feedMenupopup = document.getElementById("multipleFeedsMenuState"); - }, - - /** - * Update the browser UI to show whether or not feeds are available when - * a page is loaded or the user switches tabs to a page that has feeds. - */ - updateFeeds() { - if (this._updateFeedTimeout) - clearTimeout(this._updateFeedTimeout); - - let feeds = gBrowser.selectedBrowser.feeds; - let haveFeeds = feeds && feeds.length > 0; - - let feedButton = document.getElementById("feed-button"); - if (feedButton) { - if (haveFeeds) { - feedButton.removeAttribute("disabled"); - } else { - feedButton.setAttribute("disabled", "true"); - } - } - - if (!haveFeeds) { - this._feedMenuitem.setAttribute("disabled", "true"); - this._feedMenuitem.removeAttribute("hidden"); - this._feedMenupopup.setAttribute("hidden", "true"); - return; - } - - if (feeds.length > 1) { - this._feedMenuitem.setAttribute("hidden", "true"); - this._feedMenupopup.removeAttribute("hidden"); - } else { - this._feedMenuitem.setAttribute("feed", feeds[0].href); - this._feedMenuitem.removeAttribute("disabled"); - this._feedMenuitem.removeAttribute("hidden"); - this._feedMenupopup.setAttribute("hidden", "true"); - } - }, - - addFeed(link, browserForLink) { - if (!browserForLink.feeds) - browserForLink.feeds = []; - - browserForLink.feeds.push({ href: link.href, title: link.title }); - - // If this addition was for the current browser, update the UI. For - // background browsers, we'll update on tab switch. - if (browserForLink == gBrowser.selectedBrowser) { - // Batch updates to avoid updating the UI for multiple onLinkAdded events - // fired within 100ms of each other. - if (this._updateFeedTimeout) - clearTimeout(this._updateFeedTimeout); - this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100); - } - }, - - /** - * Get the human-readable display name of a file. This could be the - * application name. - * @param file - * A nsIFile to look up the name of - * @return The display name of the application represented by the file. - */ - _getFileDisplayName(file) { - switch (AppConstants.platform) { - case "win": - if (file instanceof Ci.nsILocalFileWin) { - try { - return file.getVersionInfoField("FileDescription"); - } catch (e) {} - } - break; - case "macosx": - if (file instanceof Ci.nsILocalFileMac) { - try { - return file.bundleDisplayName; - } catch (e) {} - } - break; - } - - return file.leafName; - }, - - _chooseClientApp(aTitle, aTypeName, aBrowser) { - const prefName = getPrefAppForType(aTypeName); - let fp = Cc["@mozilla.org/filepicker;1"].createInstance(Ci.nsIFilePicker); - - fp.init(window, aTitle, Ci.nsIFilePicker.modeOpen); - fp.appendFilters(Ci.nsIFilePicker.filterApps); - - fp.open((aResult) => { - if (aResult == Ci.nsIFilePicker.returnOK) { - let selectedApp = fp.file; - if (selectedApp) { - // XXXben - we need to compare this with the running instance - // executable just don't know how to do that via script - // XXXmano TBD: can probably add this to nsIShellService - let appName = ""; - switch (AppConstants.platform) { - case "win": - appName = AppConstants.MOZ_APP_NAME + ".exe"; - break; - case "macosx": - appName = AppConstants.MOZ_MACBUNDLE_NAME; - break; - default: - appName = AppConstants.MOZ_APP_NAME + "-bin"; - break; - } - - if (fp.file.leafName != appName) { - Services.prefs.setComplexValue(prefName, Ci.nsILocalFile, selectedApp); - aBrowser.messageManager.sendAsyncMessage("FeedWriter:SetApplicationLauncherMenuItem", - { name: this._getFileDisplayName(selectedApp), - type: "SelectedAppMenuItem" }); - } - } - } - }); - - }, - - executeClientApp(aSpec, aTitle, aSubtitle, aFeedHandler) { - // aFeedHandler is either "default", indicating the system default reader, or a pref-name containing - // an nsILocalFile pointing to the feed handler's executable. - - let clientApp = null; - if (aFeedHandler == "default") { - clientApp = Cc["@mozilla.org/browser/shell-service;1"] - .getService(Ci.nsIShellService) - .defaultFeedReader; - } else { - clientApp = Services.prefs.getComplexValue(aFeedHandler, Ci.nsILocalFile); - } - - // For the benefit of applications that might know how to deal with more - // URLs than just feeds, send feed: URLs in the following format: - // - // http urls: replace scheme with feed, e.g. - // http://foo.com/index.rdf -> feed://foo.com/index.rdf - // other urls: prepend feed: scheme, e.g. - // https://foo.com/index.rdf -> feed:https://foo.com/index.rdf - let feedURI = NetUtil.newURI(aSpec); - if (feedURI.schemeIs("http")) { - feedURI.scheme = "feed"; - aSpec = feedURI.spec; - } else { - aSpec = "feed:" + aSpec; - } - - // Retrieving the shell service might fail on some systems, most - // notably systems where GNOME is not installed. - try { - let ss = Cc["@mozilla.org/browser/shell-service;1"] - .getService(Ci.nsIShellService); - ss.openApplicationWithURI(clientApp, aSpec); - } catch (e) { - // If we couldn't use the shell service, fallback to using a - // nsIProcess instance - let p = Cc["@mozilla.org/process/util;1"] - .createInstance(Ci.nsIProcess); - p.init(clientApp); - p.run(false, [aSpec], 1); - } - }, - - // nsISupports - - QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, - Ci.nsISupportsWeakReference]), - - init() { - window.messageManager.addMessageListener("FeedWriter:ChooseClientApp", this); - window.messageManager.addMessageListener("FeedWriter:GetSubscriptionUI", this); - window.messageManager.addMessageListener("FeedWriter:SetFeedPrefsAndSubscribe", this); - window.messageManager.addMessageListener("FeedWriter:ShownFirstRun", this); - - Services.ppmm.addMessageListener("FeedConverter:ExecuteClientApp", this); - - const prefs = Services.prefs; - prefs.addObserver(PREF_SELECTED_ACTION, this, true); - prefs.addObserver(PREF_SELECTED_READER, this, true); - prefs.addObserver(PREF_SELECTED_WEB, this, true); - prefs.addObserver(PREF_VIDEO_SELECTED_ACTION, this, true); - prefs.addObserver(PREF_VIDEO_SELECTED_READER, this, true); - prefs.addObserver(PREF_VIDEO_SELECTED_WEB, this, true); - prefs.addObserver(PREF_AUDIO_SELECTED_ACTION, this, true); - prefs.addObserver(PREF_AUDIO_SELECTED_READER, this, true); - prefs.addObserver(PREF_AUDIO_SELECTED_WEB, this, true); - }, - - uninit() { - Services.ppmm.removeMessageListener("FeedConverter:ExecuteClientApp", this); - - this._prefChangeCallback = null; - }, - - // nsIObserver - observe(subject, topic, data) { - if (topic == "nsPref:changed") { - LOG(`Pref changed ${data}`) - if (this._prefChangeCallback) { - this._prefChangeCallback.disarm(); - } - // Multiple prefs are set at the same time, debounce to reduce noise - // This can happen in one feed and we want to message all feed pages - this._prefChangeCallback = new DeferredTask(() => { - this._prefChanged(data); - }, PREF_UPDATE_DELAY); - this._prefChangeCallback.arm(); - } - }, - - _prefChanged(prefName) { - // Don't observe for PREF_*SELECTED_APP as user likely just picked one - // That is also handled by SetApplicationLauncherMenuItem call - // Rather than the others which happen on subscription - switch (prefName) { - case PREF_SELECTED_READER: - case PREF_SELECTED_WEB: - case PREF_VIDEO_SELECTED_READER: - case PREF_VIDEO_SELECTED_WEB: - case PREF_AUDIO_SELECTED_READER: - case PREF_AUDIO_SELECTED_WEB: - case PREF_SELECTED_ACTION: - case PREF_VIDEO_SELECTED_ACTION: - case PREF_AUDIO_SELECTED_ACTION: - const response = { - default: this._getReaderForType(Ci.nsIFeed.TYPE_FEED), - [Ci.nsIFeed.TYPE_AUDIO]: this._getReaderForType(Ci.nsIFeed.TYPE_AUDIO), - [Ci.nsIFeed.TYPE_VIDEO]: this._getReaderForType(Ci.nsIFeed.TYPE_VIDEO) - }; - Services.mm.broadcastAsyncMessage("FeedWriter:PreferenceUpdated", - response); - break; - } - }, - - _initSubscriptionUIResponse(feedType) { - const wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. - getService(Ci.nsIWebContentConverterService); - const handlersRaw = wccr.getContentHandlers(getMimeTypeForFeedType(feedType)); - const handlers = []; - for (let handler of handlersRaw) { - LOG(`Handler found: ${handler}`); - handlers.push({ - name: handler.name, - uri: handler.uri - }); - } - let showFirstRunUI = true; - // eslint-disable-next-line mozilla/use-default-preference-values - try { - showFirstRunUI = Services.prefs.getBoolPref(PREF_SHOW_FIRST_RUN_UI); - } catch (ex) { } - const response = { handlers, showFirstRunUI }; - let selectedClientApp; - const feedTypePref = getPrefAppForType(feedType); - try { - selectedClientApp = Services.prefs.getComplexValue(feedTypePref, Ci.nsILocalFile); - } catch (ex) { - // Just do nothing, then we won't bother populating - } - - let defaultClientApp = null; - try { - // This can sometimes not exist - defaultClientApp = Cc["@mozilla.org/browser/shell-service;1"] - .getService(Ci.nsIShellService) - .defaultFeedReader; - } catch (ex) { - // Just do nothing, then we don't bother populating - } - - if (selectedClientApp && selectedClientApp.exists()) { - if (defaultClientApp && selectedClientApp.path != defaultClientApp.path) { - // Only set the default menu item if it differs from the selected one - response.defaultMenuItem = this._getFileDisplayName(defaultClientApp); - } - response.selectedMenuItem = this._getFileDisplayName(selectedClientApp); - } - response.reader = this._getReaderForType(feedType); - return response; - }, - - _setPref(aPrefName, aPrefValue, aIsComplex = false) { - LOG(`FeedWriter._setPref ${aPrefName}`); - // Ensure we have a pref that is settable - if (aPrefName && SETTABLE_PREFS.has(aPrefName)) { - if (aIsComplex) { - const supportsString = Cc["@mozilla.org/supports-string;1"]. - createInstance(Ci.nsISupportsString); - supportsString.data = aPrefValue; - Services.prefs.setComplexValue(aPrefName, Ci.nsISupportsString, supportsString); - } else { - Services.prefs.setCharPref(aPrefName, aPrefValue); - } - } else { - LOG(`FeedWriter._setPref ${aPrefName} not allowed`); - } - }, - - _getReaderForType(feedType) { - let prefs = Services.prefs; - let handler = "bookmarks"; - let url; - // eslint-disable-next-line mozilla/use-default-preference-values - try { - handler = prefs.getCharPref(getPrefReaderForType(feedType)); - } catch (ex) { } - - if (handler === "web") { - try { - url = prefs.getComplexValue(getPrefWebForType(feedType), Ci.nsISupportsString).data; - } catch (ex) { - LOG("FeedWriter._setSelectedHandler: invalid or no handler in prefs"); - url = null; - } - } - const alwaysUse = this._getAlwaysUseState(feedType); - const action = prefs.getCharPref(getPrefActionForType(feedType)); - return { handler, url, alwaysUse, action }; - }, - - _getAlwaysUseState(feedType) { - try { - return Services.prefs.getCharPref(getPrefActionForType(feedType)) != "ask"; - } catch (ex) { } - return false; - }, - - receiveMessage(msg) { - let handler; - switch (msg.name) { - case "FeedWriter:GetSubscriptionUI": - const response = this._initSubscriptionUIResponse(msg.data.feedType); - msg.target.messageManager - .sendAsyncMessage("FeedWriter:GetSubscriptionUIResponse", - response); - break; - case "FeedWriter:ChooseClientApp": - this._chooseClientApp(msg.data.title, msg.data.feedType, msg.target); - break; - case "FeedWriter:ShownFirstRun": - Services.prefs.setBoolPref(PREF_SHOW_FIRST_RUN_UI, false); - break; - case "FeedWriter:SetFeedPrefsAndSubscribe": - const settings = msg.data; - if (!settings.action || !VALID_ACTIONS.has(settings.action)) { - LOG(`Invalid action ${settings.action}`); - return; - } - if (!settings.reader || !VALID_READERS.has(settings.reader)) { - LOG(`Invalid reader ${settings.reader}`); - return; - } - const actionPref = getPrefActionForType(settings.feedType); - this._setPref(actionPref, settings.action); - const readerPref = getPrefReaderForType(settings.feedType); - this._setPref(readerPref, settings.reader); - handler = null; - - switch (settings.reader) { - case "web": - // This is a web set URI by content using window.registerContentHandler() - // Lets make sure we know about it before setting it - const webPref = getPrefWebForType(settings.feedType); - let wccr = Cc["@mozilla.org/embeddor.implemented/web-content-handler-registrar;1"]. - getService(Ci.nsIWebContentConverterService); - // If the user provided an invalid web URL this function won't give us a reference - handler = wccr.getWebContentHandlerByURI(getMimeTypeForFeedType(settings.feedType), settings.uri); - if (handler) { - this._setPref(webPref, settings.uri, true); - if (settings.useAsDefault) { - wccr.setAutoHandler(getMimeTypeForFeedType(settings.feedType), handler); - } - msg.target.messageManager - .sendAsyncMessage("FeedWriter:SetFeedPrefsAndSubscribeResponse", - { redirect: handler.getHandlerURI(settings.feedLocation) }); - } else { - LOG(`No handler found for web ${settings.feedType} ${settings.uri}`); - } - break; - default: - const feedService = Cc["@mozilla.org/browser/feeds/result-service;1"]. - getService(Ci.nsIFeedResultService); - - feedService.addToClientReader(settings.feedLocation, - settings.feedTitle, - settings.feedSubtitle, - settings.feedType, - settings.reader); - } - break; - case "FeedConverter:ExecuteClientApp": - // Always check feedHandler is from a set array of executable prefs - if (EXECUTABLE_PREFS.has(msg.data.feedHandler)) { - this.executeClientApp(msg.data.spec, msg.data.title, - msg.data.subtitle, msg.data.feedHandler); - } else { - LOG(`FeedConverter:ExecuteClientApp - Will not exec ${msg.data.feedHandler}`); - } - break; - } - }, -}; |