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 "base/command_line.h"
      6 #include "base/memory/scoped_vector.h"
      7 #include "base/path_service.h"
      8 #include "base/strings/stringprintf.h"
      9 #include "chrome/browser/extensions/extension_apitest.h"
     10 #include "chrome/browser/extensions/extension_host.h"
     11 #include "chrome/browser/extensions/extension_system.h"
     12 #include "chrome/browser/extensions/extension_test_message_listener.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/browser/ui/browser.h"
     15 #include "chrome/browser/ui/browser_finder.h"
     16 #include "chrome/browser/ui/browser_iterator.h"
     17 #include "chrome/browser/ui/panels/panel_manager.h"
     18 #include "chrome/browser/ui/tabs/tab_strip_model.h"
     19 #include "chrome/common/chrome_paths.h"
     20 #include "chrome/common/chrome_switches.h"
     21 #include "chrome/test/base/test_switches.h"
     22 #include "chrome/test/base/ui_test_utils.h"
     23 #include "content/public/browser/render_process_host.h"
     24 #include "content/public/browser/web_contents.h"
     25 #include "content/public/common/result_codes.h"
     26 #include "content/public/test/browser_test_utils.h"
     27 #include "extensions/browser/process_manager.h"
     28 #include "extensions/common/extension.h"
     29 #include "extensions/common/switches.h"
     30 #include "net/dns/mock_host_resolver.h"
     31 #include "net/test/embedded_test_server/embedded_test_server.h"
     32 #include "testing/gtest/include/gtest/gtest.h"
     33 
     34 #if defined(USE_ASH)
     35 #include "apps/shell_window_registry.h"
     36 #endif
     37 
     38 #if defined(USE_ASH) && !defined(OS_WIN)
     39 // TODO(stevenjb): Figure out the correct behavior for Ash + Win
     40 #define USE_ASH_PANELS
     41 #endif
     42 
     43 using content::OpenURLParams;
     44 using content::Referrer;
     45 using content::WebContents;
     46 
     47 // Disabled, http://crbug.com/64899.
     48 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_WindowOpen) {
     49   CommandLine::ForCurrentProcess()->AppendSwitch(
     50       extensions::switches::kEnableExperimentalExtensionApis);
     51 
     52   ResultCatcher catcher;
     53   ASSERT_TRUE(LoadExtensionIncognito(test_data_dir_
     54       .AppendASCII("window_open").AppendASCII("spanning")));
     55   EXPECT_TRUE(catcher.GetNextResult()) << catcher.message();
     56 }
     57 
     58 int GetPanelCount(Browser* browser) {
     59 #if defined(USE_ASH_PANELS)
     60   return static_cast<int>(apps::ShellWindowRegistry::Get(
     61       browser->profile())->shell_windows().size());
     62 #else
     63   return PanelManager::GetInstance()->num_panels();
     64 #endif
     65 }
     66 
     67 bool WaitForTabsAndPopups(Browser* browser,
     68                           int num_tabs,
     69                           int num_popups,
     70                           int num_panels) {
     71   SCOPED_TRACE(
     72       base::StringPrintf("WaitForTabsAndPopups tabs:%d, popups:%d, panels:%d",
     73                          num_tabs, num_popups, num_panels));
     74   // We start with one tab and one browser already open.
     75   ++num_tabs;
     76   size_t num_browsers = static_cast<size_t>(num_popups) + 1;
     77 
     78   const base::TimeDelta kWaitTime = base::TimeDelta::FromSeconds(10);
     79   base::TimeTicks end_time = base::TimeTicks::Now() + kWaitTime;
     80   while (base::TimeTicks::Now() < end_time) {
     81     if (chrome::GetBrowserCount(browser->profile(),
     82                                 browser->host_desktop_type()) == num_browsers &&
     83         browser->tab_strip_model()->count() == num_tabs &&
     84         GetPanelCount(browser) == num_panels)
     85       break;
     86 
     87     content::RunAllPendingInMessageLoop();
     88   }
     89 
     90   EXPECT_EQ(num_browsers,
     91             chrome::GetBrowserCount(browser->profile(),
     92                                     browser->host_desktop_type()));
     93   EXPECT_EQ(num_tabs, browser->tab_strip_model()->count());
     94   EXPECT_EQ(num_panels, GetPanelCount(browser));
     95 
     96   int num_popups_seen = 0;
     97   for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
     98     if (*iter == browser)
     99       continue;
    100 
    101     EXPECT_TRUE((*iter)->is_type_popup());
    102     ++num_popups_seen;
    103   }
    104   EXPECT_EQ(num_popups, num_popups_seen);
    105 
    106   return ((num_browsers ==
    107                chrome::GetBrowserCount(browser->profile(),
    108                                        browser->host_desktop_type())) &&
    109           (num_tabs == browser->tab_strip_model()->count()) &&
    110           (num_panels == GetPanelCount(browser)) &&
    111           (num_popups == num_popups_seen));
    112 }
    113 
    114 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, BrowserIsApp) {
    115   host_resolver()->AddRule("a.com", "127.0.0.1");
    116   ASSERT_TRUE(StartEmbeddedTestServer());
    117   ASSERT_TRUE(LoadExtension(
    118       test_data_dir_.AppendASCII("window_open").AppendASCII("browser_is_app")));
    119 
    120   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 0, 2, 0));
    121 
    122   for (chrome::BrowserIterator iter; !iter.done(); iter.Next()) {
    123     if (*iter == browser())
    124       ASSERT_FALSE(iter->is_app());
    125     else
    126       ASSERT_TRUE(iter->is_app());
    127   }
    128 }
    129 
    130 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupDefault) {
    131   ASSERT_TRUE(StartEmbeddedTestServer());
    132   ASSERT_TRUE(LoadExtension(
    133       test_data_dir_.AppendASCII("window_open").AppendASCII("popup")));
    134 
    135   const int num_tabs = 1;
    136   const int num_popups = 0;
    137   EXPECT_TRUE(WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0));
    138 }
    139 
    140 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupIframe) {
    141   ASSERT_TRUE(StartEmbeddedTestServer());
    142   base::FilePath test_data_dir;
    143   PathService::Get(chrome::DIR_TEST_DATA, &test_data_dir);
    144   embedded_test_server()->ServeFilesFromDirectory(test_data_dir);
    145   ASSERT_TRUE(LoadExtension(
    146       test_data_dir_.AppendASCII("window_open").AppendASCII("popup_iframe")));
    147 
    148   const int num_tabs = 0;
    149   const int num_popups = 1;
    150   EXPECT_TRUE(WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0));
    151 }
    152 
    153 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupLarge) {
    154   ASSERT_TRUE(StartEmbeddedTestServer());
    155   ASSERT_TRUE(LoadExtension(
    156       test_data_dir_.AppendASCII("window_open").AppendASCII("popup_large")));
    157 
    158   // On other systems this should open a new popup window.
    159   const int num_tabs = 0;
    160   const int num_popups = 1;
    161   EXPECT_TRUE(WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0));
    162 }
    163 
    164 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowOpenPopupSmall) {
    165   ASSERT_TRUE(StartEmbeddedTestServer());
    166   ASSERT_TRUE(LoadExtension(
    167       test_data_dir_.AppendASCII("window_open").AppendASCII("popup_small")));
    168 
    169   // On ChromeOS this should open a new panel (acts like a new popup window).
    170   // On other systems this should open a new popup window.
    171   const int num_tabs = 0;
    172   const int num_popups = 1;
    173   EXPECT_TRUE(WaitForTabsAndPopups(browser(), num_tabs, num_popups, 0));
    174 }
    175 
    176 // Disabled on Windows. Often times out or fails: crbug.com/177530
    177 #if defined(OS_WIN)
    178 #define MAYBE_PopupBlockingExtension DISABLED_PopupBlockingExtension
    179 #else
    180 #define MAYBE_PopupBlockingExtension PopupBlockingExtension
    181 #endif
    182 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_PopupBlockingExtension) {
    183   host_resolver()->AddRule("*", "127.0.0.1");
    184   ASSERT_TRUE(StartEmbeddedTestServer());
    185 
    186   ASSERT_TRUE(LoadExtension(
    187       test_data_dir_.AppendASCII("window_open").AppendASCII("popup_blocking")
    188       .AppendASCII("extension")));
    189 
    190   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 5, 3, 0));
    191 }
    192 
    193 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, PopupBlockingHostedApp) {
    194   host_resolver()->AddRule("*", "127.0.0.1");
    195   ASSERT_TRUE(test_server()->Start());
    196 
    197   ASSERT_TRUE(LoadExtension(
    198       test_data_dir_.AppendASCII("window_open").AppendASCII("popup_blocking")
    199       .AppendASCII("hosted_app")));
    200 
    201   // The app being tested owns the domain a.com .  The test URLs we navigate
    202   // to below must be within that domain, so that they fall within the app's
    203   // web extent.
    204   GURL::Replacements replace_host;
    205   std::string a_dot_com = "a.com";
    206   replace_host.SetHostStr(a_dot_com);
    207 
    208   const std::string popup_app_contents_path(
    209     "files/extensions/api_test/window_open/popup_blocking/hosted_app/");
    210 
    211   GURL open_tab =
    212       test_server()->GetURL(popup_app_contents_path + "open_tab.html")
    213           .ReplaceComponents(replace_host);
    214   GURL open_popup =
    215       test_server()->GetURL(popup_app_contents_path + "open_popup.html")
    216           .ReplaceComponents(replace_host);
    217 
    218   browser()->OpenURL(OpenURLParams(
    219       open_tab, Referrer(), NEW_FOREGROUND_TAB, content::PAGE_TRANSITION_TYPED,
    220       false));
    221   browser()->OpenURL(OpenURLParams(
    222       open_popup, Referrer(), NEW_FOREGROUND_TAB,
    223       content::PAGE_TRANSITION_TYPED, false));
    224 
    225   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 3, 1, 0));
    226 }
    227 
    228 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowArgumentsOverflow) {
    229   ASSERT_TRUE(RunExtensionTest("window_open/argument_overflow")) << message_;
    230 }
    231 
    232 class WindowOpenPanelDisabledTest : public ExtensionApiTest {
    233   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    234     ExtensionApiTest::SetUpCommandLine(command_line);
    235     // TODO(jennb): Re-enable when panels are enabled by default.
    236     // command_line->AppendSwitch(switches::kDisablePanels);
    237   }
    238 };
    239 
    240 IN_PROC_BROWSER_TEST_F(WindowOpenPanelDisabledTest,
    241                        DISABLED_WindowOpenPanelNotEnabled) {
    242   ASSERT_TRUE(RunExtensionTest("window_open/panel_not_enabled")) << message_;
    243 }
    244 
    245 class WindowOpenPanelTest : public ExtensionApiTest {
    246   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    247     ExtensionApiTest::SetUpCommandLine(command_line);
    248     command_line->AppendSwitch(switches::kEnablePanels);
    249   }
    250 };
    251 
    252 #if defined(USE_ASH_PANELS)
    253 // On Ash, this currently fails because we're currently opening new panel
    254 // windows as popup windows instead.
    255 #define MAYBE_WindowOpenPanel DISABLED_WindowOpenPanel
    256 #else
    257 #define MAYBE_WindowOpenPanel WindowOpenPanel
    258 #endif
    259 IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_WindowOpenPanel) {
    260   ASSERT_TRUE(RunExtensionTest("window_open/panel")) << message_;
    261 }
    262 
    263 #if defined(USE_ASH_PANELS)
    264 // On Ash, this currently fails because we're currently opening new panel
    265 // windows as popup windows instead.
    266 #define MAYBE_WindowOpenPanelDetached DISABLED_WindowOpenPanelDetached
    267 #else
    268 #define MAYBE_WindowOpenPanelDetached WindowOpenPanelDetached
    269 #endif
    270 IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_WindowOpenPanelDetached) {
    271   ASSERT_TRUE(RunExtensionTest("window_open/panel_detached")) << message_;
    272 }
    273 
    274 IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest,
    275                        CloseNonExtensionPanelsOnUninstall) {
    276 #if defined(OS_WIN) && defined(USE_ASH)
    277   // Disable this test in Metro+Ash for now (http://crbug.com/262796).
    278   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests))
    279     return;
    280 #endif
    281 
    282 #if defined(USE_ASH_PANELS)
    283   // On Ash, new panel windows open as popup windows instead.
    284   int num_popups, num_panels;
    285   if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePanels)) {
    286     num_popups = 2;
    287     num_panels = 2;
    288   } else {
    289     num_popups = 4;
    290     num_panels = 0;
    291   }
    292 #else
    293   int num_popups = 2;
    294   int num_panels = 2;
    295 #endif
    296   ASSERT_TRUE(StartEmbeddedTestServer());
    297 
    298   // Setup listeners to wait on strings we expect the extension pages to send.
    299   std::vector<std::string> test_strings;
    300   test_strings.push_back("content_tab");
    301   if (num_panels)
    302     test_strings.push_back("content_panel");
    303   test_strings.push_back("content_popup");
    304 
    305   ScopedVector<ExtensionTestMessageListener> listeners;
    306   for (size_t i = 0; i < test_strings.size(); ++i) {
    307     listeners.push_back(
    308         new ExtensionTestMessageListener(test_strings[i], false));
    309   }
    310 
    311   const extensions::Extension* extension = LoadExtension(
    312       test_data_dir_.AppendASCII("window_open").AppendASCII(
    313           "close_panels_on_uninstall"));
    314   ASSERT_TRUE(extension);
    315 
    316   // Two tabs. One in extension domain and one in non-extension domain.
    317   // Two popups - one in extension domain and one in non-extension domain.
    318   // Two panels - one in extension domain and one in non-extension domain.
    319   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 2, num_popups, num_panels));
    320 
    321   // Wait on test messages to make sure the pages loaded.
    322   for (size_t i = 0; i < listeners.size(); ++i)
    323     ASSERT_TRUE(listeners[i]->WaitUntilSatisfied());
    324 
    325   UninstallExtension(extension->id());
    326 
    327   // Wait for the tabs and popups in non-extension domain to stay open.
    328   // Expect everything else, including panels, to close.
    329   num_popups -= 1;
    330 #if defined(USE_ASH_PANELS)
    331   if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnablePanels)) {
    332     // On Ash, new panel windows open as popup windows instead, so there are 2
    333     // extension domain popups that will close (instead of 1 popup on non-Ash).
    334     num_popups -= 1;
    335   }
    336 #endif
    337 #if defined(USE_ASH)
    338 #if !defined(OS_WIN)
    339   // On linux ash we close all popup applications when closing its extension.
    340   num_popups = 0;
    341 #endif
    342 #endif
    343   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 1, num_popups, 0));
    344 }
    345 
    346 // This test isn't applicable on Chrome OS, which automatically reloads
    347 // crashed pages.
    348 #if !defined(OS_CHROMEOS)
    349 IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, ClosePanelsOnExtensionCrash) {
    350 #if defined(USE_ASH_PANELS)
    351   // On Ash, new panel windows open as popup windows instead.
    352   int num_popups = 4;
    353   int num_panels = 0;
    354 #else
    355   int num_popups = 2;
    356   int num_panels = 2;
    357 #endif
    358   ASSERT_TRUE(StartEmbeddedTestServer());
    359 
    360   // Setup listeners to wait on strings we expect the extension pages to send.
    361   std::vector<std::string> test_strings;
    362   test_strings.push_back("content_tab");
    363   if (num_panels)
    364     test_strings.push_back("content_panel");
    365   test_strings.push_back("content_popup");
    366 
    367   ScopedVector<ExtensionTestMessageListener> listeners;
    368   for (size_t i = 0; i < test_strings.size(); ++i) {
    369     listeners.push_back(
    370         new ExtensionTestMessageListener(test_strings[i], false));
    371   }
    372 
    373   const extensions::Extension* extension = LoadExtension(
    374       test_data_dir_.AppendASCII("window_open").AppendASCII(
    375           "close_panels_on_uninstall"));
    376   ASSERT_TRUE(extension);
    377 
    378   // Two tabs. One in extension domain and one in non-extension domain.
    379   // Two popups - one in extension domain and one in non-extension domain.
    380   // Two panels - one in extension domain and one in non-extension domain.
    381   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 2, num_popups, num_panels));
    382 
    383   // Wait on test messages to make sure the pages loaded.
    384   for (size_t i = 0; i < listeners.size(); ++i)
    385     ASSERT_TRUE(listeners[i]->WaitUntilSatisfied());
    386 
    387   // Crash the extension.
    388   extensions::ExtensionHost* extension_host =
    389       extensions::ExtensionSystem::Get(browser()->profile())->
    390           process_manager()->GetBackgroundHostForExtension(extension->id());
    391   ASSERT_TRUE(extension_host);
    392   base::KillProcess(extension_host->render_process_host()->GetHandle(),
    393                     content::RESULT_CODE_KILLED, false);
    394   WaitForExtensionCrash(extension->id());
    395 
    396   // Only expect panels to close. The rest stay open to show a sad-tab.
    397   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 2, num_popups, 0));
    398 }
    399 #endif  // !defined(OS_CHROMEOS)
    400 
    401 #if defined(USE_ASH_PANELS)
    402 // This test is not applicable on Ash. The modified window.open behavior only
    403 // applies to non-Ash panel windows.
    404 #define MAYBE_WindowOpenFromPanel DISABLED_WindowOpenFromPanel
    405 #else
    406 #define MAYBE_WindowOpenFromPanel WindowOpenFromPanel
    407 #endif
    408 IN_PROC_BROWSER_TEST_F(WindowOpenPanelTest, MAYBE_WindowOpenFromPanel) {
    409   ASSERT_TRUE(StartEmbeddedTestServer());
    410 
    411   // Load the extension that will open a panel which then calls window.open.
    412   ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("window_open").
    413                             AppendASCII("panel_window_open")));
    414 
    415   // Expect one panel (opened by extension) and one tab (from the panel calling
    416   // window.open). Panels modify the WindowOpenDisposition in window.open
    417   // to always open in a tab.
    418   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 1, 0, 1));
    419 }
    420 
    421 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, DISABLED_WindowOpener) {
    422   ASSERT_TRUE(RunExtensionTest("window_open/opener")) << message_;
    423 }
    424 
    425 #if defined(OS_MACOSX)
    426 // Extension popup windows are incorrectly sized on OSX, crbug.com/225601
    427 #define MAYBE_WindowOpenSized DISABLED_WindowOpenSized
    428 #else
    429 #define MAYBE_WindowOpenSized WindowOpenSized
    430 #endif
    431 // Ensure that the width and height properties of a window opened with
    432 // chrome.windows.create match the creation parameters. See crbug.com/173831.
    433 IN_PROC_BROWSER_TEST_F(ExtensionApiTest, MAYBE_WindowOpenSized) {
    434   ASSERT_TRUE(RunExtensionTest("window_open/window_size")) << message_;
    435   EXPECT_TRUE(WaitForTabsAndPopups(browser(), 0, 1, 0));
    436 }
    437 
    438 // Tests that an extension page can call window.open to an extension URL and
    439 // the new window has extension privileges.
    440 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenExtension) {
    441   ASSERT_TRUE(LoadExtension(
    442       test_data_dir_.AppendASCII("uitest").AppendASCII("window_open")));
    443 
    444   GURL start_url(std::string("chrome-extension://") +
    445       last_loaded_extension_id() + "/test.html");
    446   ui_test_utils::NavigateToURL(browser(), start_url);
    447   WebContents* newtab = NULL;
    448   ASSERT_NO_FATAL_FAILURE(
    449       OpenWindow(browser()->tab_strip_model()->GetActiveWebContents(),
    450       start_url.Resolve("newtab.html"), true, &newtab));
    451 
    452   bool result = false;
    453   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(newtab, "testExtensionApi()",
    454                                                    &result));
    455   EXPECT_TRUE(result);
    456 }
    457 
    458 // Tests that if an extension page calls window.open to an invalid extension
    459 // URL, the browser doesn't crash.
    460 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenInvalidExtension) {
    461   ASSERT_TRUE(LoadExtension(
    462       test_data_dir_.AppendASCII("uitest").AppendASCII("window_open")));
    463 
    464   GURL start_url(std::string("chrome-extension://") +
    465       last_loaded_extension_id() + "/test.html");
    466   ui_test_utils::NavigateToURL(browser(), start_url);
    467   ASSERT_NO_FATAL_FAILURE(
    468       OpenWindow(browser()->tab_strip_model()->GetActiveWebContents(),
    469       GURL("chrome-extension://thisissurelynotavalidextensionid/newtab.html"),
    470       false, NULL));
    471 
    472   // If we got to this point, we didn't crash, so we're good.
    473 }
    474 
    475 // Tests that calling window.open from the newtab page to an extension URL
    476 // gives the new window extension privileges - even though the opening page
    477 // does not have extension privileges, we break the script connection, so
    478 // there is no privilege leak.
    479 IN_PROC_BROWSER_TEST_F(ExtensionBrowserTest, WindowOpenNoPrivileges) {
    480   ASSERT_TRUE(LoadExtension(
    481       test_data_dir_.AppendASCII("uitest").AppendASCII("window_open")));
    482 
    483   ui_test_utils::NavigateToURL(browser(), GURL("about:blank"));
    484   WebContents* newtab = NULL;
    485   ASSERT_NO_FATAL_FAILURE(
    486       OpenWindow(browser()->tab_strip_model()->GetActiveWebContents(),
    487       GURL(std::string("chrome-extension://") + last_loaded_extension_id() +
    488           "/newtab.html"), false, &newtab));
    489 
    490   // Extension API should succeed.
    491   bool result = false;
    492   ASSERT_TRUE(content::ExecuteScriptAndExtractBool(newtab, "testExtensionApi()",
    493                                                    &result));
    494   EXPECT_TRUE(result);
    495 }
    496