diff options
author | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-02 21:09:14 +0000 |
---|---|---|
committer | finnur@chromium.org <finnur@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-10-02 21:09:14 +0000 |
commit | 18bc07889e87c01d9cebf2d35400fafc02d71732 (patch) | |
tree | 094aef02c6e2b25ba738f7a7f97b9b21c215e0b7 | |
parent | 0691bdf48f7ae1af0f5490da1fab0b644068e0f1 (diff) | |
download | chromium_src-18bc07889e87c01d9cebf2d35400fafc02d71732.zip chromium_src-18bc07889e87c01d9cebf2d35400fafc02d71732.tar.gz chromium_src-18bc07889e87c01d9cebf2d35400fafc02d71732.tar.bz2 |
We now postMessage the RSS file over to the js script and have it parse it there in the context of the iframe instead of doing this in the extension html page.
BUG=None
TEST=Covered by automated tests.
Review URL: http://codereview.chromium.org/246066
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27898 0039d316-1c4b-4281-b951-d872f2087c98
6 files changed, 264 insertions, 153 deletions
diff --git a/chrome/browser/extensions/extension_browsertests_misc.cc b/chrome/browser/extensions/extension_browsertests_misc.cc index 5d4ecdc..91df2ef 100644 --- a/chrome/browser/extensions/extension_browsertests_misc.cc +++ b/chrome/browser/extensions/extension_browsertests_misc.cc @@ -30,6 +30,17 @@ // ID assigned to the first unpacked extension loaded by LoadExtension(). #define kDefaultExtensionID "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" +const std::wstring kSubscribePage = + L"files/extensions/samples/subscribe_page_action/subscribe.html"; +const std::wstring kValidFeed0 = L"files/feeds/feed_script.xml"; +const std::wstring kValidFeed1 = L"files/feeds/feed1.xml"; +const std::wstring kValidFeed2 = L"files/feeds/feed2.xml"; +const std::wstring kValidFeed3 = L"files/feeds/feed3.xml"; +const std::wstring kValidFeed4 = L"files/feeds/feed4.xml"; +const std::wstring kValidFeed5 = L"files/feeds/feed5.xml"; +const std::wstring kInvalidFeed1 = L"files/feeds/feed_invalid1.xml"; +const std::wstring kInvalidFeed2 = L"files/feeds/feed_invalid2.xml"; + // Looks for an ExtensionHost whose URL has the given path component (including // leading slash). Also verifies that the expected number of hosts are loaded. static ExtensionHost* FindHostWithPath(ExtensionProcessManager* manager, @@ -246,24 +257,11 @@ IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, UnloadPageAction) { } #endif // defined(OS_WIN) || defined(OS_LINUX) -GURL GetFeedUrl(const std::string& feed_page) { - FilePath test_dir; - PathService::Get(chrome::DIR_TEST_DATA, &test_dir); - - FilePath subscribe; - subscribe = test_dir.AppendASCII("extensions") - .AppendASCII("samples") - .AppendASCII("subscribe_page_action") - .AppendASCII("subscribe.html"); - subscribe = subscribe.StripTrailingSeparators(); - - FilePath feed_dir = test_dir.AppendASCII("feeds") - .AppendASCII(feed_page.c_str()); - - return GURL(net::FilePathToFileURL(subscribe).spec() + - std::string("?") + - net::FilePathToFileURL(feed_dir).spec() + - "&synchronous"); // synchronous XHR for easier testing. +GURL GetFeedUrl(HTTPTestServer* server, const std::wstring& feed_page) { + static GURL base_url = server->TestServerPageW(kSubscribePage); + GURL feed_url = server->TestServerPageW(feed_page); + return GURL(base_url.spec() + std::string("?") + feed_url.spec() + + std::string("&synchronous")); } static const wchar_t* jscript_feed_title = @@ -314,64 +312,82 @@ void GetParsedFeedData(Browser* browser, std::string* feed_title, // Tests that we can parse feeds. IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeed) { + HTTPTestServer* server = StartHTTPServer(); + std::string feed_title; std::string item_title; std::string item_desc; std::string error; - ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed1.xml")); + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, kValidFeed1)); GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); EXPECT_STREQ("Feed for 'MyFeedTitle'", feed_title.c_str()); EXPECT_STREQ("Title 1", item_title.c_str()); EXPECT_STREQ("Desc", item_desc.c_str()); EXPECT_STREQ("No error", error.c_str()); - ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed2.xml")); + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, kValidFeed2)); GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); EXPECT_STREQ("Feed for 'MyFeed2'", feed_title.c_str()); EXPECT_STREQ("My item title1", item_title.c_str()); EXPECT_STREQ("This is a summary.", item_desc.c_str()); EXPECT_STREQ("No error", error.c_str()); - ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed3.xml")); + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, kValidFeed3)); GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); EXPECT_STREQ("Feed for 'Google Code buglist rss feed'", feed_title.c_str()); EXPECT_STREQ("My dear title", item_title.c_str()); EXPECT_STREQ("My dear content", item_desc.c_str()); EXPECT_STREQ("No error", error.c_str()); + // Feed with weird characters in title. + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, kValidFeed4)); + GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); + EXPECT_STREQ("Feed for 'Title chars <script> %23 stop'", feed_title.c_str()); + EXPECT_STREQ("Title chars <script> %23 stop", item_title.c_str()); + EXPECT_STREQ("My dear content", item_desc.c_str()); + EXPECT_STREQ("No error", error.c_str()); + // Try a feed that doesn't exist. - ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed_nonexistant.xml")); + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, L"foo.xml")); GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); EXPECT_STREQ("Feed for 'Unknown feed name'", feed_title.c_str()); EXPECT_STREQ("element 'anchor_0' not found", item_title.c_str()); EXPECT_STREQ("element 'desc_0' not found", item_desc.c_str()); - EXPECT_STREQ("Not a valid feed", error.c_str()); + EXPECT_STREQ("Not a valid feed.", error.c_str()); // Try an empty feed. - ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed_invalid1.xml")); + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, kInvalidFeed1)); GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); EXPECT_STREQ("Feed for 'Unknown feed name'", feed_title.c_str()); EXPECT_STREQ("element 'anchor_0' not found", item_title.c_str()); EXPECT_STREQ("element 'desc_0' not found", item_desc.c_str()); - EXPECT_STREQ("Not a valid feed", error.c_str()); + EXPECT_STREQ("Not a valid feed.", error.c_str()); // Try a garbage feed. - ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed_invalid2.xml")); + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, kInvalidFeed2)); GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); EXPECT_STREQ("Feed for 'Unknown feed name'", feed_title.c_str()); EXPECT_STREQ("element 'anchor_0' not found", item_title.c_str()); EXPECT_STREQ("element 'desc_0' not found", item_desc.c_str()); - EXPECT_STREQ("Not a valid feed", error.c_str()); + EXPECT_STREQ("Not a valid feed.", error.c_str()); // Try a feed with a link with an onclick handler (before r27440 this would // trigger a NOTREACHED). - ui_test_utils::NavigateToURL(browser(), GetFeedUrl("feed_script.xml")); + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, kValidFeed0)); GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); EXPECT_STREQ("Feed for 'MyFeedTitle'", feed_title.c_str()); EXPECT_STREQ("Title 1", item_title.c_str()); EXPECT_STREQ("Desc VIDEO", item_desc.c_str()); EXPECT_STREQ("No error", error.c_str()); + + // Feed with valid but mostly empty xml. + ui_test_utils::NavigateToURL(browser(), GetFeedUrl(server, kValidFeed5)); + GetParsedFeedData(browser(), &feed_title, &item_title, &item_desc, &error); + EXPECT_STREQ("Feed for 'Unknown feed name'", feed_title.c_str()); + EXPECT_STREQ("element 'anchor_0' not found", item_title.c_str()); + EXPECT_STREQ("element 'desc_0' not found", item_desc.c_str()); + EXPECT_STREQ("This feed contains no entries.", error.c_str()); } #if defined(OS_WIN) // TODO(port) - enable. diff --git a/chrome/test/data/extensions/samples/subscribe_page_action/iframe.js b/chrome/test/data/extensions/samples/subscribe_page_action/iframe.js new file mode 100644 index 0000000..475ce52 --- /dev/null +++ b/chrome/test/data/extensions/samples/subscribe_page_action/iframe.js @@ -0,0 +1,98 @@ +/* Use only multi-line comments in this file, since during testing + its contents will get read from disk and stuffed into the + iframe .src tag, which is a process that doesn't preserve line + breaks and makes single-line comment out the rest of the code. +*/ + +/* The maximum number of feed items to show in the preview. */ +var maxFeedItems = 10; + +/* The maximum number of characters to show in the feed item title. */ +var maxTitleCount = 64; + +window.addEventListener("message", function(e) { + var parser = new DOMParser(); + var doc = parser.parseFromString(e.data, "text/xml"); + + if (doc) { + buildPreview(doc); + } else { + /* Already handled in subscribe.html */ + } +}, false); + +function buildPreview(doc) { + /* Start building the part we render inside an IFRAME. We use a table to + ensure that items are separated vertically from each other. */ + var table = document.createElement("table"); + var tbody = document.createElement("tbody"); + table.appendChild(tbody); + + /* Now parse the rest. Some use <entry> for each feed item, others use + <channel><item>. */ + var entries = doc.getElementsByTagName('entry'); + if (entries.length == 0) + entries = doc.getElementsByTagName('item'); + + for (i = 0; i < entries.length && i < maxFeedItems; ++i) { + item = entries.item(i); + + /* Grab the title for the feed item. */ + var itemTitle = item.getElementsByTagName('title')[0]; + if (itemTitle) + itemTitle = itemTitle.textContent; + else + itemTitle = "Unknown title"; + + /* Ensure max length for title. */ + if (itemTitle.length > maxTitleCount) + itemTitle = itemTitle.substring(0, maxTitleCount) + "..."; + + /* Grab the description. + TODO(aa): Do we need to check for type=html here? */ + var itemDesc = item.getElementsByTagName('description')[0]; + if (!itemDesc) + itemDesc = item.getElementsByTagName('summary')[0]; + if (!itemDesc) + itemDesc = item.getElementsByTagName('content')[0]; + + if (itemDesc) + itemDesc = itemDesc.textContent; + else + itemDesc = ""; + + /* Grab the link URL. */ + var itemLink = item.getElementsByTagName('link'); + var link = itemLink[0].childNodes[0]; + if (link) + link = itemLink[0].childNodes[0].nodeValue; + else + link = itemLink[0].getAttribute('href'); + + var tr = document.createElement("tr"); + var td = document.createElement("td"); + + var anchor = document.createElement("a"); + anchor.id = "anchor_" + String(i); + anchor.href = link; + anchor.appendChild(document.createTextNode(itemTitle)); + anchor.className = "item_title"; + + var span = document.createElement("span"); + span.id = "desc_" + String(i); + span.className = "item_desc"; + span.innerHTML = itemDesc; + + td.appendChild(anchor); + td.appendChild(document.createElement("br")); + td.appendChild(span); + td.appendChild(document.createElement("br")); + td.appendChild(document.createElement("br")); + + tr.appendChild(td); + tbody.appendChild(tr); + } + + table.appendChild(tbody); + document.body.appendChild(table); +} diff --git a/chrome/test/data/extensions/samples/subscribe_page_action/style.css b/chrome/test/data/extensions/samples/subscribe_page_action/style.css index 036a334..f5ef149 100644 --- a/chrome/test/data/extensions/samples/subscribe_page_action/style.css +++ b/chrome/test/data/extensions/samples/subscribe_page_action/style.css @@ -1,3 +1,6 @@ +/** + * Styles that are shared between the iframe and the parent document. + */ body { font-family: arial, sans-serif; font-size: 11px; @@ -11,13 +14,6 @@ a:hover { font-weight:bold; text-decoration: underline; } -.splitter { - padding: 2px 8px 2px 5px; - border-top: solid 1px #9CC2EF; - background: #EBEFF9; - font-size: 13px; - font-weight:bold; -} .item_title { font-size: 12px; } diff --git a/chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html b/chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html index 195bb75..0863170 100644 --- a/chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html +++ b/chrome/test/data/extensions/samples/subscribe_page_action/subscribe.html @@ -14,21 +14,22 @@ // if true. var synchronousRequest = queryString[1] == "synchronous"; - // The XMLHttpRequest object that tries to load and parse the feed. + // The XMLHttpRequest object that tries to load and parse the feed, and (if + // testing) also the style sheet and the frame js. var req; - var reqStylesheet; - // What to show when we cannot parse the feed name. - var unknownName = "Unknown feed name"; - - // The maximum number of feed items to show in the preview. - var maxFeedItems = 10; + // Depending on whether this is run from a test or from the extension, this + // will either be a link to the css file within the extension or contain the + // contents of the style sheet, fetched through XmlHttpRequest. + var styleSheet = ""; - // The maximum number of characters to show in the feed item title. - var maxTitleCount = 64; + // Depending on whether this is run from a test or from the extension, this + // will either be a link to the js file within the extension or contain the + // contents of the style sheet, fetched through XmlHttpRequest. + var frameScript = ""; - // The style sheet we use for both the subscribe page and the IFRAME. - var style_sheet = ""; // Populated later using a XMLHttpRequest. + // What to show when we cannot parse the feed name. + var unknownName = "Unknown feed name"; // Navigates to the reader of the user's choice (for subscribing to the feed). function navigate() { @@ -37,24 +38,31 @@ document.location = url; } - function loadStylesheet() { - reqStylesheet = new XMLHttpRequest(); - reqStylesheet.onload = handleCssLoaded; - reqStylesheet.onerror = loadFeed; // I ain't got no style, lets do without. - reqStylesheet.open("GET", "style.css", true); - reqStylesheet.send(null); - } - - function handleCssLoaded() { - style_sheet = reqStylesheet.responseText; - loadFeed(); - } + function main() { + req = new XMLHttpRequest(); + if (synchronousRequest) { + // Tests that load the html page directly through a file:// url don't have + // access to the js and css from the frame so we must load them first and + // inject them into the src for the iframe. + req.open("GET", "style.css", false); + req.send(null); + + styleSheet = "<style>" + req.responseText + "</style>"; + + req.open("GET", "iframe.js", false); + req.send(null); + + frameScript = "<script>" + req.responseText + + "<" + "/script>"; + } else { + // Normal loading just requires links to the css and the js file. + styleSheet = "<link rel='stylesheet' type='text/css' href='" + + chrome.extension.getURL("style.css") + "'>"; + frameScript = "<script src='" + chrome.extension.getURL("iframe.js") + + "'></" + "script>"; + } - // Parses the feed specified. We will either end up in handleResponse or - // handleError from here. - function loadFeed() { feedUrl = decodeURIComponent(feedUrl); - req = new XMLHttpRequest(); req.onload = handleResponse; req.onerror = handleError; req.open("GET", feedUrl, !synchronousRequest); @@ -76,40 +84,57 @@ function handleFeedParsingFailed(error) { setFeedTitle(unknownName); - // The tests always expect an IFRAME, so add an one showing the error. - var html = "<html><body><span id=\"error\" class=\"item_desc\">" + error + - "</span></html>"; + // The tests always expect an IFRAME, so add one showing the error. + var html = "<body><span id=\"error\" class=\"item_desc\">" + error + + "</span></body>"; + + var error_frame = createFrame('error', html); + var itemsTag = document.getElementById('items'); + itemsTag.appendChild(error_frame); + } - embedAsIframe(html); + function createFrame(frame_id, html) { + frame = document.createElement('iframe'); + frame.id = frame_id; + frame.src = "data:text/html;charset=utf-8,<html>" + styleSheet + html + + "</html>"; + frame.scrolling = "auto"; + frame.frameBorder = "0"; + frame.marginWidth = "0"; + return frame; } - function embedAsIframe(html) { + function embedAsIframe(rssText) { var itemsTag = document.getElementById('items'); - html = html.replace(/"/g, "%22"); - - iframe = document.createElement('iframe'); - iframe.src = "data:text/html;charset=utf-8," + html; - iframe.id = "rss"; - iframe.height = "100%"; - iframe.width = "100%"; - iframe.scrolling = "auto"; - iframe.frameBorder = "0"; - iframe.marginWidth = "0"; - iframe.noResize = "noresize"; + + // TODO(aa): Add base URL tag + iframe = createFrame('rss', styleSheet + frameScript); itemsTag.appendChild(iframe); + + iframe.onload = function() { + iframe.contentWindow.postMessage(rssText, "*"); + } } // Handles parsing the feed data we got back from XMLHttpRequest. function handleResponse() { - var itemsTag = document.getElementById('items'); - - // Uncomment these two lines to see what the feed data looks like. + // Uncomment these three lines to see what the feed data looks like. + // var itemsTag = document.getElementById('items'); // itemsTag.textContent = req.responseText; // return; var doc = req.responseXML; if (!doc) { - handleFeedParsingFailed("Not a valid feed"); + handleFeedParsingFailed("Not a valid feed."); + return; + } + + // We must find at least one 'entry' or 'item' element before proceeding. + var entries = doc.getElementsByTagName('entry'); + if (entries.length == 0) + entries = doc.getElementsByTagName('item'); + if (entries.length == 0) { + handleFeedParsingFailed("This feed contains no entries.") return; } @@ -120,82 +145,39 @@ else setFeedTitle(unknownName); - // Obtain the base url so that we can show images using relative path. - var base_url = ""; - var slash = feedUrl.lastIndexOf("/"); - if (slash > -1) - base_url = feedUrl.substring(0, slash + 1); - else - base_url = feedUrl; - base_url = "<base href=\"" + base_url + "\" />"; - - // Start building the part we render inside an IFRAME. We use a table to - // ensure that items are separated vertically from each other. - var items_html = "<html><head>" + base_url + - "<style>" + style_sheet + "</style>" + "</head>" + - "<body><table>"; - - // Now parse the rest. Some use <entry> for each feed item, others use - // <channel><item>. - var entries = doc.getElementsByTagName('entry'); - if (entries.length == 0) - entries = doc.getElementsByTagName('item'); - - for (i = 0; i < entries.length && i < maxFeedItems; ++i) { - item = entries.item(i); - - // Grab the title for the feed item. - var itemTitle = item.getElementsByTagName('title')[0]; - if (itemTitle) - itemTitle = itemTitle.textContent; - else - itemTitle = "Unknown title"; - - // Ensure max length for title. - if (itemTitle.length > maxTitleCount) - itemTitle = itemTitle.substring(0, maxTitleCount) + "..."; - - // Grab the description. - var itemDesc = item.getElementsByTagName('description')[0]; - if (!itemDesc) - itemDesc = item.getElementsByTagName('summary')[0]; - if (!itemDesc) - itemDesc = item.getElementsByTagName('content')[0]; - - if (itemDesc) - itemDesc = itemDesc.textContent; - else - itemDesc = ""; - - // Grab the link URL. - var itemLink = item.getElementsByTagName('link'); - var link = itemLink[0].childNodes[0]; - if (link) - link = itemLink[0].childNodes[0].nodeValue; - else - link = itemLink[0].getAttribute('href'); - - items_html = items_html + - "<tr><td>" + - "<a id=\"anchor_" + String(i) + "\" href=\"" + link + "\" " + - "class=\"item_title\">" + - itemTitle + "</a><br>"; - - items_html = items_html + "<span class=\"item_desc\" " + - "id=\"desc_" + String(i) + "\">" + - itemDesc + "<br><br>" + - "</td></tr>"; - } - items_html = items_html + "</table></body></html>"; - // Add an IFRAME with the html contents. - embedAsIframe(items_html); + embedAsIframe(req.responseText); } </script> - <link rel="stylesheet" href="style.css" type="text/css" /> +<link rel="stylesheet" href="style.css" type="text/css" /> +<style> +body { + display:-webkit-box; + -webkit-box-orient:vertical; +} +body>* { + display:-webkit-box; +} +#items { + -webkit-box-flex:1; + -webkit-box-orient:vertical; + -webkit-box-align:stretch; +} +iframe { + display:-webkit-box; + -webkit-box-flex:1; +} +.splitter { + padding: 2px 8px 2px 5px; + border-top: solid 1px #9CC2EF; + background: #EBEFF9; + font-size: 13px; + font-weight:bold; +} +</style> </head> -<body onload="loadStylesheet();"> +<body onload="main();"> <table> <tr> <td width="75"> diff --git a/chrome/test/data/feeds/feed4.xml b/chrome/test/data/feeds/feed4.xml new file mode 100644 index 0000000..fa200c1 --- /dev/null +++ b/chrome/test/data/feeds/feed4.xml @@ -0,0 +1,15 @@ +<?xml version="1.0"?>
+<feed>
+ <updated>2009-09-29T17:51:01Z</updated>
+ <title>Title chars <script> %23 stop</title>
+
+ <entry>
+ <updated>2009-09-29T17:51:01Z</updated>
+ <link rel="alternate" type="text/html" href="http://code.google.com/p/chromium/issues/detail?id=22699#c9" />
+ <title>Title chars <script> %23 stop</title>
+ <author>
+ <name>Some name</name>
+ </author>
+ <content type="html">My dear content<script> %23 stop</content>
+ </entry>
+</feed>
\ No newline at end of file diff --git a/chrome/test/data/feeds/feed5.xml b/chrome/test/data/feeds/feed5.xml new file mode 100644 index 0000000..f7c43db --- /dev/null +++ b/chrome/test/data/feeds/feed5.xml @@ -0,0 +1,4 @@ +<rss version="2.0">
+<channel>
+</channel>
+</rss>
\ No newline at end of file |