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);