michael@0: # -*- Mode: javascript; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- michael@0: # This Source Code Form is subject to the terms of the Mozilla Public michael@0: # License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: # file, You can obtain one at http://mozilla.org/MPL/2.0/. michael@0: michael@0: /** michael@0: * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages michael@0: * and shows UI when they are discovered. michael@0: */ michael@0: var FeedHandler = { michael@0: /** Called when the user clicks on the Subscribe to This Page... menu item, michael@0: * or when the user clicks the feed button when the page contains multiple michael@0: * feeds. michael@0: * Builds a menu of unique feeds associated with the page, and if there michael@0: * is only one, shows the feed inline in the browser window. michael@0: * @param container michael@0: * The feed list container (menupopup or subview) to be populated. michael@0: * @param isSubview michael@0: * Whether we're creating a subview (true) or menu (false/undefined) michael@0: * @returns true if the menu/subview should be shown, false if there was only michael@0: * one feed and the feed should be shown inline in the browser michael@0: * window (do not show the menupopup/subview). michael@0: */ michael@0: buildFeedList: function(container, isSubview) { michael@0: var feeds = gBrowser.selectedBrowser.feeds; michael@0: if (!isSubview && feeds == null) { michael@0: // XXX hack -- menu opening depends on setting of an "open" michael@0: // attribute, and the menu refuses to open if that attribute is michael@0: // set (because it thinks it's already open). onpopupshowing gets michael@0: // called after the attribute is unset, and it doesn't get unset michael@0: // if we return false. so we unset it here; otherwise, the menu michael@0: // refuses to work past this point. michael@0: container.parentNode.removeAttribute("open"); michael@0: return false; michael@0: } michael@0: michael@0: for (let i = container.childNodes.length - 1; i >= 0; --i) { michael@0: let node = container.childNodes[i]; michael@0: if (isSubview && node.localName == "label") michael@0: continue; michael@0: container.removeChild(node); michael@0: } michael@0: michael@0: if (!feeds || feeds.length <= 1) michael@0: return false; michael@0: michael@0: // Build the menu showing the available feed choices for viewing. michael@0: var itemNodeType = isSubview ? "toolbarbutton" : "menuitem"; michael@0: for (let feedInfo of feeds) { michael@0: var item = document.createElement(itemNodeType); michael@0: var baseTitle = feedInfo.title || feedInfo.href; michael@0: var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]); michael@0: item.setAttribute("label", labelStr); michael@0: item.setAttribute("feed", feedInfo.href); michael@0: item.setAttribute("tooltiptext", feedInfo.href); michael@0: item.setAttribute("crop", "center"); michael@0: let className = "feed-" + itemNodeType; michael@0: if (isSubview) { michael@0: className += " subviewbutton"; michael@0: } michael@0: item.setAttribute("class", className); michael@0: container.appendChild(item); michael@0: } michael@0: return true; michael@0: }, michael@0: michael@0: /** michael@0: * Subscribe to a given feed. Called when michael@0: * 1. Page has a single feed and user clicks feed icon in location bar michael@0: * 2. Page has a single feed and user selects Subscribe menu item michael@0: * 3. Page has multiple feeds and user selects from feed icon popup (or subview) michael@0: * 4. Page has multiple feeds and user selects from Subscribe submenu michael@0: * @param href michael@0: * The feed to subscribe to. May be null, in which case the michael@0: * event target's feed attribute is examined. michael@0: * @param event michael@0: * The event this method is handling. Used to decide where michael@0: * to open the preview UI. (Optional, unless href is null) michael@0: */ michael@0: subscribeToFeed: function(href, event) { michael@0: // Just load the feed in the content area to either subscribe or show the michael@0: // preview UI michael@0: if (!href) michael@0: href = event.target.getAttribute("feed"); michael@0: urlSecurityCheck(href, gBrowser.contentPrincipal, michael@0: Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL); michael@0: var feedURI = makeURI(href, document.characterSet); michael@0: // Use the feed scheme so X-Moz-Is-Feed will be set michael@0: // The value doesn't matter michael@0: if (/^https?$/.test(feedURI.scheme)) michael@0: href = "feed:" + href; michael@0: this.loadFeed(href, event); michael@0: }, michael@0: michael@0: loadFeed: function(href, event) { michael@0: var feeds = gBrowser.selectedBrowser.feeds; michael@0: try { michael@0: openUILink(href, event, { ignoreAlt: true }); michael@0: } michael@0: finally { michael@0: // We might default to a livebookmarks modal dialog, michael@0: // so reset that if the user happens to click it again michael@0: gBrowser.selectedBrowser.feeds = feeds; michael@0: } michael@0: }, michael@0: michael@0: get _feedMenuitem() { michael@0: delete this._feedMenuitem; michael@0: return this._feedMenuitem = document.getElementById("singleFeedMenuitemState"); michael@0: }, michael@0: michael@0: get _feedMenupopup() { michael@0: delete this._feedMenupopup; michael@0: return this._feedMenupopup = document.getElementById("multipleFeedsMenuState"); michael@0: }, michael@0: michael@0: /** michael@0: * Update the browser UI to show whether or not feeds are available when michael@0: * a page is loaded or the user switches tabs to a page that has feeds. michael@0: */ michael@0: updateFeeds: function() { michael@0: if (this._updateFeedTimeout) michael@0: clearTimeout(this._updateFeedTimeout); michael@0: michael@0: var feeds = gBrowser.selectedBrowser.feeds; michael@0: var haveFeeds = feeds && feeds.length > 0; michael@0: michael@0: var feedButton = document.getElementById("feed-button"); michael@0: if (feedButton) { michael@0: if (haveFeeds) { michael@0: feedButton.removeAttribute("disabled"); michael@0: } else { michael@0: feedButton.setAttribute("disabled", "true"); michael@0: } michael@0: } michael@0: michael@0: if (!haveFeeds) { michael@0: this._feedMenuitem.setAttribute("disabled", "true"); michael@0: this._feedMenuitem.removeAttribute("hidden"); michael@0: this._feedMenupopup.setAttribute("hidden", "true"); michael@0: return; michael@0: } michael@0: michael@0: if (feeds.length > 1) { michael@0: this._feedMenuitem.setAttribute("hidden", "true"); michael@0: this._feedMenupopup.removeAttribute("hidden"); michael@0: } else { michael@0: this._feedMenuitem.setAttribute("feed", feeds[0].href); michael@0: this._feedMenuitem.removeAttribute("disabled"); michael@0: this._feedMenuitem.removeAttribute("hidden"); michael@0: this._feedMenupopup.setAttribute("hidden", "true"); michael@0: } michael@0: }, michael@0: michael@0: addFeed: function(link, browserForLink) { michael@0: if (!browserForLink.feeds) michael@0: browserForLink.feeds = []; michael@0: michael@0: browserForLink.feeds.push({ href: link.href, title: link.title }); michael@0: michael@0: // If this addition was for the current browser, update the UI. For michael@0: // background browsers, we'll update on tab switch. michael@0: if (browserForLink == gBrowser.selectedBrowser) { michael@0: // Batch updates to avoid updating the UI for multiple onLinkAdded events michael@0: // fired within 100ms of each other. michael@0: if (this._updateFeedTimeout) michael@0: clearTimeout(this._updateFeedTimeout); michael@0: this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100); michael@0: } michael@0: } michael@0: };