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