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