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