forked from mirrors/gecko-dev
		
	 19c596cba2
			
		
	
	
		19c596cba2
		
	
	
	
	
		
			
			MozReview-Commit-ID: 4lDdvZoklM --HG-- extra : rebase_source : 33a02db3d33f853f0680083a07fdeec55ce794a0
		
			
				
	
	
		
			643 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			643 lines
		
	
	
	
		
			22 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| /* -*- 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) {
 | |
|         Services.prefs.setStringPref(aPrefName, aPrefValue);
 | |
|       } 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.getStringPref(getPrefWebForType(feedType));
 | |
|       } 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;
 | |
|     }
 | |
|   },
 | |
| };
 |