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 "chrome/browser/chrome_notification_types.h" 7#include "chrome/browser/extensions/extension_apitest.h" 8#include "chrome/browser/extensions/extension_service.h" 9#include "chrome/browser/profiles/profile.h" 10#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h" 11#include "chrome/browser/ui/browser.h" 12#include "chrome/browser/ui/browser_commands.h" 13#include "chrome/browser/ui/browser_finder.h" 14#include "chrome/browser/ui/browser_list.h" 15#include "chrome/browser/ui/browser_window.h" 16#include "chrome/browser/ui/tabs/tab_strip_model.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/navigation_entry.h" 21#include "content/public/browser/notification_service.h" 22#include "content/public/browser/render_process_host.h" 23#include "content/public/browser/render_view_host.h" 24#include "content/public/browser/site_instance.h" 25#include "content/public/browser/web_contents.h" 26#include "content/public/test/browser_test_utils.h" 27#include "content/public/test/test_navigation_observer.h" 28#include "extensions/browser/extension_host.h" 29#include "extensions/browser/extension_system.h" 30#include "extensions/browser/install_flag.h" 31#include "extensions/browser/process_map.h" 32#include "extensions/common/extension.h" 33#include "extensions/common/file_util.h" 34#include "extensions/common/switches.h" 35#include "net/dns/mock_host_resolver.h" 36#include "net/test/embedded_test_server/embedded_test_server.h" 37#include "sync/api/string_ordinal.h" 38 39using content::NavigationController; 40using content::RenderViewHost; 41using content::SiteInstance; 42using content::WebContents; 43using extensions::Extension; 44 45class AppApiTest : public ExtensionApiTest { 46 protected: 47 // Gets the base URL for files for a specific test, making sure that it uses 48 // "localhost" as the hostname, since that is what the extent is declared 49 // as in the test apps manifests. 50 GURL GetTestBaseURL(const std::string& test_directory) { 51 GURL::Replacements replace_host; 52 std::string host_str("localhost"); // must stay in scope with replace_host 53 replace_host.SetHostStr(host_str); 54 GURL base_url = embedded_test_server()->GetURL( 55 "/extensions/api_test/" + test_directory + "/"); 56 return base_url.ReplaceComponents(replace_host); 57 } 58 59 // Pass flags to make testing apps easier. 60 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 61 ExtensionApiTest::SetUpCommandLine(command_line); 62 CommandLine::ForCurrentProcess()->AppendSwitch( 63 switches::kDisablePopupBlocking); 64 CommandLine::ForCurrentProcess()->AppendSwitch( 65 extensions::switches::kAllowHTTPBackgroundPage); 66 } 67 68 // Helper function to test that independent tabs of the named app are loaded 69 // into separate processes. 70 void TestAppInstancesHelper(const std::string& app_name) { 71 LOG(INFO) << "Start of test."; 72 73 extensions::ProcessMap* process_map = 74 extensions::ProcessMap::Get(browser()->profile()); 75 76 host_resolver()->AddRule("*", "127.0.0.1"); 77 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 78 79 ASSERT_TRUE(LoadExtension( 80 test_data_dir_.AppendASCII(app_name))); 81 const Extension* extension = GetSingleLoadedExtension(); 82 83 // Open two tabs in the app, one outside it. 84 GURL base_url = GetTestBaseURL(app_name); 85 86 // Test both opening a URL in a new tab, and opening a tab and then 87 // navigating it. Either way, app tabs should be considered extension 88 // processes, but they have no elevated privileges and thus should not 89 // have WebUI bindings. 90 ui_test_utils::NavigateToURLWithDisposition( 91 browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB, 92 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 93 LOG(INFO) << "Nav 1."; 94 EXPECT_TRUE(process_map->Contains( 95 browser()->tab_strip_model()->GetWebContentsAt(1)-> 96 GetRenderProcessHost()->GetID())); 97 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI()); 98 99 content::WindowedNotificationObserver tab_added_observer( 100 chrome::NOTIFICATION_TAB_ADDED, 101 content::NotificationService::AllSources()); 102 chrome::NewTab(browser()); 103 tab_added_observer.Wait(); 104 LOG(INFO) << "New tab."; 105 ui_test_utils::NavigateToURL(browser(), 106 base_url.Resolve("path2/empty.html")); 107 LOG(INFO) << "Nav 2."; 108 EXPECT_TRUE(process_map->Contains( 109 browser()->tab_strip_model()->GetWebContentsAt(2)-> 110 GetRenderProcessHost()->GetID())); 111 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI()); 112 113 // We should have opened 2 new extension tabs. Including the original blank 114 // tab, we now have 3 tabs. The two app tabs should not be in the same 115 // process, since they do not have the background permission. (Thus, we 116 // want to separate them to improve responsiveness.) 117 ASSERT_EQ(3, browser()->tab_strip_model()->count()); 118 WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(1); 119 WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(2); 120 EXPECT_NE(tab1->GetRenderProcessHost(), tab2->GetRenderProcessHost()); 121 122 // Opening tabs with window.open should keep the page in the opener's 123 // process. 124 ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(), 125 browser()->host_desktop_type())); 126 OpenWindow(tab1, base_url.Resolve("path1/empty.html"), true, NULL); 127 LOG(INFO) << "WindowOpenHelper 1."; 128 OpenWindow(tab2, base_url.Resolve("path2/empty.html"), true, NULL); 129 LOG(INFO) << "End of test."; 130 UnloadExtension(extension->id()); 131 } 132}; 133 134// Omits the disable-popup-blocking flag so we can cover that case. 135class BlockedAppApiTest : public AppApiTest { 136 protected: 137 virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE { 138 ExtensionApiTest::SetUpCommandLine(command_line); 139 CommandLine::ForCurrentProcess()->AppendSwitch( 140 extensions::switches::kAllowHTTPBackgroundPage); 141 } 142}; 143 144// Tests that hosted apps with the background permission get a process-per-app 145// model, since all pages need to be able to script the background page. 146// http://crbug.com/172750 147IN_PROC_BROWSER_TEST_F(AppApiTest, DISABLED_AppProcess) { 148 LOG(INFO) << "Start of test."; 149 150 extensions::ProcessMap* process_map = 151 extensions::ProcessMap::Get(browser()->profile()); 152 153 host_resolver()->AddRule("*", "127.0.0.1"); 154 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 155 156 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process"))); 157 158 LOG(INFO) << "Loaded extension."; 159 160 // Open two tabs in the app, one outside it. 161 GURL base_url = GetTestBaseURL("app_process"); 162 163 // Test both opening a URL in a new tab, and opening a tab and then navigating 164 // it. Either way, app tabs should be considered extension processes, but 165 // they have no elevated privileges and thus should not have WebUI bindings. 166 ui_test_utils::NavigateToURLWithDisposition( 167 browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB, 168 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 169 EXPECT_TRUE(process_map->Contains( 170 browser()->tab_strip_model()->GetWebContentsAt(1)-> 171 GetRenderProcessHost()->GetID())); 172 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI()); 173 LOG(INFO) << "Nav 1."; 174 175 ui_test_utils::NavigateToURLWithDisposition( 176 browser(), base_url.Resolve("path2/empty.html"), NEW_FOREGROUND_TAB, 177 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 178 EXPECT_TRUE(process_map->Contains( 179 browser()->tab_strip_model()->GetWebContentsAt(2)-> 180 GetRenderProcessHost()->GetID())); 181 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI()); 182 LOG(INFO) << "Nav 2."; 183 184 content::WindowedNotificationObserver tab_added_observer( 185 chrome::NOTIFICATION_TAB_ADDED, 186 content::NotificationService::AllSources()); 187 chrome::NewTab(browser()); 188 tab_added_observer.Wait(); 189 LOG(INFO) << "New tab."; 190 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path3/empty.html")); 191 LOG(INFO) << "Nav 3."; 192 EXPECT_FALSE(process_map->Contains( 193 browser()->tab_strip_model()->GetWebContentsAt(3)-> 194 GetRenderProcessHost()->GetID())); 195 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(3)->GetWebUI()); 196 197 // We should have opened 3 new extension tabs. Including the original blank 198 // tab, we now have 4 tabs. Because the app_process app has the background 199 // permission, all of its instances are in the same process. Thus two tabs 200 // should be part of the extension app and grouped in the same process. 201 ASSERT_EQ(4, browser()->tab_strip_model()->count()); 202 WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(1); 203 204 EXPECT_EQ(tab->GetRenderProcessHost(), 205 browser()->tab_strip_model()->GetWebContentsAt(2)-> 206 GetRenderProcessHost()); 207 EXPECT_NE(tab->GetRenderProcessHost(), 208 browser()->tab_strip_model()->GetWebContentsAt(3)-> 209 GetRenderProcessHost()); 210 211 // Now let's do the same using window.open. The same should happen. 212 ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(), 213 browser()->host_desktop_type())); 214 OpenWindow(tab, base_url.Resolve("path1/empty.html"), true, NULL); 215 LOG(INFO) << "WindowOpenHelper 1."; 216 OpenWindow(tab, base_url.Resolve("path2/empty.html"), true, NULL); 217 LOG(INFO) << "WindowOpenHelper 2."; 218 // TODO(creis): This should open in a new process (i.e., false for the last 219 // argument), but we temporarily avoid swapping processes away from a hosted 220 // app if it has an opener, because some OAuth providers make script calls 221 // between non-app popups and non-app iframes in the app process. 222 // See crbug.com/59285. 223 OpenWindow(tab, base_url.Resolve("path3/empty.html"), true, NULL); 224 LOG(INFO) << "WindowOpenHelper 3."; 225 226 // Now let's have these pages navigate, into or out of the extension web 227 // extent. They should switch processes. 228 const GURL& app_url(base_url.Resolve("path1/empty.html")); 229 const GURL& non_app_url(base_url.Resolve("path3/empty.html")); 230 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2), 231 non_app_url); 232 LOG(INFO) << "NavigateTabHelper 1."; 233 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(3), 234 app_url); 235 LOG(INFO) << "NavigateTabHelper 2."; 236 EXPECT_NE(tab->GetRenderProcessHost(), 237 browser()->tab_strip_model()->GetWebContentsAt(2)-> 238 GetRenderProcessHost()); 239 EXPECT_EQ(tab->GetRenderProcessHost(), 240 browser()->tab_strip_model()->GetWebContentsAt(3)-> 241 GetRenderProcessHost()); 242 243 // If one of the popup tabs navigates back to the app, window.opener should 244 // be valid. 245 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(6), 246 app_url); 247 LOG(INFO) << "NavigateTabHelper 3."; 248 EXPECT_EQ(tab->GetRenderProcessHost(), 249 browser()->tab_strip_model()->GetWebContentsAt(6)-> 250 GetRenderProcessHost()); 251 bool windowOpenerValid = false; 252 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 253 browser()->tab_strip_model()->GetWebContentsAt(6), 254 "window.domAutomationController.send(window.opener != null)", 255 &windowOpenerValid)); 256 ASSERT_TRUE(windowOpenerValid); 257 258 LOG(INFO) << "End of test."; 259} 260 261// Test that hosted apps without the background permission use a process per app 262// instance model, such that separate instances are in separate processes. 263// Flaky on Windows. http://crbug.com/248047 264#if defined(OS_WIN) 265#define MAYBE_AppProcessInstances DISABLED_AppProcessInstances 266#else 267#define MAYBE_AppProcessInstances AppProcessInstances 268#endif 269IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessInstances) { 270 TestAppInstancesHelper("app_process_instances"); 271} 272 273// Test that hosted apps with the background permission but that set 274// allow_js_access to false also use a process per app instance model. 275// Separate instances should be in separate processes. 276// Flaky on XP: http://crbug.com/165834 277#if defined(OS_WIN) 278#define MAYBE_AppProcessBackgroundInstances \ 279 DISABLED_AppProcessBackgroundInstances 280#else 281#define MAYBE_AppProcessBackgroundInstances AppProcessBackgroundInstances 282#endif 283IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessBackgroundInstances) { 284 TestAppInstancesHelper("app_process_background_instances"); 285} 286 287// Tests that bookmark apps do not use the app process model and are treated 288// like normal web pages instead. http://crbug.com/104636. 289// Timing out on Windows. http://crbug.com/238777 290#if defined(OS_WIN) 291#define MAYBE_BookmarkAppGetsNormalProcess DISABLED_BookmarkAppGetsNormalProcess 292#else 293#define MAYBE_BookmarkAppGetsNormalProcess BookmarkAppGetsNormalProcess 294#endif 295IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_BookmarkAppGetsNormalProcess) { 296 ExtensionService* service = extensions::ExtensionSystem::Get( 297 browser()->profile())->extension_service(); 298 extensions::ProcessMap* process_map = 299 extensions::ProcessMap::Get(browser()->profile()); 300 301 host_resolver()->AddRule("*", "127.0.0.1"); 302 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 303 GURL base_url = GetTestBaseURL("app_process"); 304 305 // Load an app as a bookmark app. 306 std::string error; 307 scoped_refptr<const Extension> extension(extensions::file_util::LoadExtension( 308 test_data_dir_.AppendASCII("app_process"), 309 extensions::Manifest::UNPACKED, 310 Extension::FROM_BOOKMARK, 311 &error)); 312 service->OnExtensionInstalled(extension.get(), 313 syncer::StringOrdinal::CreateInitialOrdinal(), 314 extensions::kInstallFlagInstallImmediately); 315 ASSERT_TRUE(extension.get()); 316 ASSERT_TRUE(extension->from_bookmark()); 317 318 // Test both opening a URL in a new tab, and opening a tab and then navigating 319 // it. Either way, bookmark app tabs should be considered normal processes 320 // with no elevated privileges and no WebUI bindings. 321 ui_test_utils::NavigateToURLWithDisposition( 322 browser(), base_url.Resolve("path1/empty.html"), NEW_FOREGROUND_TAB, 323 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 324 EXPECT_FALSE(process_map->Contains( 325 browser()->tab_strip_model()->GetWebContentsAt(1)-> 326 GetRenderProcessHost()->GetID())); 327 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI()); 328 329 content::WindowedNotificationObserver tab_added_observer( 330 chrome::NOTIFICATION_TAB_ADDED, 331 content::NotificationService::AllSources()); 332 chrome::NewTab(browser()); 333 tab_added_observer.Wait(); 334 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path2/empty.html")); 335 EXPECT_FALSE(process_map->Contains( 336 browser()->tab_strip_model()->GetWebContentsAt(2)-> 337 GetRenderProcessHost()->GetID())); 338 EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI()); 339 340 // We should have opened 2 new bookmark app tabs. Including the original blank 341 // tab, we now have 3 tabs. Because normal pages use the 342 // process-per-site-instance model, each should be in its own process. 343 ASSERT_EQ(3, browser()->tab_strip_model()->count()); 344 WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(1); 345 EXPECT_NE(tab->GetRenderProcessHost(), 346 browser()->tab_strip_model()->GetWebContentsAt(2)-> 347 GetRenderProcessHost()); 348 349 // Now let's do the same using window.open. The same should happen. 350 ASSERT_EQ(1u, chrome::GetBrowserCount(browser()->profile(), 351 browser()->host_desktop_type())); 352 OpenWindow(tab, base_url.Resolve("path1/empty.html"), true, NULL); 353 OpenWindow(tab, base_url.Resolve("path2/empty.html"), true, NULL); 354 355 // Now let's have a tab navigate out of and back into the app's web 356 // extent. Neither navigation should switch processes. 357 const GURL& app_url(base_url.Resolve("path1/empty.html")); 358 const GURL& non_app_url(base_url.Resolve("path3/empty.html")); 359 RenderViewHost* host2 = 360 browser()->tab_strip_model()->GetWebContentsAt(2)->GetRenderViewHost(); 361 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2), 362 non_app_url); 363 EXPECT_EQ(host2->GetProcess(), 364 browser()->tab_strip_model()->GetWebContentsAt(2)-> 365 GetRenderProcessHost()); 366 NavigateInRenderer(browser()->tab_strip_model()->GetWebContentsAt(2), 367 app_url); 368 EXPECT_EQ(host2->GetProcess(), 369 browser()->tab_strip_model()->GetWebContentsAt(2)-> 370 GetRenderProcessHost()); 371} 372 373// Tests that app process switching works properly in the following scenario: 374// 1. navigate to a page1 in the app 375// 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect") 376// 3. page2 redirects back to a page in the app 377// The final navigation should end up in the app process. 378// See http://crbug.com/61757 379// Flaky. http://crbug.com/341898 380IN_PROC_BROWSER_TEST_F(AppApiTest, DISABLED_AppProcessRedirectBack) { 381 host_resolver()->AddRule("*", "127.0.0.1"); 382 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 383 384 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process"))); 385 386 // Open two tabs in the app. 387 GURL base_url = GetTestBaseURL("app_process"); 388 389 chrome::NewTab(browser()); 390 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 391 chrome::NewTab(browser()); 392 // Wait until the second tab finishes its redirect train (2 hops). 393 // 1. We navigate to redirect.html 394 // 2. Renderer navigates and finishes, counting as a load stop. 395 // 3. Renderer issues the meta refresh to navigate to server-redirect. 396 // 4. Renderer is now in a "provisional load", waiting for navigation to 397 // complete. 398 // 5. Browser sees a redirect response from server-redirect to empty.html, and 399 // transfers that to a new navigation, using RequestTransferURL. 400 // 6. Renderer navigates to empty.html, and finishes loading, counting as the 401 // second load stop 402 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( 403 browser(), base_url.Resolve("path1/redirect.html"), 2); 404 405 // 3 tabs, including the initial about:blank. The last 2 should be the same 406 // process. 407 ASSERT_EQ(3, browser()->tab_strip_model()->count()); 408 EXPECT_EQ("/extensions/api_test/app_process/path1/empty.html", 409 browser()->tab_strip_model()->GetWebContentsAt(2)-> 410 GetController().GetLastCommittedEntry()->GetURL().path()); 411 EXPECT_EQ(browser()->tab_strip_model()->GetWebContentsAt(1)-> 412 GetRenderProcessHost(), 413 browser()->tab_strip_model()->GetWebContentsAt(2)-> 414 GetRenderProcessHost()); 415} 416 417// Ensure that re-navigating to a URL after installing or uninstalling it as an 418// app correctly swaps the tab to the app process. (http://crbug.com/80621) 419// 420// Fails on Windows. http://crbug.com/238670 421// Added logging to help diagnose the location of the problem. 422IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) { 423 extensions::ProcessMap* process_map = 424 extensions::ProcessMap::Get(browser()->profile()); 425 426 host_resolver()->AddRule("*", "127.0.0.1"); 427 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 428 429 // The app under test acts on URLs whose host is "localhost", 430 // so the URLs we navigate to must have host "localhost". 431 GURL base_url = GetTestBaseURL("app_process"); 432 433 // Load an app URL before loading the app. 434 LOG(INFO) << "Loading path1/empty.html."; 435 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 436 LOG(INFO) << "Loading path1/empty.html - done."; 437 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 438 EXPECT_FALSE(process_map->Contains( 439 contents->GetRenderProcessHost()->GetID())); 440 441 // Load app and re-navigate to the page. 442 LOG(INFO) << "Loading extension."; 443 const Extension* app = 444 LoadExtension(test_data_dir_.AppendASCII("app_process")); 445 LOG(INFO) << "Loading extension - done."; 446 ASSERT_TRUE(app); 447 LOG(INFO) << "Loading path1/empty.html."; 448 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 449 LOG(INFO) << "Loading path1/empty.html - done."; 450 EXPECT_TRUE(process_map->Contains( 451 contents->GetRenderProcessHost()->GetID())); 452 453 // Disable app and re-navigate to the page. 454 LOG(INFO) << "Disabling extension."; 455 DisableExtension(app->id()); 456 LOG(INFO) << "Disabling extension - done."; 457 LOG(INFO) << "Loading path1/empty.html."; 458 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 459 LOG(INFO) << "Loading path1/empty.html - done."; 460 EXPECT_FALSE(process_map->Contains( 461 contents->GetRenderProcessHost()->GetID())); 462} 463 464// Ensure that reloading a URL after installing or uninstalling it as an app 465// correctly swaps the tab to the app process. (http://crbug.com/80621) 466// 467// Added logging to help diagnose the location of the problem. 468// http://crbug.com/238670 469IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) { 470 extensions::ProcessMap* process_map = 471 extensions::ProcessMap::Get(browser()->profile()); 472 473 host_resolver()->AddRule("*", "127.0.0.1"); 474 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 475 476 // The app under test acts on URLs whose host is "localhost", 477 // so the URLs we navigate to must have host "localhost". 478 GURL base_url = GetTestBaseURL("app_process"); 479 480 // Load app, disable it, and navigate to the page. 481 LOG(INFO) << "Loading extension."; 482 const Extension* app = 483 LoadExtension(test_data_dir_.AppendASCII("app_process")); 484 LOG(INFO) << "Loading extension - done."; 485 ASSERT_TRUE(app); 486 LOG(INFO) << "Disabling extension."; 487 DisableExtension(app->id()); 488 LOG(INFO) << "Disabling extension - done."; 489 LOG(INFO) << "Navigate to path1/empty.html."; 490 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 491 LOG(INFO) << "Navigate to path1/empty.html - done."; 492 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 493 EXPECT_FALSE(process_map->Contains( 494 contents->GetRenderProcessHost()->GetID())); 495 496 // Enable app and reload the page. 497 LOG(INFO) << "Enabling extension."; 498 EnableExtension(app->id()); 499 LOG(INFO) << "Enabling extension - done."; 500 content::WindowedNotificationObserver reload_observer( 501 content::NOTIFICATION_LOAD_STOP, 502 content::Source<NavigationController>( 503 &browser()->tab_strip_model()->GetActiveWebContents()-> 504 GetController())); 505 LOG(INFO) << "Reloading."; 506 chrome::Reload(browser(), CURRENT_TAB); 507 reload_observer.Wait(); 508 LOG(INFO) << "Reloading - done."; 509 EXPECT_TRUE(process_map->Contains( 510 contents->GetRenderProcessHost()->GetID())); 511 512 // Disable app and reload the page. 513 LOG(INFO) << "Disabling extension."; 514 DisableExtension(app->id()); 515 LOG(INFO) << "Disabling extension - done."; 516 content::WindowedNotificationObserver reload_observer2( 517 content::NOTIFICATION_LOAD_STOP, 518 content::Source<NavigationController>( 519 &browser()->tab_strip_model()->GetActiveWebContents()-> 520 GetController())); 521 LOG(INFO) << "Reloading."; 522 chrome::Reload(browser(), CURRENT_TAB); 523 reload_observer2.Wait(); 524 LOG(INFO) << "Reloading - done."; 525 EXPECT_FALSE(process_map->Contains( 526 contents->GetRenderProcessHost()->GetID())); 527} 528 529// Ensure that reloading a URL with JavaScript after installing or uninstalling 530// it as an app correctly swaps the process. (http://crbug.com/80621) 531// 532// Crashes on Windows and Mac. http://crbug.com/238670 533// Added logging to help diagnose the location of the problem. 534IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) { 535 extensions::ProcessMap* process_map = 536 extensions::ProcessMap::Get(browser()->profile()); 537 538 host_resolver()->AddRule("*", "127.0.0.1"); 539 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 540 541 // The app under test acts on URLs whose host is "localhost", 542 // so the URLs we navigate to must have host "localhost". 543 GURL base_url = GetTestBaseURL("app_process"); 544 545 // Load app, disable it, and navigate to the page. 546 LOG(INFO) << "Loading extension."; 547 const Extension* app = 548 LoadExtension(test_data_dir_.AppendASCII("app_process")); 549 LOG(INFO) << "Loading extension - done."; 550 ASSERT_TRUE(app); 551 LOG(INFO) << "Disabling extension."; 552 DisableExtension(app->id()); 553 LOG(INFO) << "Disabling extension - done."; 554 LOG(INFO) << "Navigate to path1/empty.html."; 555 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 556 LOG(INFO) << "Navigate to path1/empty.html - done."; 557 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 558 EXPECT_FALSE(process_map->Contains( 559 contents->GetRenderProcessHost()->GetID())); 560 561 // Enable app and reload via JavaScript. 562 LOG(INFO) << "Enabling extension."; 563 EnableExtension(app->id()); 564 LOG(INFO) << "Enabling extension - done."; 565 content::WindowedNotificationObserver js_reload_observer( 566 content::NOTIFICATION_LOAD_STOP, 567 content::Source<NavigationController>( 568 &browser()->tab_strip_model()->GetActiveWebContents()-> 569 GetController())); 570 LOG(INFO) << "Executing location.reload()."; 571 ASSERT_TRUE(content::ExecuteScript(contents, "location.reload();")); 572 js_reload_observer.Wait(); 573 LOG(INFO) << "Executing location.reload() - done."; 574 EXPECT_TRUE(process_map->Contains( 575 contents->GetRenderProcessHost()->GetID())); 576 577 // Disable app and reload via JavaScript. 578 LOG(INFO) << "Disabling extension."; 579 DisableExtension(app->id()); 580 LOG(INFO) << "Disabling extension - done."; 581 content::WindowedNotificationObserver js_reload_observer2( 582 content::NOTIFICATION_LOAD_STOP, 583 content::Source<NavigationController>( 584 &browser()->tab_strip_model()->GetActiveWebContents()-> 585 GetController())); 586 LOG(INFO) << "Executing location = location."; 587 ASSERT_TRUE(content::ExecuteScript(contents, "location = location;")); 588 js_reload_observer2.Wait(); 589 LOG(INFO) << "Executing location = location - done."; 590 EXPECT_FALSE(process_map->Contains( 591 contents->GetRenderProcessHost()->GetID())); 592} 593 594// Tests that if we have a non-app process (path3/container.html) that has an 595// iframe with a URL in the app's extent (path1/iframe.html), then opening a 596// link from that iframe to a new window to a URL in the app's extent (path1/ 597// empty.html) results in the new window being in an app process. See 598// http://crbug.com/89272 for more details. 599IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) { 600#if defined(OS_WIN) && defined(USE_ASH) 601 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 602 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 603 return; 604#endif 605 606 extensions::ProcessMap* process_map = 607 extensions::ProcessMap::Get(browser()->profile()); 608 609 host_resolver()->AddRule("*", "127.0.0.1"); 610 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 611 612 GURL base_url = GetTestBaseURL("app_process"); 613 614 // Load app and start URL (not in the app). 615 const Extension* app = 616 LoadExtension(test_data_dir_.AppendASCII("app_process")); 617 ASSERT_TRUE(app); 618 619 ui_test_utils::NavigateToURL(browser(), 620 base_url.Resolve("path3/container.html")); 621 EXPECT_FALSE(process_map->Contains( 622 browser()->tab_strip_model()->GetWebContentsAt(0)-> 623 GetRenderProcessHost()->GetID())); 624 625 const BrowserList* active_browser_list = 626 BrowserList::GetInstance(chrome::GetActiveDesktop()); 627 EXPECT_EQ(2U, active_browser_list->size()); 628 content::WebContents* popup_contents = 629 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents(); 630 content::WaitForLoadStop(popup_contents); 631 632 // Popup window should be in the app's process. 633 RenderViewHost* popup_host = popup_contents->GetRenderViewHost(); 634 EXPECT_TRUE(process_map->Contains(popup_host->GetProcess()->GetID())); 635} 636 637// Similar to the previous test, but ensure that popup blocking bypass 638// isn't granted to the iframe. See crbug.com/117446. 639#if defined(OS_CHROMEOS) 640// http://crbug.com/153513 641#define MAYBE_OpenAppFromIframe DISABLED_OpenAppFromIframe 642#else 643#define MAYBE_OpenAppFromIframe OpenAppFromIframe 644#endif 645IN_PROC_BROWSER_TEST_F(BlockedAppApiTest, MAYBE_OpenAppFromIframe) { 646 host_resolver()->AddRule("*", "127.0.0.1"); 647 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 648 649 // Load app and start URL (not in the app). 650 const Extension* app = 651 LoadExtension(test_data_dir_.AppendASCII("app_process")); 652 ASSERT_TRUE(app); 653 654 ui_test_utils::NavigateToURL( 655 browser(), GetTestBaseURL("app_process").Resolve("path3/container.html")); 656 657 WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); 658 PopupBlockerTabHelper* popup_blocker_tab_helper = 659 PopupBlockerTabHelper::FromWebContents(tab); 660 if (!popup_blocker_tab_helper->GetBlockedPopupsCount()) { 661 content::WindowedNotificationObserver observer( 662 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, 663 content::NotificationService::AllSources()); 664 observer.Wait(); 665 } 666 667 EXPECT_EQ(1u, popup_blocker_tab_helper->GetBlockedPopupsCount()); 668} 669 670// Tests that if an extension launches an app via chrome.tabs.create with an URL 671// that's not in the app's extent but that server redirects to it, we still end 672// up with an app process. See http://crbug.com/99349 for more details. 673IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) { 674 host_resolver()->AddRule("*", "127.0.0.1"); 675 ASSERT_TRUE(StartEmbeddedTestServer()); 676 677 LoadExtension(test_data_dir_.AppendASCII("app_process")); 678 const Extension* launcher = 679 LoadExtension(test_data_dir_.AppendASCII("app_launcher")); 680 681 // There should be two navigations by the time the app page is loaded. 682 // 1. The extension launcher page. 683 // 2. The app's URL (which includes a server redirect). 684 // Note that the server redirect does not generate a navigation event. 685 content::TestNavigationObserver test_navigation_observer( 686 browser()->tab_strip_model()->GetActiveWebContents(), 687 2); 688 test_navigation_observer.StartWatchingNewWebContents(); 689 690 // Load the launcher extension, which should launch the app. 691 ui_test_utils::NavigateToURLWithDisposition( 692 browser(), 693 launcher->GetResourceURL("server_redirect.html"), 694 CURRENT_TAB, 695 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 696 697 // Wait for app tab to be created and loaded. 698 test_navigation_observer.Wait(); 699 700 // App has loaded, and chrome.app.isInstalled should be true. 701 bool is_installed = false; 702 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 703 browser()->tab_strip_model()->GetActiveWebContents(), 704 "window.domAutomationController.send(chrome.app.isInstalled)", 705 &is_installed)); 706 ASSERT_TRUE(is_installed); 707} 708 709// Tests that if an extension launches an app via chrome.tabs.create with an URL 710// that's not in the app's extent but that client redirects to it, we still end 711// up with an app process. 712IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) { 713 host_resolver()->AddRule("*", "127.0.0.1"); 714 ASSERT_TRUE(StartEmbeddedTestServer()); 715 716 LoadExtension(test_data_dir_.AppendASCII("app_process")); 717 const Extension* launcher = 718 LoadExtension(test_data_dir_.AppendASCII("app_launcher")); 719 720 // There should be three navigations by the time the app page is loaded. 721 // 1. The extension launcher page. 722 // 2. The URL that the extension launches, which client redirects. 723 // 3. The app's URL. 724 content::TestNavigationObserver test_navigation_observer( 725 browser()->tab_strip_model()->GetActiveWebContents(), 726 3); 727 test_navigation_observer.StartWatchingNewWebContents(); 728 729 // Load the launcher extension, which should launch the app. 730 ui_test_utils::NavigateToURLWithDisposition( 731 browser(), 732 launcher->GetResourceURL("client_redirect.html"), 733 CURRENT_TAB, 734 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 735 736 // Wait for app tab to be created and loaded. 737 test_navigation_observer.Wait(); 738 739 // App has loaded, and chrome.app.isInstalled should be true. 740 bool is_installed = false; 741 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 742 browser()->tab_strip_model()->GetActiveWebContents(), 743 "window.domAutomationController.send(chrome.app.isInstalled)", 744 &is_installed)); 745 ASSERT_TRUE(is_installed); 746} 747 748// Tests that if we have an app process (path1/container.html) with a non-app 749// iframe (path3/iframe.html), then opening a link from that iframe to a new 750// window to a same-origin non-app URL (path3/empty.html) should keep the window 751// in the app process. 752// This is in contrast to OpenAppFromIframe, since here the popup will not be 753// missing special permissions and should be scriptable from the iframe. 754// See http://crbug.com/92669 for more details. 755IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) { 756 extensions::ProcessMap* process_map = 757 extensions::ProcessMap::Get(browser()->profile()); 758 759 host_resolver()->AddRule("*", "127.0.0.1"); 760 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 761 762 GURL base_url = GetTestBaseURL("app_process"); 763 764 // Load app and start URL (in the app). 765 const Extension* app = 766 LoadExtension(test_data_dir_.AppendASCII("app_process")); 767 ASSERT_TRUE(app); 768 769 ui_test_utils::NavigateToURL(browser(), 770 base_url.Resolve("path1/container.html")); 771 content::RenderProcessHost* process = 772 browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost(); 773 EXPECT_TRUE(process_map->Contains(process->GetID())); 774 775 // Popup window should be in the app's process. 776 const BrowserList* active_browser_list = 777 BrowserList::GetInstance(chrome::GetActiveDesktop()); 778 EXPECT_EQ(2U, active_browser_list->size()); 779 content::WebContents* popup_contents = 780 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents(); 781 content::WaitForLoadStop(popup_contents); 782 783 RenderViewHost* popup_host = popup_contents->GetRenderViewHost(); 784 EXPECT_EQ(process, popup_host->GetProcess()); 785} 786 787// http://crbug.com/118502 788#if defined(OS_MACOSX) || defined(OS_LINUX) 789#define MAYBE_ReloadAppAfterCrash DISABLED_ReloadAppAfterCrash 790#else 791#define MAYBE_ReloadAppAfterCrash ReloadAppAfterCrash 792#endif 793IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_ReloadAppAfterCrash) { 794 extensions::ProcessMap* process_map = 795 extensions::ProcessMap::Get(browser()->profile()); 796 797 host_resolver()->AddRule("*", "127.0.0.1"); 798 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 799 800 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process"))); 801 802 GURL base_url = GetTestBaseURL("app_process"); 803 804 // Load the app, chrome.app.isInstalled should be true. 805 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 806 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 807 EXPECT_TRUE(process_map->Contains( 808 contents->GetRenderProcessHost()->GetID())); 809 bool is_installed = false; 810 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 811 contents, 812 "window.domAutomationController.send(chrome.app.isInstalled)", 813 &is_installed)); 814 ASSERT_TRUE(is_installed); 815 816 // Crash the tab and reload it, chrome.app.isInstalled should still be true. 817 content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents()); 818 content::WindowedNotificationObserver observer( 819 content::NOTIFICATION_LOAD_STOP, 820 content::Source<NavigationController>( 821 &browser()->tab_strip_model()->GetActiveWebContents()-> 822 GetController())); 823 chrome::Reload(browser(), CURRENT_TAB); 824 observer.Wait(); 825 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 826 contents, 827 "window.domAutomationController.send(chrome.app.isInstalled)", 828 &is_installed)); 829 ASSERT_TRUE(is_installed); 830} 831 832// Test that a cross-process navigation away from a hosted app stays in the same 833// BrowsingInstance, so that postMessage calls to the app's other windows still 834// work. 835IN_PROC_BROWSER_TEST_F(AppApiTest, SameBrowsingInstanceAfterSwap) { 836 extensions::ProcessMap* process_map = 837 extensions::ProcessMap::Get(browser()->profile()); 838 839 host_resolver()->AddRule("*", "127.0.0.1"); 840 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 841 842 GURL base_url = GetTestBaseURL("app_process"); 843 844 // Load app and start URL (in the app). 845 const Extension* app = 846 LoadExtension(test_data_dir_.AppendASCII("app_process")); 847 ASSERT_TRUE(app); 848 849 ui_test_utils::NavigateToURL(browser(), 850 base_url.Resolve("path1/iframe.html")); 851 content::SiteInstance* app_instance = 852 browser()->tab_strip_model()->GetWebContentsAt(0)->GetSiteInstance(); 853 EXPECT_TRUE(process_map->Contains(app_instance->GetProcess()->GetID())); 854 855 // Popup window should be in the app's process. 856 const BrowserList* active_browser_list = 857 BrowserList::GetInstance(chrome::GetActiveDesktop()); 858 EXPECT_EQ(2U, active_browser_list->size()); 859 content::WebContents* popup_contents = 860 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents(); 861 content::WaitForLoadStop(popup_contents); 862 863 SiteInstance* popup_instance = popup_contents->GetSiteInstance(); 864 EXPECT_EQ(app_instance, popup_instance); 865 866 // Navigate the popup to another process outside the app. 867 GURL non_app_url(base_url.Resolve("path3/empty.html")); 868 ui_test_utils::NavigateToURL(active_browser_list->get(1), non_app_url); 869 SiteInstance* new_instance = popup_contents->GetSiteInstance(); 870 EXPECT_NE(app_instance, new_instance); 871 872 // It should still be in the same BrowsingInstance, allowing postMessage to 873 // work. 874 EXPECT_TRUE(app_instance->IsRelatedSiteInstance(new_instance)); 875} 876