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. 4 5 #include "chrome/browser/extensions/extension_browsertest.h" 6 #include "chrome/browser/ui/browser.h" 7 #include "chrome/browser/ui/tabs/tab_strip_model.h" 8 #include "chrome/common/url_constants.h" 9 #include "chrome/test/base/ui_test_utils.h" 10 #include "content/public/browser/web_contents.h" 11 #include "content/public/test/browser_test_utils.h" 12 #include "extensions/common/constants.h" 13 #include "extensions/common/extension.h" 14 15 using content::WebContents; 16 using extensions::Extension; 17 18 namespace { 19 20 const char kSubscribePage[] = "/subscribe.html"; 21 const char kFeedPageMultiRel[] = "files/feeds/feed_multi_rel.html"; 22 const char kValidFeedNoLinks[] = "files/feeds/feed_nolinks.xml"; 23 const char kValidFeed0[] = "files/feeds/feed_script.xml"; 24 const char kValidFeed1[] = "files/feeds/feed1.xml"; 25 const char kValidFeed2[] = "files/feeds/feed2.xml"; 26 const char kValidFeed3[] = "files/feeds/feed3.xml"; 27 const char kValidFeed4[] = "files/feeds/feed4.xml"; 28 const char kValidFeed5[] = "files/feeds/feed5.xml"; 29 const char kValidFeed6[] = "files/feeds/feed6.xml"; 30 const char kInvalidFeed1[] = "files/feeds/feed_invalid1.xml"; 31 const char kInvalidFeed2[] = "files/feeds/feed_invalid2.xml"; 32 // We need a triple encoded string to prove that we are not decoding twice in 33 // subscribe.js because one layer is also stripped off when subscribe.js passes 34 // it to the XMLHttpRequest object. 35 const char kFeedTripleEncoded[] = "files/feeds/url%25255Fdecoding.html"; 36 37 static const char kScriptFeedTitle[] = 38 "window.domAutomationController.send(" 39 " document.getElementById('title') ? " 40 " document.getElementById('title').textContent : " 41 " \"element 'title' not found\"" 42 ");"; 43 static const char kScriptAnchor[] = 44 "window.domAutomationController.send(" 45 " document.getElementById('anchor_0') ? " 46 " document.getElementById('anchor_0').textContent : " 47 " \"element 'anchor_0' not found\"" 48 ");"; 49 static const char kScriptDesc[] = 50 "window.domAutomationController.send(" 51 " document.getElementById('desc_0') ? " 52 " document.getElementById('desc_0').textContent : " 53 " \"element 'desc_0' not found\"" 54 ");"; 55 static const char kScriptError[] = 56 "window.domAutomationController.send(" 57 " document.getElementById('error') ? " 58 " document.getElementById('error').textContent : " 59 " \"No error\"" 60 ");"; 61 62 GURL GetFeedUrl(net::SpawnedTestServer* server, const std::string& feed_page, 63 bool direct_url, std::string extension_id) { 64 GURL feed_url = server->GetURL(feed_page); 65 if (direct_url) { 66 // We navigate directly to the subscribe page for feeds where the feed 67 // sniffing won't work, in other words, as is the case for malformed feeds. 68 return GURL(std::string(extensions::kExtensionScheme) + 69 url::kStandardSchemeSeparator + 70 extension_id + std::string(kSubscribePage) + std::string("?") + 71 feed_url.spec() + std::string("&synchronous")); 72 } else { 73 // Navigate to the feed content (which will cause the extension to try to 74 // sniff the type and display the subscribe page in another tab. 75 return GURL(feed_url.spec()); 76 } 77 } 78 79 bool ValidatePageElement(content::RenderFrameHost* frame, 80 const std::string& javascript, 81 const std::string& expected_value) { 82 std::string returned_value; 83 84 if (!content::ExecuteScriptAndExtractString(frame, 85 javascript, 86 &returned_value)) 87 return false; 88 89 EXPECT_STREQ(expected_value.c_str(), returned_value.c_str()); 90 return expected_value == returned_value; 91 } 92 93 // Navigates to a feed page and, if |sniff_xml_type| is set, wait for the 94 // extension to kick in, detect the feed and redirect to a feed preview page. 95 // |sniff_xml_type| is generally set to true if the feed is sniffable and false 96 // for invalid feeds. 97 void NavigateToFeedAndValidate(net::SpawnedTestServer* server, 98 const std::string& url, 99 Browser* browser, 100 std::string extension_id, 101 bool sniff_xml_type, 102 const std::string& expected_feed_title, 103 const std::string& expected_item_title, 104 const std::string& expected_item_desc, 105 const std::string& expected_error) { 106 if (sniff_xml_type) { 107 // TODO(finnur): Implement this is a non-flaky way. 108 } 109 110 // Navigate to the subscribe page directly. 111 ui_test_utils::NavigateToURL(browser, 112 GetFeedUrl(server, url, true, extension_id)); 113 114 WebContents* tab = browser->tab_strip_model()->GetActiveWebContents(); 115 content::RenderFrameHost* frame = content::FrameMatchingPredicate( 116 tab, base::Bind(&content::FrameIsChildOfMainFrame)); 117 ASSERT_TRUE(ValidatePageElement( 118 tab->GetMainFrame(), kScriptFeedTitle, expected_feed_title)); 119 ASSERT_TRUE(ValidatePageElement(frame, kScriptAnchor, expected_item_title)); 120 ASSERT_TRUE(ValidatePageElement(frame, kScriptDesc, expected_item_desc)); 121 ASSERT_TRUE(ValidatePageElement(frame, kScriptError, expected_error)); 122 } 123 124 } // namespace 125 126 // Makes sure that the RSS detects RSS feed links, even when rel tag contains 127 // more than just "alternate". 128 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSMultiRelLink) { 129 ASSERT_TRUE(test_server()->Start()); 130 131 ASSERT_TRUE(LoadExtension( 132 test_data_dir_.AppendASCII("subscribe_page_action"))); 133 134 ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0)); 135 136 // Navigate to the feed page. 137 GURL feed_url = test_server()->GetURL(kFeedPageMultiRel); 138 ui_test_utils::NavigateToURL(browser(), feed_url); 139 // We should now have one page action ready to go in the LocationBar. 140 ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); 141 } 142 143 // This test is flaky on all platforms; see http://crbug.com/340354 144 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, DISABLED_RSSParseFeedValidFeed1) { 145 ASSERT_TRUE(test_server()->Start()); 146 147 const Extension* extension = LoadExtension( 148 test_data_dir_.AppendASCII("subscribe_page_action")); 149 ASSERT_TRUE(extension); 150 std::string id = extension->id(); 151 152 NavigateToFeedAndValidate(test_server(), kValidFeed1, browser(), id, true, 153 "Feed for MyFeedTitle", 154 "Title 1", 155 "Desc", 156 "No error"); 157 } 158 159 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed2) { 160 ASSERT_TRUE(test_server()->Start()); 161 162 const Extension* extension = LoadExtension( 163 test_data_dir_.AppendASCII("subscribe_page_action")); 164 ASSERT_TRUE(extension); 165 std::string id = extension->id(); 166 167 NavigateToFeedAndValidate(test_server(), kValidFeed2, browser(), id, true, 168 "Feed for MyFeed2", 169 "My item title1", 170 "This is a summary.", 171 "No error"); 172 } 173 174 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed3) { 175 ASSERT_TRUE(test_server()->Start()); 176 177 const Extension* extension = LoadExtension( 178 test_data_dir_.AppendASCII("subscribe_page_action")); 179 ASSERT_TRUE(extension); 180 std::string id = extension->id(); 181 182 NavigateToFeedAndValidate(test_server(), kValidFeed3, browser(), id, true, 183 "Feed for Google Code buglist rss feed", 184 "My dear title", 185 "My dear content", 186 "No error"); 187 } 188 189 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed4) { 190 ASSERT_TRUE(test_server()->Start()); 191 192 const Extension* extension = LoadExtension( 193 test_data_dir_.AppendASCII("subscribe_page_action")); 194 ASSERT_TRUE(extension); 195 std::string id = extension->id(); 196 197 NavigateToFeedAndValidate(test_server(), kValidFeed4, browser(), id, true, 198 "Feed for Title chars <script> %23 stop", 199 "Title chars %23 stop", 200 "My dear content %23 stop", 201 "No error"); 202 } 203 204 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed0) { 205 ASSERT_TRUE(test_server()->Start()); 206 207 const Extension* extension = LoadExtension( 208 test_data_dir_.AppendASCII("subscribe_page_action")); 209 ASSERT_TRUE(extension); 210 std::string id = extension->id(); 211 212 // Try a feed with a link with an onclick handler (before r27440 this would 213 // trigger a NOTREACHED). 214 NavigateToFeedAndValidate(test_server(), kValidFeed0, browser(), id, true, 215 "Feed for MyFeedTitle", 216 "Title 1", 217 "Desc VIDEO", 218 "No error"); 219 } 220 221 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed5) { 222 ASSERT_TRUE(test_server()->Start()); 223 224 const Extension* extension = LoadExtension( 225 test_data_dir_.AppendASCII("subscribe_page_action")); 226 ASSERT_TRUE(extension); 227 std::string id = extension->id(); 228 229 // Feed with valid but mostly empty xml. 230 NavigateToFeedAndValidate(test_server(), kValidFeed5, browser(), id, true, 231 "Feed for Unknown feed name", 232 "element 'anchor_0' not found", 233 "element 'desc_0' not found", 234 "This feed contains no entries."); 235 } 236 237 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeed6) { 238 ASSERT_TRUE(test_server()->Start()); 239 240 const Extension* extension = LoadExtension( 241 test_data_dir_.AppendASCII("subscribe_page_action")); 242 ASSERT_TRUE(extension); 243 std::string id = extension->id(); 244 245 // Feed that is technically invalid but still parseable. 246 NavigateToFeedAndValidate(test_server(), kValidFeed6, browser(), id, true, 247 "Feed for MyFeedTitle", 248 "Title 1", 249 "Desc", 250 "No error"); 251 } 252 253 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedInvalidFeed1) { 254 ASSERT_TRUE(test_server()->Start()); 255 256 const Extension* extension = LoadExtension( 257 test_data_dir_.AppendASCII("subscribe_page_action")); 258 ASSERT_TRUE(extension); 259 std::string id = extension->id(); 260 261 // Try an empty feed. 262 NavigateToFeedAndValidate(test_server(), kInvalidFeed1, browser(), id, false, 263 "Feed for Unknown feed name", 264 "element 'anchor_0' not found", 265 "element 'desc_0' not found", 266 "This feed contains no entries."); 267 } 268 269 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedInvalidFeed2) { 270 ASSERT_TRUE(test_server()->Start()); 271 272 const Extension* extension = LoadExtension( 273 test_data_dir_.AppendASCII("subscribe_page_action")); 274 ASSERT_TRUE(extension); 275 std::string id = extension->id(); 276 277 // Try a garbage feed. 278 NavigateToFeedAndValidate(test_server(), kInvalidFeed2, browser(), id, false, 279 "Feed for Unknown feed name", 280 "element 'anchor_0' not found", 281 "element 'desc_0' not found", 282 "This feed contains no entries."); 283 } 284 285 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedInvalidFeed3) { 286 ASSERT_TRUE(test_server()->Start()); 287 288 const Extension* extension = LoadExtension( 289 test_data_dir_.AppendASCII("subscribe_page_action")); 290 ASSERT_TRUE(extension); 291 std::string id = extension->id(); 292 293 // Try a feed that doesn't exist. 294 NavigateToFeedAndValidate(test_server(), "foo.xml", browser(), id, false, 295 "Feed for Unknown feed name", 296 "element 'anchor_0' not found", 297 "element 'desc_0' not found", 298 "This feed contains no entries."); 299 } 300 301 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedInvalidFeed4) { 302 ASSERT_TRUE(test_server()->Start()); 303 304 const Extension* extension = LoadExtension( 305 test_data_dir_.AppendASCII("subscribe_page_action")); 306 ASSERT_TRUE(extension); 307 std::string id = extension->id(); 308 309 // subscribe.js shouldn't double-decode the URL passed in. Otherwise feed 310 // links such as http://search.twitter.com/search.atom?lang=en&q=%23chrome 311 // will result in no feed being downloaded because %23 gets decoded to # and 312 // therefore #chrome is not treated as part of the Twitter query. This test 313 // uses an underscore instead of a hash, but the principle is the same. If 314 // we start erroneously double decoding again, the path (and the feed) will 315 // become valid resulting in a failure for this test. 316 NavigateToFeedAndValidate( 317 test_server(), kFeedTripleEncoded, browser(), id, true, 318 "Feed for Unknown feed name", 319 "element 'anchor_0' not found", 320 "element 'desc_0' not found", 321 "This feed contains no entries."); 322 } 323 324 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSParseFeedValidFeedNoLinks) { 325 ASSERT_TRUE(test_server()->Start()); 326 327 const Extension* extension = LoadExtension( 328 test_data_dir_.AppendASCII("subscribe_page_action")); 329 ASSERT_TRUE(extension); 330 std::string id = extension->id(); 331 332 // Valid feed but containing no links. 333 NavigateToFeedAndValidate( 334 test_server(), kValidFeedNoLinks, browser(), id, true, 335 "Feed for MyFeedTitle", 336 "Title with no link", 337 "Desc", 338 "No error"); 339 } 340