Home | History | Annotate | Download | only in extensions
      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