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 46using content::OpenURLParams; 47using content::Referrer; 48using content::WebContents; 49 50// Disabled, http://crbug.com/64899. 51IN_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 61int 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 70bool 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 117IN_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 133IN_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 143IN_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 156IN_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 167IN_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 185IN_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 196IN_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 231IN_PROC_BROWSER_TEST_F(ExtensionApiTest, WindowArgumentsOverflow) { 232 ASSERT_TRUE(RunExtensionTest("window_open/argument_overflow")) << message_; 233} 234 235class 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 243IN_PROC_BROWSER_TEST_F(WindowOpenPanelDisabledTest, 244 DISABLED_WindowOpenPanelNotEnabled) { 245 ASSERT_TRUE(RunExtensionTest("window_open/panel_not_enabled")) << message_; 246} 247 248class 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 262IN_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 275IN_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 287IN_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) 362IN_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 421IN_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 434IN_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. 446IN_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. 453IN_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. 474IN_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. 494IN_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