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