1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // Grab the querystring, removing question mark at the front and splitting on
7 var queryString
= location
.search
.substring(1).split("&");
9 // The feed URL is the first component and always present.
10 var feedUrl
= decodeURIComponent(queryString
[0]);
12 // This extension's ID.
13 var extension_id
= chrome
.i18n
.getMessage("@@extension_id");
15 // During testing, we cannot load the iframe and stylesheet from an external
16 // source, so we allow loading them synchronously and stuff the frame with the
17 // results. Since this is only allowed during testing, it isn't supported for
18 // the official extension ID.
19 var synchronousRequest
=
20 extension_id
!= "nlbjncdgjeocebhnmkbbbdekmmmcbfjd" ?
21 queryString
[1] == "synchronous" : false;
23 // The XMLHttpRequest object that tries to load and parse the feed, and (if
24 // testing) also the style sheet and the frame js.
27 // Depending on whether this is run from a test or from the extension, this
28 // will either be a link to the css file within the extension or contain the
29 // contents of the style sheet, fetched through XmlHttpRequest.
32 // Depending on whether this is run from a test or from the extension, this
33 // will either be a link to the js file within the extension or contain the
34 // contents of the style sheet, fetched through XmlHttpRequest.
37 // What to show when we cannot parse the feed name.
38 var unknownName
= chrome
.i18n
.getMessage("rss_subscription_unknown_feed_name");
40 // A list of feed readers, populated by localStorage if available, otherwise
44 // The token to use during communications with the iframe.
47 // Navigates to the reader of the user's choice (for subscribing to the feed).
49 var select
= document
.getElementById('readerDropdown');
51 feedReaderList
[select
.selectedIndex
].url
.replace(
52 "%s", encodeURIComponent(feedUrl
));
54 // Before we navigate, see if we want to skip this step in the future...
56 // See if the user wants to always use this reader.
57 var alwaysUse
= document
.getElementById('alwaysUse');
58 if (alwaysUse
.checked
) {
59 window
.localStorage
.defaultReader
=
60 feedReaderList
[select
.selectedIndex
].url
;
61 window
.localStorage
.showPreviewPage
= "No";
65 document
.location
= url
;
69 * The main function. Sets up the selection list for possible readers and
73 if (storageEnabled
&& window
.localStorage
.readerList
)
74 feedReaderList
= JSON
.parse(window
.localStorage
.readerList
);
76 feedReaderList
= defaultReaderList();
78 // Populate the list of readers.
79 var readerDropdown
= document
.getElementById('readerDropdown');
80 for (i
= 0; i
< feedReaderList
.length
; ++i
) {
81 readerDropdown
.options
[i
] = new Option(feedReaderList
[i
].description
, i
);
82 if (storageEnabled
&& isDefaultReader(feedReaderList
[i
].url
))
83 readerDropdown
.selectedIndex
= i
;
87 // Add the "Manage..." entry to the dropdown and show the checkbox asking
88 // if we always want to use this reader in the future (skip the preview).
89 readerDropdown
.options
[i
] =
90 new Option(chrome
.i18n
.getMessage("rss_subscription_manage_label"), "");
91 document
.getElementById('alwaysUseSpan').style
.display
= "block";
95 var tokenArray
= new Uint32Array(4);
96 crypto
.getRandomValues(tokenArray
);
97 token
= [].join
.call(tokenArray
);
99 // Now fetch the data.
100 req
= new XMLHttpRequest();
101 if (synchronousRequest
) {
102 // Tests that load the html page directly through a file:// url don't have
103 // access to the js and css from the frame so we must load them first and
104 // inject them into the src for the iframe.
105 req
.open("GET", "style.css", false);
108 styleSheet
= "<style>" + req
.responseText
+ "</style>";
110 req
.open("GET", "iframe.js", false);
113 if (req
.responseText
.indexOf('//') != -1) {
114 console
.log('Error: Single-line comment(s) found in iframe.js');
116 frameScript
= "<script>" +
121 // Normal loading just requires links to the css and the js file.
122 styleSheet
= "<link rel='stylesheet' type='text/css' href='" +
123 chrome
.extension
.getURL("style.css") + "'>";
124 frameScript
= "<script src='" + chrome
.extension
.getURL("iframe.js") +
128 req
.onload
= handleResponse
;
129 req
.onerror
= handleError
;
130 req
.open("GET", feedUrl
, !synchronousRequest
);
131 // Not everyone sets the mime type correctly, which causes handleResponse
132 // to fail to XML parse the response text from the server. By forcing
133 // it to text/xml we avoid this.
134 req
.overrideMimeType('text/xml');
137 document
.getElementById('feedUrl').href
= 'view-source:' + feedUrl
;
140 // Sets the title for the feed.
141 function setFeedTitle(title
) {
142 var titleTag
= document
.getElementById('title');
143 titleTag
.textContent
=
144 chrome
.i18n
.getMessage("rss_subscription_feed_for", title
);
147 // Handles errors during the XMLHttpRequest.
148 function handleError() {
149 handleFeedParsingFailed(
150 chrome
.i18n
.getMessage("rss_subscription_error_fetching"));
153 // Handles feed parsing errors.
154 function handleFeedParsingFailed(error
) {
155 setFeedTitle(unknownName
);
157 // The tests always expect an IFRAME, so add one showing the error.
158 var html
= "<body><span id=\"error\" class=\"item_desc\">" + error
+
161 var error_frame
= createFrame('error', html
);
162 var itemsTag
= document
.getElementById('items');
163 itemsTag
.appendChild(error_frame
);
166 function createFrame(frame_id
, html
) {
167 // During testing, we stuff the iframe with the script directly, so we relax
168 // the policy on running scripts under that scenario.
169 var csp
= synchronousRequest
?
170 '<meta http-equiv="content-security-policy" ' +
171 'content="object-src \'none\'">' :
172 '<meta http-equiv="content-security-policy" ' +
173 'content="object-src \'none\'; script-src \'self\'">';
174 frame
= document
.createElement('iframe');
176 frame
.src
= "data:text/html;charset=utf-8,<html>" + csp
+
177 "<!--Token:" + extension_id
+ token
+
178 "-->" + html
+ "</html>";
179 frame
.scrolling
= "auto";
180 frame
.frameBorder
= "0";
181 frame
.marginWidth
= "0";
185 // Handles parsing the feed data we got back from XMLHttpRequest.
186 function handleResponse() {
187 // Uncomment these three lines to see what the feed data looks like.
188 // var itemsTag = document.getElementById('items');
189 // itemsTag.textContent = req.responseText;
192 var doc
= req
.responseXML
;
194 // If the XMLHttpRequest object fails to parse the feed we make an attempt
195 // ourselves, because sometimes feeds have html/script code appended below a
196 // valid feed, which makes the feed invalid as a whole even though it is
198 var domParser
= new DOMParser();
199 doc
= domParser
.parseFromString(req
.responseText
, "text/xml");
201 handleFeedParsingFailed(
202 chrome
.i18n
.getMessage("rss_subscription_not_valid_feed"));
207 // We must find at least one 'entry' or 'item' element before proceeding.
208 var entries
= doc
.getElementsByTagName('entry');
209 if (entries
.length
== 0)
210 entries
= doc
.getElementsByTagName('item');
211 if (entries
.length
== 0) {
212 handleFeedParsingFailed(
213 chrome
.i18n
.getMessage("rss_subscription_no_entries"))
217 // Figure out what the title of the whole feed is.
218 var title
= doc
.getElementsByTagName('title')[0];
220 setFeedTitle(title
.textContent
);
222 setFeedTitle(unknownName
);
225 var itemsTag
= document
.getElementById('items');
226 // TODO(aa): Add base URL tag
227 iframe
= createFrame('rss', styleSheet
+ frameScript
);
228 itemsTag
.appendChild(iframe
);
232 * Handler for when selection changes.
234 function onSelectChanged() {
237 var readerDropdown
= document
.getElementById('readerDropdown');
239 // If the last item (Manage...) was selected we show the options.
240 var oldSelection
= readerDropdown
.selectedIndex
;
241 if (readerDropdown
.selectedIndex
== readerDropdown
.length
- 1)
242 window
.location
= "options.html";
245 document
.addEventListener('DOMContentLoaded', function () {
247 chrome
.i18n
.getMessage("rss_subscription_default_title");
248 i18nReplace('rss_subscription_subscribe_using');
249 i18nReplace('rss_subscription_subscribe_button');
250 i18nReplace('rss_subscription_always_use');
251 i18nReplace('rss_subscription_feed_preview');
252 i18nReplaceImpl('feedUrl', 'rss_subscription_feed_link', '');
254 var dropdown
= document
.getElementById('readerDropdown');
255 dropdown
.addEventListener('change', onSelectChanged
);
256 var button
= document
.getElementById('rss_subscription_subscribe_button');
257 button
.addEventListener('click', navigate
);
262 window
.addEventListener("message", function(e
) {
263 if (e
.ports
[0] && e
.data
=== token
)
264 e
.ports
[0].postMessage(req
.responseText
);