summaryrefslogtreecommitdiffstats
path: root/application/basilisk/base/content/browser-feeds.js
diff options
context:
space:
mode:
authorwolfbeast <mcwerewolf@gmail.com>2018-07-18 08:24:24 +0200
committerwolfbeast <mcwerewolf@gmail.com>2018-07-18 08:24:24 +0200
commitfc61780b35af913801d72086456f493f63197da6 (patch)
treef85891288a7bd988da9f0f15ae64e5c63f00d493 /application/basilisk/base/content/browser-feeds.js
parent69f7f9e5f1475891ce11cc4f431692f965b0cd30 (diff)
parent50d3e596bbe89c95615f96eb71f6bc5be737a1db (diff)
downloadUXP-2018.07.18.tar
UXP-2018.07.18.tar.gz
UXP-2018.07.18.tar.lz
UXP-2018.07.18.tar.xz
UXP-2018.07.18.zip
Merge commit '50d3e596bbe89c95615f96eb71f6bc5be737a1db' into Basilisk-releasev2018.07.18
# Conflicts: # browser/app/profile/firefox.js # browser/components/preferences/jar.mn
Diffstat (limited to 'application/basilisk/base/content/browser-feeds.js')
-rw-r--r--application/basilisk/base/content/browser-feeds.js646
1 files changed, 646 insertions, 0 deletions
diff --git a/application/basilisk/base/content/browser-feeds.js b/application/basilisk/base/content/browser-feeds.js
new file mode 100644
index 000000000..6f29d8915
--- /dev/null
+++ b/application/basilisk/base/content/browser-feeds.js
@@ -0,0 +1,646 @@
+/* -*- 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;
+ }
+ },
+};