 |
1 # -*- indent-tabs-mode: nil; js-indent-level: 2 -*-
2 # This Source Code Form is subject to the terms of the Mozilla Public
3 # License, v. 2.0. If a copy of the MPL was not distributed with this
4 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
5
6 /**
7 * The Feed Handler object manages discovery of RSS/ATOM feeds in web pages
8 * and shows UI when they are discovered.
9 */
10 var FeedHandler = {
11 /** Called when the user clicks on the Subscribe to This Page... menu item,
12 * or when the user clicks the feed button when the page contains multiple
13 * feeds.
14 * Builds a menu of unique feeds associated with the page, and if there
15 * is only one, shows the feed inline in the browser window.
16 * @param container
17 * The feed list container (menupopup or subview) to be populated.
18 * @param isSubview
19 * Whether we're creating a subview (true) or menu (false/undefined)
20 * @returns true if the menu/subview should be shown, false if there was only
21 * one feed and the feed should be shown inline in the browser
22 * window (do not show the menupopup/subview).
23 */
24 buildFeedList: function(container, isSubview) {
25 var feeds = gBrowser.selectedBrowser.feeds;
26 if (!isSubview && feeds == null) {
27 // XXX hack -- menu opening depends on setting of an "open"
28 // attribute, and the menu refuses to open if that attribute is
29 // set (because it thinks it's already open). onpopupshowing gets
30 // called after the attribute is unset, and it doesn't get unset
31 // if we return false. so we unset it here; otherwise, the menu
32 // refuses to work past this point.
33 container.parentNode.removeAttribute("open");
34 return false;
35 }
36
37 for (let i = container.childNodes.length - 1; i >= 0; --i) {
38 let node = container.childNodes[i];
39 if (isSubview && node.localName == "label")
40 continue;
41 container.removeChild(node);
42 }
43
44 if (!feeds || feeds.length <= 1)
45 return false;
46
47 // Build the menu showing the available feed choices for viewing.
48 var itemNodeType = isSubview ? "toolbarbutton" : "menuitem";
49 for (let feedInfo of feeds) {
50 var item = document.createElement(itemNodeType);
51 var baseTitle = feedInfo.title || feedInfo.href;
52 var labelStr = gNavigatorBundle.getFormattedString("feedShowFeedNew", [baseTitle]);
53 item.setAttribute("label", labelStr);
54 item.setAttribute("feed", feedInfo.href);
55 item.setAttribute("tooltiptext", feedInfo.href);
56 item.setAttribute("crop", "center");
57 let className = "feed-" + itemNodeType;
58 if (isSubview) {
59 className += " subviewbutton";
60 }
61 item.setAttribute("class", className);
62 container.appendChild(item);
63 }
64 return true;
65 },
66
67 /**
68 * Subscribe to a given feed. Called when
69 * 1. Page has a single feed and user clicks feed icon in location bar
70 * 2. Page has a single feed and user selects Subscribe menu item
71 * 3. Page has multiple feeds and user selects from feed icon popup (or subview)
72 * 4. Page has multiple feeds and user selects from Subscribe submenu
73 * @param href
74 * The feed to subscribe to. May be null, in which case the
75 * event target's feed attribute is examined.
76 * @param event
77 * The event this method is handling. Used to decide where
78 * to open the preview UI. (Optional, unless href is null)
79 */
80 subscribeToFeed: function(href, event) {
81 // Just load the feed in the content area to either subscribe or show the
82 // preview UI
83 if (!href)
84 href = event.target.getAttribute("feed");
85 urlSecurityCheck(href, gBrowser.contentPrincipal,
86 Ci.nsIScriptSecurityManager.DISALLOW_INHERIT_PRINCIPAL);
87 var feedURI = makeURI(href, document.characterSet);
88 // Use the feed scheme so X-Moz-Is-Feed will be set
89 // The value doesn't matter
90 if (/^https?$/.test(feedURI.scheme))
91 href = "feed:" + href;
92 this.loadFeed(href, event);
93 },
94
95 loadFeed: function(href, event) {
96 var feeds = gBrowser.selectedBrowser.feeds;
97 try {
98 openUILink(href, event, { ignoreAlt: true });
99 }
100 finally {
101 // We might default to a livebookmarks modal dialog,
102 // so reset that if the user happens to click it again
103 gBrowser.selectedBrowser.feeds = feeds;
104 }
105 },
106
107 get _feedMenuitem() {
108 delete this._feedMenuitem;
109 return this._feedMenuitem = document.getElementById("singleFeedMenuitemState");
110 },
111
112 get _feedMenupopup() {
113 delete this._feedMenupopup;
114 return this._feedMenupopup = document.getElementById("multipleFeedsMenuState");
115 },
116
117 /**
118 * Update the browser UI to show whether or not feeds are available when
119 * a page is loaded or the user switches tabs to a page that has feeds.
120 */
121 updateFeeds: function() {
122 if (this._updateFeedTimeout)
123 clearTimeout(this._updateFeedTimeout);
124
125 var feeds = gBrowser.selectedBrowser.feeds;
126 var haveFeeds = feeds && feeds.length > 0;
127
128 var feedButton = document.getElementById("feed-button");
129 if (feedButton) {
130 if (haveFeeds) {
131 feedButton.removeAttribute("disabled");
132 } else {
133 feedButton.setAttribute("disabled", "true");
134 }
135 }
136
137 if (!haveFeeds) {
138 this._feedMenuitem.setAttribute("disabled", "true");
139 this._feedMenuitem.removeAttribute("hidden");
140 this._feedMenupopup.setAttribute("hidden", "true");
141 return;
142 }
143
144 if (feeds.length > 1) {
145 this._feedMenuitem.setAttribute("hidden", "true");
146 this._feedMenupopup.removeAttribute("hidden");
147 } else {
148 this._feedMenuitem.setAttribute("feed", feeds[0].href);
149 this._feedMenuitem.removeAttribute("disabled");
150 this._feedMenuitem.removeAttribute("hidden");
151 this._feedMenupopup.setAttribute("hidden", "true");
152 }
153 },
154
155 addFeed: function(link, browserForLink) {
156 if (!browserForLink.feeds)
157 browserForLink.feeds = [];
158
159 browserForLink.feeds.push({ href: link.href, title: link.title });
160
161 // If this addition was for the current browser, update the UI. For
162 // background browsers, we'll update on tab switch.
163 if (browserForLink == gBrowser.selectedBrowser) {
164 // Batch updates to avoid updating the UI for multiple onLinkAdded events
165 // fired within 100ms of each other.
166 if (this._updateFeedTimeout)
167 clearTimeout(this._updateFeedTimeout);
168 this._updateFeedTimeout = setTimeout(this.updateFeeds.bind(this), 100);
169 }
170 }
171 };
172