# -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- # This Source Code Form is subject to the terms of the Mozilla Public # License, v. 2.0. If a copy of the MPL was not distributed with this # file, You can obtain one at http://mozilla.org/MPL/2.0/. /** * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages * and shows UI when they are discovered. */ var FeedHandler = { /** 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) * @returns 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: function(container, isSubview) { var 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. var itemNodeType = isSubview ? "toolbarbutton" : "menuitem"; for (let feedInfo of feeds) { var item = document.createElement(itemNodeType); var baseTitle = feedInfo.title || feedInfo.href; var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]); item.setAttribute("label", labelStr); 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: function(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); var 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: function(href, event) { var 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: function() { if (this._updateFeedTimeout) clearTimeout(this._updateFeedTimeout); var feeds = gBrowser.selectedBrowser.feeds; var haveFeeds = feeds && feeds.length > 0; var 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: function(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); } } };