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/extensions/extension.h" 9 #include "chrome/common/url_constants.h" 10 #include "chrome/test/base/ui_test_utils.h" 11 #include "content/public/browser/web_contents.h" 12 #include "content/public/test/browser_test_utils.h" 13 #include "extensions/common/constants.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 content::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(WebContents* tab, 80 const std::string& frame_xpath, 81 const std::string& javascript, 82 const std::string& expected_value) { 83 std::string returned_value; 84 std::string error; 85 86 if (!content::ExecuteScriptInFrameAndExtractString(tab, frame_xpath, 87 javascript, 88 &returned_value)) 89 return false; 90 91 EXPECT_STREQ(expected_value.c_str(), returned_value.c_str()); 92 return expected_value == returned_value; 93 } 94 95 // Navigates to a feed page and, if |sniff_xml_type| is set, wait for the 96 // extension to kick in, detect the feed and redirect to a feed preview page. 97 // |sniff_xml_type| is generally set to true if the feed is sniffable and false 98 // for invalid feeds. 99 void NavigateToFeedAndValidate(net::SpawnedTestServer* server, 100 const std::string& url, 101 Browser* browser, 102 std::string extension_id, 103 bool sniff_xml_type, 104 const std::string& expected_feed_title, 105 const std::string& expected_item_title, 106 const std::string& expected_item_desc, 107 const std::string& expected_error) { 108 if (sniff_xml_type) { 109 // TODO(finnur): Implement this is a non-flaky way. 110 } 111 112 // Navigate to the subscribe page directly. 113 ui_test_utils::NavigateToURL(browser, 114 GetFeedUrl(server, url, true, extension_id)); 115 116 WebContents* tab = browser->tab_strip_model()->GetActiveWebContents(); 117 ASSERT_TRUE(ValidatePageElement( 118 tab, std::string(), kScriptFeedTitle, expected_feed_title)); 119 ASSERT_TRUE(ValidatePageElement(tab, 120 "//html/body/div/iframe[1]", 121 kScriptAnchor, 122 expected_item_title)); 123 ASSERT_TRUE(ValidatePageElement(tab, 124 "//html/body/div/iframe[1]", 125 kScriptDesc, 126 expected_item_desc)); 127 ASSERT_TRUE(ValidatePageElement(tab, 128 "//html/body/div/iframe[1]", 129 kScriptError, 130 expected_error)); 131 } 132 133 } // namespace 134 135 // Makes sure that the RSS detects RSS feed links, even when rel tag contains 136 // more than just "alternate". 137 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSMultiRelLink) { 138 ASSERT_TRUE(test_server()->Start()); 139 140 ASSERT_TRUE(LoadExtension( 141 test_data_dir_.AppendASCII("subscribe_page_action"))); 142 143 ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0)); 144 145 // Navigate to the feed page. 146 GURL feed_url = test_server()->GetURL(kFeedPageMultiRel); 147 ui_test_utils::NavigateToURL(browser(), feed_url); 148 // We should now have one page action ready to go in the LocationBar. 149 ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1)); 150 } 151 152 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed1) { 153 ASSERT_TRUE(test_server()->Start()); 154 155 const Extension* extension = LoadExtension( 156 test_data_dir_.AppendASCII("subscribe_page_action")); 157 ASSERT_TRUE(extension); 158 std::string id = extension->id(); 159 160 NavigateToFeedAndValidate(test_server(), kValidFeed1, browser(), id, true, 161 "Feed for MyFeedTitle", 162 "Title 1", 163 "Desc", 164 "No error"); 165 } 166 167 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed2) { 168 ASSERT_TRUE(test_server()->Start()); 169 170 const Extension* extension = LoadExtension( 171 test_data_dir_.AppendASCII("subscribe_page_action")); 172 ASSERT_TRUE(extension); 173 std::string id = extension->id(); 174 175 NavigateToFeedAndValidate(test_server(), kValidFeed2, browser(), id, true, 176 "Feed for MyFeed2", 177 "My item title1", 178 "This is a summary.", 179 "No error"); 180 } 181 182 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed3) { 183 ASSERT_TRUE(test_server()->Start()); 184 185 const Extension* extension = LoadExtension( 186 test_data_dir_.AppendASCII("subscribe_page_action")); 187 ASSERT_TRUE(extension); 188 std::string id = extension->id(); 189 190 NavigateToFeedAndValidate(test_server(), kValidFeed3, browser(), id, true, 191 "Feed for Google Code buglist rss feed", 192 "My dear title", 193 "My dear content", 194 "No error"); 195 } 196 197 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed4) { 198 ASSERT_TRUE(test_server()->Start()); 199 200 const Extension* extension = LoadExtension( 201 test_data_dir_.AppendASCII("subscribe_page_action")); 202 ASSERT_TRUE(extension); 203 std::string id = extension->id(); 204 205 NavigateToFeedAndValidate(test_server(), kValidFeed4, browser(), id, true, 206 "Feed for Title chars <script> %23 stop", 207 "Title chars %23 stop", 208 "My dear content %23 stop", 209 "No error"); 210 } 211 212 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed0) { 213 ASSERT_TRUE(test_server()->Start()); 214 215 const Extension* extension = LoadExtension( 216 test_data_dir_.AppendASCII("subscribe_page_action")); 217 ASSERT_TRUE(extension); 218 std::string id = extension->id(); 219 220 // Try a feed with a link with an onclick handler (before r27440 this would 221 // trigger a NOTREACHED). 222 NavigateToFeedAndValidate(test_server(), kValidFeed0, browser(), id, true, 223 "Feed for MyFeedTitle", 224 "Title 1", 225 "Desc VIDEO", 226 "No error"); 227 } 228 229 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed5) { 230 ASSERT_TRUE(test_server()->Start()); 231 232 const Extension* extension = LoadExtension( 233 test_data_dir_.AppendASCII("subscribe_page_action")); 234 ASSERT_TRUE(extension); 235 std::string id = extension->id(); 236 237 // Feed with valid but mostly empty xml. 238 NavigateToFeedAndValidate(test_server(), kValidFeed5, browser(), id, true, 239 "Feed for Unknown feed name", 240 "element 'anchor_0' not found", 241 "element 'desc_0' not found", 242 "This feed contains no entries."); 243 } 244 245 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed6) { 246 ASSERT_TRUE(test_server()->Start()); 247 248 const Extension* extension = LoadExtension( 249 test_data_dir_.AppendASCII("subscribe_page_action")); 250 ASSERT_TRUE(extension); 251 std::string id = extension->id(); 252 253 // Feed that is technically invalid but still parseable. 254 NavigateToFeedAndValidate(test_server(), kValidFeed6, browser(), id, true, 255 "Feed for MyFeedTitle", 256 "Title 1", 257 "Desc", 258 "No error"); 259 } 260 261 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedInvalidFeed1) { 262 ASSERT_TRUE(test_server()->Start()); 263 264 const Extension* extension = LoadExtension( 265 test_data_dir_.AppendASCII("subscribe_page_action")); 266 ASSERT_TRUE(extension); 267 std::string id = extension->id(); 268 269 // Try an empty feed. 270 NavigateToFeedAndValidate(test_server(), kInvalidFeed1, browser(), id, false, 271 "Feed for Unknown feed name", 272 "element 'anchor_0' not found", 273 "element 'desc_0' not found", 274 "This feed contains no entries."); 275 } 276 277 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedInvalidFeed2) { 278 ASSERT_TRUE(test_server()->Start()); 279 280 const Extension* extension = LoadExtension( 281 test_data_dir_.AppendASCII("subscribe_page_action")); 282 ASSERT_TRUE(extension); 283 std::string id = extension->id(); 284 285 // Try a garbage feed. 286 NavigateToFeedAndValidate(test_server(), kInvalidFeed2, browser(), id, false, 287 "Feed for Unknown feed name", 288 "element 'anchor_0' not found", 289 "element 'desc_0' not found", 290 "This feed contains no entries."); 291 } 292 293 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedInvalidFeed3) { 294 ASSERT_TRUE(test_server()->Start()); 295 296 const Extension* extension = LoadExtension( 297 test_data_dir_.AppendASCII("subscribe_page_action")); 298 ASSERT_TRUE(extension); 299 std::string id = extension->id(); 300 301 // Try a feed that doesn't exist. 302 NavigateToFeedAndValidate(test_server(), "foo.xml", browser(), id, false, 303 "Feed for Unknown feed name", 304 "element 'anchor_0' not found", 305 "element 'desc_0' not found", 306 "This feed contains no entries."); 307 } 308 309 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedInvalidFeed4) { 310 ASSERT_TRUE(test_server()->Start()); 311 312 const Extension* extension = LoadExtension( 313 test_data_dir_.AppendASCII("subscribe_page_action")); 314 ASSERT_TRUE(extension); 315 std::string id = extension->id(); 316 317 // subscribe.js shouldn't double-decode the URL passed in. Otherwise feed 318 // links such as http://search.twitter.com/search.atom?lang=en&q=%23chrome 319 // will result in no feed being downloaded because %23 gets decoded to # and 320 // therefore #chrome is not treated as part of the Twitter query. This test 321 // uses an underscore instead of a hash, but the principle is the same. If 322 // we start erroneously double decoding again, the path (and the feed) will 323 // become valid resulting in a failure for this test. 324 NavigateToFeedAndValidate( 325 test_server(), kFeedTripleEncoded, browser(), id, true, 326 "Feed for Unknown feed name", 327 "element 'anchor_0' not found", 328 "element 'desc_0' not found", 329 "This feed contains no entries."); 330 } 331 332 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeedNoLinks) { 333 ASSERT_TRUE(test_server()->Start()); 334 335 const Extension* extension = LoadExtension( 336 test_data_dir_.AppendASCII("subscribe_page_action")); 337 ASSERT_TRUE(extension); 338 std::string id = extension->id(); 339 340 // Valid feed but containing no links. 341 NavigateToFeedAndValidate( 342 test_server(), kValidFeedNoLinks, browser(), id, true, 343 "Feed for MyFeedTitle", 344 "Title with no link", 345 "Desc", 346 "No error"); 347 } 348