1// Copyright (c) 2011 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 "base/file_util.h"
6#include "base/memory/ref_counted.h"
7#include "base/utf_string_conversions.h"
8#include "chrome/browser/extensions/autoupdate_interceptor.h"
9#include "chrome/browser/extensions/extension_apitest.h"
10#include "chrome/browser/extensions/extension_browsertest.h"
11#include "chrome/browser/extensions/extension_error_reporter.h"
12#include "chrome/browser/extensions/extension_host.h"
13#include "chrome/browser/extensions/extension_process_manager.h"
14#include "chrome/browser/extensions/extension_service.h"
15#include "chrome/browser/extensions/extension_tabs_module.h"
16#include "chrome/browser/extensions/extension_updater.h"
17#include "chrome/browser/profiles/profile.h"
18#include "chrome/browser/tabs/tab_strip_model.h"
19#include "chrome/browser/ui/browser.h"
20#include "chrome/browser/ui/browser_list.h"
21#include "chrome/browser/ui/tab_contents/tab_contents_wrapper.h"
22#include "chrome/common/chrome_paths.h"
23#include "chrome/common/extensions/extension_action.h"
24#include "chrome/common/url_constants.h"
25#include "chrome/test/ui_test_utils.h"
26#include "content/browser/renderer_host/render_view_host.h"
27#include "content/browser/site_instance.h"
28#include "content/browser/tab_contents/tab_contents.h"
29#include "net/base/net_util.h"
30#include "net/test/test_server.h"
31
32#if defined(TOOLKIT_VIEWS)
33#include "chrome/browser/ui/views/frame/browser_view.h"
34#endif
35
36const std::string kSubscribePage = "/subscribe.html";
37const std::string kFeedPage = "files/feeds/feed.html";
38const std::string kFeedPageMultiRel = "files/feeds/feed_multi_rel.html";
39const std::string kNoFeedPage = "files/feeds/no_feed.html";
40const std::string kValidFeed0 = "files/feeds/feed_script.xml";
41const std::string kValidFeed1 = "files/feeds/feed1.xml";
42const std::string kValidFeed2 = "files/feeds/feed2.xml";
43const std::string kValidFeed3 = "files/feeds/feed3.xml";
44const std::string kValidFeed4 = "files/feeds/feed4.xml";
45const std::string kValidFeed5 = "files/feeds/feed5.xml";
46const std::string kValidFeed6 = "files/feeds/feed6.xml";
47const std::string kValidFeedNoLinks = "files/feeds/feed_nolinks.xml";
48const std::string kInvalidFeed1 = "files/feeds/feed_invalid1.xml";
49const std::string kInvalidFeed2 = "files/feeds/feed_invalid2.xml";
50const std::string kLocalization =
51    "files/extensions/browsertest/title_localized_pa/simple.html";
52// We need a triple encoded string to prove that we are not decoding twice in
53// subscribe.js because one layer is also stripped off when subscribe.js passes
54// it to the XMLHttpRequest object.
55const std::string kFeedTripleEncoded = "files/feeds/url%25255Fdecoding.html";
56const std::string kHashPageA =
57    "files/extensions/api_test/page_action/hash_change/test_page_A.html";
58const std::string kHashPageAHash = kHashPageA + "#asdf";
59const std::string kHashPageB =
60    "files/extensions/api_test/page_action/hash_change/test_page_B.html";
61
62// Looks for an ExtensionHost whose URL has the given path component (including
63// leading slash).  Also verifies that the expected number of hosts are loaded.
64static ExtensionHost* FindHostWithPath(ExtensionProcessManager* manager,
65                                       const std::string& path,
66                                       int expected_hosts) {
67  ExtensionHost* host = NULL;
68  int num_hosts = 0;
69  for (ExtensionProcessManager::const_iterator iter = manager->begin();
70       iter != manager->end(); ++iter) {
71    if ((*iter)->GetURL().path() == path) {
72      EXPECT_FALSE(host);
73      host = *iter;
74    }
75    num_hosts++;
76  }
77  EXPECT_EQ(expected_hosts, num_hosts);
78  return host;
79}
80
81// Tests that we can load extension pages into the tab area and they can call
82// extension APIs.
83IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TabContents) {
84  ASSERT_TRUE(LoadExtension(
85      test_data_dir_.AppendASCII("good").AppendASCII("Extensions")
86                    .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
87                    .AppendASCII("1.0.0.0")));
88
89  ui_test_utils::NavigateToURL(
90      browser(),
91      GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html"));
92
93  bool result = false;
94  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
95      browser()->GetSelectedTabContents()->render_view_host(), L"",
96      L"testTabsAPI()", &result));
97  EXPECT_TRUE(result);
98
99  // There was a bug where we would crash if we navigated to a page in the same
100  // extension because no new render view was getting created, so we would not
101  // do some setup.
102  ui_test_utils::NavigateToURL(
103      browser(),
104      GURL("chrome-extension://behllobkkfkfnphdnhnkndlbkcpglgmj/page.html"));
105  result = false;
106  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
107      browser()->GetSelectedTabContents()->render_view_host(), L"",
108      L"testTabsAPI()", &result));
109  EXPECT_TRUE(result);
110}
111
112// Tests that GPU-related WebKit preferences are set for extension background
113// pages. See http://crbug.com/64512.
114IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WebKitPrefsBackgroundPage) {
115  ASSERT_TRUE(LoadExtension(
116      test_data_dir_.AppendASCII("good").AppendASCII("Extensions")
117                    .AppendASCII("behllobkkfkfnphdnhnkndlbkcpglgmj")
118                    .AppendASCII("1.0.0.0")));
119
120  ExtensionProcessManager* manager =
121        browser()->profile()->GetExtensionProcessManager();
122  ExtensionHost* host = FindHostWithPath(manager, "/backgroundpage.html", 1);
123  WebPreferences prefs = host->GetWebkitPrefs();
124  ASSERT_FALSE(prefs.experimental_webgl_enabled);
125  ASSERT_FALSE(prefs.accelerated_compositing_enabled);
126  ASSERT_FALSE(prefs.accelerated_2d_canvas_enabled);
127}
128
129// Tests that we can load page actions in the Omnibox.
130IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageAction) {
131  ASSERT_TRUE(test_server()->Start());
132
133  // This page action will not show an icon, since it doesn't specify one but
134  // is included here to test for a crash (http://crbug.com/25562).
135  ASSERT_TRUE(LoadExtension(
136      test_data_dir_.AppendASCII("browsertest")
137                    .AppendASCII("crash_25562")));
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(kFeedPage);
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  // Navigate to a page with no feed.
151  GURL no_feed = test_server()->GetURL(kNoFeedPage);
152  ui_test_utils::NavigateToURL(browser(), no_feed);
153  // Make sure the page action goes away.
154  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0));
155}
156
157// Tests that we don't lose the page action icon on in-page navigations.
158IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageActionInPageNavigation) {
159  ASSERT_TRUE(test_server()->Start());
160
161  FilePath extension_path(test_data_dir_.AppendASCII("api_test")
162                                        .AppendASCII("page_action")
163                                        .AppendASCII("hash_change"));
164  ASSERT_TRUE(LoadExtension(extension_path));
165
166  // Page action should become visible when we navigate here.
167  GURL feed_url = test_server()->GetURL(kHashPageA);
168  ui_test_utils::NavigateToURL(browser(), feed_url);
169  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
170
171  // In-page navigation, page action should remain.
172  feed_url = test_server()->GetURL(kHashPageAHash);
173  ui_test_utils::NavigateToURL(browser(), feed_url);
174  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
175
176  // Not an in-page navigation, page action should go away.
177  feed_url = test_server()->GetURL(kHashPageB);
178  ui_test_utils::NavigateToURL(browser(), feed_url);
179  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0));
180}
181
182// Tests that the location bar forgets about unloaded page actions.
183IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, UnloadPageAction) {
184  ASSERT_TRUE(test_server()->Start());
185
186  FilePath extension_path(test_data_dir_.AppendASCII("subscribe_page_action"));
187  ASSERT_TRUE(LoadExtension(extension_path));
188
189  // Navigation prompts the location bar to load page actions.
190  GURL feed_url = test_server()->GetURL(kFeedPage);
191  ui_test_utils::NavigateToURL(browser(), feed_url);
192  ASSERT_TRUE(WaitForPageActionCountChangeTo(1));
193
194  UnloadExtension(last_loaded_extension_id_);
195
196  // Make sure the page action goes away when it's unloaded.
197  ASSERT_TRUE(WaitForPageActionCountChangeTo(0));
198}
199
200// Flaky crash on Mac debug. http://crbug.com/45079
201#if defined(OS_MACOSX)
202#define PageActionRefreshCrash PageActionRefreshCrash
203#endif
204// Tests that we can load page actions in the Omnibox.
205IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, PageActionRefreshCrash) {
206  base::TimeTicks start_time = base::TimeTicks::Now();
207
208  ExtensionService* service = browser()->profile()->GetExtensionService();
209
210  size_t size_before = service->extensions()->size();
211
212  FilePath base_path = test_data_dir_.AppendASCII("browsertest")
213                                     .AppendASCII("crash_44415");
214  // Load extension A.
215  ASSERT_TRUE(LoadExtension(base_path.AppendASCII("ExtA")));
216  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
217  ASSERT_EQ(size_before + 1, service->extensions()->size());
218  const Extension* extensionA = service->extensions()->at(size_before);
219
220  LOG(INFO) << "Load extension A done  : "
221            << (base::TimeTicks::Now() - start_time).InMilliseconds()
222            << " ms" << std::flush;
223
224  // Load extension B.
225  ASSERT_TRUE(LoadExtension(base_path.AppendASCII("ExtB")));
226  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(2));
227  ASSERT_EQ(size_before + 2, service->extensions()->size());
228  const Extension* extensionB = service->extensions()->at(size_before + 1);
229
230  LOG(INFO) << "Load extension B done  : "
231            << (base::TimeTicks::Now() - start_time).InMilliseconds()
232            << " ms" << std::flush;
233
234  ReloadExtension(extensionA->id());
235  // ExtensionA has changed, so refetch it.
236  ASSERT_EQ(size_before + 2, service->extensions()->size());
237  extensionA = service->extensions()->at(size_before + 1);
238
239  LOG(INFO) << "Reload extension A done: "
240            << (base::TimeTicks::Now() - start_time).InMilliseconds()
241            << " ms" << std::flush;
242
243  ReloadExtension(extensionB->id());
244
245  LOG(INFO) << "Reload extension B done: "
246            << (base::TimeTicks::Now() - start_time).InMilliseconds()
247            << " ms" << std::flush;
248
249  // This is where it would crash, before http://crbug.com/44415 was fixed.
250  ReloadExtension(extensionA->id());
251
252  LOG(INFO) << "Test completed         : "
253            << (base::TimeTicks::Now() - start_time).InMilliseconds()
254            << " ms" << std::flush;
255}
256
257// Makes sure that the RSS detects RSS feed links, even when rel tag contains
258// more than just "alternate".
259IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, RSSMultiRelLink) {
260  ASSERT_TRUE(test_server()->Start());
261
262  ASSERT_TRUE(LoadExtension(
263    test_data_dir_.AppendASCII("subscribe_page_action")));
264
265  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(0));
266
267  // Navigate to the feed page.
268  GURL feed_url = test_server()->GetURL(kFeedPageMultiRel);
269  ui_test_utils::NavigateToURL(browser(), feed_url);
270  // We should now have one page action ready to go in the LocationBar.
271  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
272}
273
274// Tests that tooltips of a browser action icon can be specified using UTF8.
275// See http://crbug.com/25349.
276IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TitleLocalizationBrowserAction) {
277  ExtensionService* service = browser()->profile()->GetExtensionService();
278  const size_t size_before = service->extensions()->size();
279  FilePath extension_path(test_data_dir_.AppendASCII("browsertest")
280                                        .AppendASCII("title_localized"));
281  ASSERT_TRUE(LoadExtension(extension_path));
282
283  ASSERT_EQ(size_before + 1, service->extensions()->size());
284  const Extension* extension = service->extensions()->at(size_before);
285
286  EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur: l10n browser action").c_str(),
287               extension->description().c_str());
288  EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur is my name").c_str(),
289               extension->name().c_str());
290  int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents());
291  EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur").c_str(),
292               extension->browser_action()->GetTitle(tab_id).c_str());
293}
294
295// Tests that tooltips of a page action icon can be specified using UTF8.
296// See http://crbug.com/25349.
297IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, TitleLocalizationPageAction) {
298  ASSERT_TRUE(test_server()->Start());
299
300  ExtensionService* service = browser()->profile()->GetExtensionService();
301  const size_t size_before = service->extensions()->size();
302
303  FilePath extension_path(test_data_dir_.AppendASCII("browsertest")
304                                        .AppendASCII("title_localized_pa"));
305  ASSERT_TRUE(LoadExtension(extension_path));
306
307  // Any navigation prompts the location bar to load the page action.
308  GURL url = test_server()->GetURL(kLocalization);
309  ui_test_utils::NavigateToURL(browser(), url);
310  ASSERT_TRUE(WaitForPageActionVisibilityChangeTo(1));
311
312  ASSERT_EQ(size_before + 1, service->extensions()->size());
313  const Extension* extension = service->extensions()->at(size_before);
314
315  EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur: l10n page action").c_str(),
316               extension->description().c_str());
317  EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur is my name").c_str(),
318               extension->name().c_str());
319  int tab_id = ExtensionTabUtil::GetTabId(browser()->GetSelectedTabContents());
320  EXPECT_STREQ(WideToUTF8(L"Hreggvi\u00F0ur").c_str(),
321               extension->page_action()->GetTitle(tab_id).c_str());
322}
323
324GURL GetFeedUrl(net::TestServer* server, const std::string& feed_page,
325                bool direct_url, std::string extension_id) {
326  GURL feed_url = server->GetURL(feed_page);
327  if (direct_url) {
328    // We navigate directly to the subscribe page for feeds where the feed
329    // sniffing won't work, in other words, as is the case for malformed feeds.
330    return GURL(std::string(chrome::kExtensionScheme) +
331        chrome::kStandardSchemeSeparator +
332        extension_id + std::string(kSubscribePage) + std::string("?") +
333        feed_url.spec() + std::string("&synchronous"));
334  } else {
335    // Navigate to the feed content (which will cause the extension to try to
336    // sniff the type and display the subscribe page in another tab.
337    return GURL(feed_url.spec());
338  }
339}
340
341static const wchar_t* jscript_feed_title =
342    L"window.domAutomationController.send("
343    L"  document.getElementById('title') ? "
344    L"    document.getElementById('title').textContent : "
345    L"    \"element 'title' not found\""
346    L");";
347static const wchar_t* jscript_anchor =
348    L"window.domAutomationController.send("
349    L"  document.getElementById('anchor_0') ? "
350    L"    document.getElementById('anchor_0').textContent : "
351    L"    \"element 'anchor_0' not found\""
352    L");";
353static const wchar_t* jscript_desc =
354    L"window.domAutomationController.send("
355    L"  document.getElementById('desc_0') ? "
356    L"    document.getElementById('desc_0').textContent : "
357    L"    \"element 'desc_0' not found\""
358    L");";
359static const wchar_t* jscript_error =
360    L"window.domAutomationController.send("
361    L"  document.getElementById('error') ? "
362    L"    document.getElementById('error').textContent : "
363    L"    \"No error\""
364    L");";
365
366bool ValidatePageElement(TabContents* tab,
367                         const std::wstring& frame,
368                         const std::wstring& javascript,
369                         const std::string& expected_value) {
370  std::string returned_value;
371  std::string error;
372
373  if (!ui_test_utils::ExecuteJavaScriptAndExtractString(
374          tab->render_view_host(),
375          frame,
376          javascript, &returned_value))
377    return false;
378
379  EXPECT_STREQ(expected_value.c_str(), returned_value.c_str());
380  return expected_value == returned_value;
381}
382
383// Navigates to a feed page and, if |sniff_xml_type| is set, wait for the
384// extension to kick in, detect the feed and redirect to a feed preview page.
385// |sniff_xml_type| is generally set to true if the feed is sniffable and false
386// for invalid feeds.
387void NavigateToFeedAndValidate(net::TestServer* server,
388                               const std::string& url,
389                               Browser* browser,
390                               bool sniff_xml_type,
391                               const std::string& expected_feed_title,
392                               const std::string& expected_item_title,
393                               const std::string& expected_item_desc,
394                               const std::string& expected_error) {
395  if (sniff_xml_type) {
396    // TODO(finnur): Implement this is a non-flaky way.
397  }
398
399  ExtensionService* service = browser->profile()->GetExtensionService();
400  const Extension* extension = service->extensions()->back();
401  std::string id = extension->id();
402
403  // Navigate to the subscribe page directly.
404  ui_test_utils::NavigateToURL(browser, GetFeedUrl(server, url, true, id));
405
406  TabContents* tab = browser->GetSelectedTabContents();
407  ASSERT_TRUE(ValidatePageElement(tab,
408                                  L"",
409                                  jscript_feed_title,
410                                  expected_feed_title));
411  ASSERT_TRUE(ValidatePageElement(tab,
412                                  L"//html/body/div/iframe[1]",
413                                  jscript_anchor,
414                                  expected_item_title));
415  ASSERT_TRUE(ValidatePageElement(tab,
416                                  L"//html/body/div/iframe[1]",
417                                  jscript_desc,
418                                  expected_item_desc));
419  ASSERT_TRUE(ValidatePageElement(tab,
420                                  L"//html/body/div/iframe[1]",
421                                  jscript_error,
422                                  expected_error));
423}
424
425IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed1) {
426  ASSERT_TRUE(test_server()->Start());
427
428  ASSERT_TRUE(LoadExtension(
429      test_data_dir_.AppendASCII("subscribe_page_action")));
430
431  NavigateToFeedAndValidate(test_server(), kValidFeed1, browser(), true,
432                            "Feed for MyFeedTitle",
433                            "Title 1",
434                            "Desc",
435                            "No error");
436}
437
438IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed2) {
439  ASSERT_TRUE(test_server()->Start());
440
441  ASSERT_TRUE(LoadExtension(
442      test_data_dir_.AppendASCII("subscribe_page_action")));
443
444  NavigateToFeedAndValidate(test_server(), kValidFeed2, browser(), true,
445                            "Feed for MyFeed2",
446                            "My item title1",
447                            "This is a summary.",
448                            "No error");
449}
450
451IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed3) {
452  ASSERT_TRUE(test_server()->Start());
453
454  ASSERT_TRUE(LoadExtension(
455      test_data_dir_.AppendASCII("subscribe_page_action")));
456
457  NavigateToFeedAndValidate(test_server(), kValidFeed3, browser(), true,
458                            "Feed for Google Code buglist rss feed",
459                            "My dear title",
460                            "My dear content",
461                            "No error");
462}
463
464IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed4) {
465  ASSERT_TRUE(test_server()->Start());
466
467  ASSERT_TRUE(LoadExtension(
468      test_data_dir_.AppendASCII("subscribe_page_action")));
469
470  NavigateToFeedAndValidate(test_server(), kValidFeed4, browser(), true,
471                            "Feed for Title chars <script> %23 stop",
472                            "Title chars  %23 stop",
473                            "My dear content %23 stop",
474                            "No error");
475}
476
477IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed0) {
478  ASSERT_TRUE(test_server()->Start());
479
480  ASSERT_TRUE(LoadExtension(
481      test_data_dir_.AppendASCII("subscribe_page_action")));
482
483  // Try a feed with a link with an onclick handler (before r27440 this would
484  // trigger a NOTREACHED).
485  NavigateToFeedAndValidate(test_server(), kValidFeed0, browser(), true,
486                            "Feed for MyFeedTitle",
487                            "Title 1",
488                            "Desc VIDEO",
489                            "No error");
490}
491
492IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed5) {
493  ASSERT_TRUE(test_server()->Start());
494
495  ASSERT_TRUE(LoadExtension(
496      test_data_dir_.AppendASCII("subscribe_page_action")));
497
498  // Feed with valid but mostly empty xml.
499  NavigateToFeedAndValidate(test_server(), kValidFeed5, browser(), true,
500                            "Feed for Unknown feed name",
501                            "element 'anchor_0' not found",
502                            "element 'desc_0' not found",
503                            "This feed contains no entries.");
504}
505
506IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeed6) {
507  ASSERT_TRUE(test_server()->Start());
508
509  ASSERT_TRUE(LoadExtension(
510      test_data_dir_.AppendASCII("subscribe_page_action")));
511
512  // Feed that is technically invalid but still parseable.
513  NavigateToFeedAndValidate(test_server(), kValidFeed6, browser(), true,
514                            "Feed for MyFeedTitle",
515                            "Title 1",
516                            "Desc",
517                            "No error");
518}
519
520IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedInvalidFeed1) {
521  ASSERT_TRUE(test_server()->Start());
522
523  ASSERT_TRUE(LoadExtension(
524      test_data_dir_.AppendASCII("subscribe_page_action")));
525
526  // Try an empty feed.
527  NavigateToFeedAndValidate(test_server(), kInvalidFeed1, browser(), false,
528                            "Feed for Unknown feed name",
529                            "element 'anchor_0' not found",
530                            "element 'desc_0' not found",
531                            "This feed contains no entries.");
532}
533
534IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedInvalidFeed2) {
535  ASSERT_TRUE(test_server()->Start());
536
537  ASSERT_TRUE(LoadExtension(
538      test_data_dir_.AppendASCII("subscribe_page_action")));
539
540  // Try a garbage feed.
541  NavigateToFeedAndValidate(test_server(), kInvalidFeed2, browser(), false,
542                            "Feed for Unknown feed name",
543                            "element 'anchor_0' not found",
544                            "element 'desc_0' not found",
545                            "This feed contains no entries.");
546}
547
548IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedInvalidFeed3) {
549  ASSERT_TRUE(test_server()->Start());
550
551  ASSERT_TRUE(LoadExtension(
552      test_data_dir_.AppendASCII("subscribe_page_action")));
553
554  // Try a feed that doesn't exist.
555  NavigateToFeedAndValidate(test_server(), "foo.xml", browser(), false,
556                            "Feed for Unknown feed name",
557                            "element 'anchor_0' not found",
558                            "element 'desc_0' not found",
559                            "This feed contains no entries.");
560}
561
562IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedInvalidFeed4) {
563  ASSERT_TRUE(test_server()->Start());
564
565  ASSERT_TRUE(LoadExtension(
566      test_data_dir_.AppendASCII("subscribe_page_action")));
567
568  // subscribe.js shouldn't double-decode the URL passed in. Otherwise feed
569  // links such as http://search.twitter.com/search.atom?lang=en&q=%23chrome
570  // will result in no feed being downloaded because %23 gets decoded to # and
571  // therefore #chrome is not treated as part of the Twitter query. This test
572  // uses an underscore instead of a hash, but the principle is the same. If
573  // we start erroneously double decoding again, the path (and the feed) will
574  // become valid resulting in a failure for this test.
575  NavigateToFeedAndValidate(test_server(), kFeedTripleEncoded, browser(), true,
576                            "Feed for Unknown feed name",
577                            "element 'anchor_0' not found",
578                            "element 'desc_0' not found",
579                            "This feed contains no entries.");
580}
581
582IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, ParseFeedValidFeedNoLinks) {
583  ASSERT_TRUE(test_server()->Start());
584
585  ASSERT_TRUE(LoadExtension(
586      test_data_dir_.AppendASCII("subscribe_page_action")));
587
588  // Valid feed but containing no links.
589  NavigateToFeedAndValidate(test_server(), kValidFeedNoLinks, browser(), true,
590                            "Feed for MyFeedTitle",
591                            "Title with no link",
592                            "Desc",
593                            "No error");
594}
595
596// Tests that an error raised during an async function still fires
597// the callback, but sets chrome.extension.lastError.
598IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, LastError) {
599  ASSERT_TRUE(LoadExtension(
600      test_data_dir_.AppendASCII("browsertest").AppendASCII("last_error")));
601
602  // Get the ExtensionHost that is hosting our toolstrip page.
603  ExtensionProcessManager* manager =
604      browser()->profile()->GetExtensionProcessManager();
605  ExtensionHost* host = FindHostWithPath(manager, "/bg.html", 1);
606
607  bool result = false;
608  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
609      host->render_view_host(), L"", L"testLastError()", &result));
610  EXPECT_TRUE(result);
611}
612
613// Helper function for common code shared by the 3 WindowOpen tests below.
614static void WindowOpenHelper(Browser* browser, const GURL& start_url,
615                             const std::string& newtab_url,
616                             TabContents** newtab_result) {
617  ui_test_utils::NavigateToURL(browser, start_url);
618
619  ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
620      browser->GetSelectedTabContents()->render_view_host(), L"",
621      L"window.open('" + UTF8ToWide(newtab_url) + L"');"));
622
623  // Now the active tab in last active window should be the new tab.
624  Browser* last_active_browser = BrowserList::GetLastActive();
625  EXPECT_TRUE(last_active_browser);
626  TabContents* newtab = last_active_browser->GetSelectedTabContents();
627  EXPECT_TRUE(newtab);
628  GURL expected_url = start_url.Resolve(newtab_url);
629  if (!newtab->controller().GetLastCommittedEntry() ||
630      newtab->controller().GetLastCommittedEntry()->url() != expected_url)
631    ui_test_utils::WaitForNavigation(&newtab->controller());
632  EXPECT_EQ(expected_url,
633            newtab->controller().GetLastCommittedEntry()->url());
634  if (newtab_result)
635    *newtab_result = newtab;
636}
637
638// Tests that an extension page can call window.open to an extension URL and
639// the new window has extension privileges.
640IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenExtension) {
641  ASSERT_TRUE(LoadExtension(
642      test_data_dir_.AppendASCII("uitest").AppendASCII("window_open")));
643
644  TabContents* newtab;
645  ASSERT_NO_FATAL_FAILURE(WindowOpenHelper(
646      browser(),
647      GURL(std::string("chrome-extension://") + last_loaded_extension_id_ +
648           "/test.html"),
649      "newtab.html", &newtab));
650
651  bool result = false;
652  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
653      newtab->render_view_host(), L"", L"testExtensionApi()", &result));
654  EXPECT_TRUE(result);
655}
656
657// Tests that if an extension page calls window.open to an invalid extension
658// URL, the browser doesn't crash.
659IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenInvalidExtension) {
660  ASSERT_TRUE(LoadExtension(
661      test_data_dir_.AppendASCII("uitest").AppendASCII("window_open")));
662
663  ASSERT_NO_FATAL_FAILURE(WindowOpenHelper(
664      browser(),
665      GURL(std::string("chrome-extension://") + last_loaded_extension_id_ +
666           "/test.html"),
667      "chrome-extension://thisissurelynotavalidextensionid/newtab.html", NULL));
668
669  // If we got to this point, we didn't crash, so we're good.
670}
671
672// Tests that calling window.open from the newtab page to an extension URL
673// gives the new window extension privileges - even though the opening page
674// does not have extension privileges, we break the script connection, so
675// there is no privilege leak.
676IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenNoPrivileges) {
677  ASSERT_TRUE(LoadExtension(
678      test_data_dir_.AppendASCII("uitest").AppendASCII("window_open")));
679
680  TabContents* newtab;
681  ASSERT_NO_FATAL_FAILURE(WindowOpenHelper(
682      browser(),
683      GURL("about:blank"),
684      std::string("chrome-extension://") + last_loaded_extension_id_ +
685          "/newtab.html",
686      &newtab));
687
688  // Extension API should succeed.
689  bool result = false;
690  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
691      newtab->render_view_host(), L"", L"testExtensionApi()", &result));
692  EXPECT_TRUE(result);
693}
694
695#if defined(OS_WIN)
696#define MAYBE_PluginLoadUnload PluginLoadUnload
697#elif defined(OS_LINUX)
698// http://crbug.com/47598
699#define MAYBE_PluginLoadUnload DISABLED_PluginLoadUnload
700#else
701// TODO(mpcomplete): http://crbug.com/29900 need cross platform plugin support.
702#define MAYBE_PluginLoadUnload DISABLED_PluginLoadUnload
703#endif
704
705// Tests that a renderer's plugin list is properly updated when we load and
706// unload an extension that contains a plugin.
707IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, MAYBE_PluginLoadUnload) {
708  FilePath extension_dir =
709      test_data_dir_.AppendASCII("uitest").AppendASCII("plugins");
710
711  ui_test_utils::NavigateToURL(browser(),
712      net::FilePathToFileURL(extension_dir.AppendASCII("test.html")));
713  TabContents* tab = browser()->GetSelectedTabContents();
714
715  // With no extensions, the plugin should not be loaded.
716  bool result = false;
717  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
718      tab->render_view_host(), L"", L"testPluginWorks()", &result));
719  EXPECT_FALSE(result);
720
721  ExtensionService* service = browser()->profile()->GetExtensionService();
722  const size_t size_before = service->extensions()->size();
723  ASSERT_TRUE(LoadExtension(extension_dir));
724  EXPECT_EQ(size_before + 1, service->extensions()->size());
725  // Now the plugin should be in the cache, but we have to reload the page for
726  // it to work.
727  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
728      tab->render_view_host(), L"", L"testPluginWorks()", &result));
729  EXPECT_FALSE(result);
730  browser()->Reload(CURRENT_TAB);
731  ui_test_utils::WaitForNavigationInCurrentTab(browser());
732  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
733      tab->render_view_host(), L"", L"testPluginWorks()", &result));
734  EXPECT_TRUE(result);
735
736  EXPECT_EQ(size_before + 1, service->extensions()->size());
737  UnloadExtension(service->extensions()->at(size_before)->id());
738  EXPECT_EQ(size_before, service->extensions()->size());
739
740  // Now the plugin should be unloaded, and the page should be broken.
741
742  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
743      tab->render_view_host(), L"", L"testPluginWorks()", &result));
744  EXPECT_FALSE(result);
745
746  // If we reload the extension and page, it should work again.
747
748  ASSERT_TRUE(LoadExtension(extension_dir));
749  EXPECT_EQ(size_before + 1, service->extensions()->size());
750  browser()->Reload(CURRENT_TAB);
751  ui_test_utils::WaitForNavigationInCurrentTab(browser());
752  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
753      tab->render_view_host(), L"", L"testPluginWorks()", &result));
754  EXPECT_TRUE(result);
755}
756
757#if defined(OS_WIN) || defined(OS_LINUX) || defined(OS_CHROMEOS)
758#define MAYBE_PluginPrivate PluginPrivate
759#else
760// TODO(mpcomplete): http://crbug.com/29900 need cross platform plugin support.
761#define MAYBE_PluginPrivate DISABLED_PluginPrivate
762#endif
763
764// Tests that private extension plugins are only visible to the extension.
765IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, MAYBE_PluginPrivate) {
766  FilePath extension_dir =
767      test_data_dir_.AppendASCII("uitest").AppendASCII("plugins_private");
768
769  ExtensionService* service = browser()->profile()->GetExtensionService();
770  const size_t size_before = service->extensions()->size();
771  ASSERT_TRUE(LoadExtension(extension_dir));
772  EXPECT_EQ(size_before + 1, service->extensions()->size());
773
774  // Load the test page through the extension URL, and the plugin should work.
775  const Extension* extension = service->extensions()->back();
776  ui_test_utils::NavigateToURL(browser(),
777      extension->GetResourceURL("test.html"));
778  TabContents* tab = browser()->GetSelectedTabContents();
779  bool result = false;
780  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
781      tab->render_view_host(), L"", L"testPluginWorks()", &result));
782  // We don't allow extension plugins to run on ChromeOS.
783#if defined(OS_CHROMEOS)
784  EXPECT_FALSE(result);
785#else
786  EXPECT_TRUE(result);
787#endif
788
789  // Now load it through a file URL. The plugin should not load.
790  ui_test_utils::NavigateToURL(browser(),
791      net::FilePathToFileURL(extension_dir.AppendASCII("test.html")));
792  ASSERT_TRUE(ui_test_utils::ExecuteJavaScriptAndExtractBool(
793      tab->render_view_host(), L"", L"testPluginWorks()", &result));
794  EXPECT_FALSE(result);
795}
796
797// Used to simulate a click on the first button named 'Options'.
798static const wchar_t* jscript_click_option_button =
799    L"(function() { "
800    L"  var button = document.evaluate(\"//button[text()='Options']\","
801    L"      document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,"
802    L"      null).snapshotItem(0);"
803    L"  button.click();"
804    L"})();";
805
806// Test that an extension with an options page makes an 'Options' button appear
807// on chrome://extensions, and that clicking the button opens a new tab with the
808// extension's options page.
809// Disabled.  See http://crbug.com/26948 for details.
810IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, DISABLED_OptionsPage) {
811  // Install an extension with an options page.
812  ASSERT_TRUE(InstallExtension(test_data_dir_.AppendASCII("options.crx"), 1));
813  ExtensionService* service = browser()->profile()->GetExtensionService();
814  const ExtensionList* extensions = service->extensions();
815  ASSERT_EQ(1u, extensions->size());
816  const Extension* extension = extensions->at(0);
817
818  // Go to the chrome://extensions page and click the Options button.
819  ui_test_utils::NavigateToURL(browser(), GURL(chrome::kChromeUIExtensionsURL));
820  TabStripModel* tab_strip = browser()->tabstrip_model();
821  ASSERT_TRUE(ui_test_utils::ExecuteJavaScript(
822      browser()->GetSelectedTabContents()->render_view_host(), L"",
823      jscript_click_option_button));
824
825  // If the options page hasn't already come up, wait for it.
826  if (tab_strip->count() == 1) {
827    ui_test_utils::WaitForNewTab(browser());
828  }
829  ASSERT_EQ(2, tab_strip->count());
830
831  EXPECT_EQ(extension->GetResourceURL("options.html"),
832            tab_strip->GetTabContentsAt(1)->tab_contents()->GetURL());
833}
834
835//==============================================================================
836// STOP! Please do not add any more random-ass tests here. Create new files for
837// your tests grouped by functionality. Also, you should strongly consider using
838// ExtensionAPITest if possible.
839//==============================================================================
840