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