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