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