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 39 using content::NavigationController; 40 using content::RenderViewHost; 41 using content::SiteInstance; 42 using content::WebContents; 43 using extensions::Extension; 44 45 class 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. 135 class 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 147 IN_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 269 IN_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 283 IN_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 295 IN_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 on Linux. http://crbug.com/341898 380 #if defined(OS_LINUX) 381 #define MAYBE_AppProcessRedirectBack DISABLED_AppProcessRedirectBack 382 #else 383 #define MAYBE_AppProcessRedirectBack AppProcessRedirectBack 384 #endif 385 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_AppProcessRedirectBack) { 386 host_resolver()->AddRule("*", "127.0.0.1"); 387 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 388 389 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process"))); 390 391 // Open two tabs in the app. 392 GURL base_url = GetTestBaseURL("app_process"); 393 394 chrome::NewTab(browser()); 395 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 396 chrome::NewTab(browser()); 397 // Wait until the second tab finishes its redirect train (2 hops). 398 // 1. We navigate to redirect.html 399 // 2. Renderer navigates and finishes, counting as a load stop. 400 // 3. Renderer issues the meta refresh to navigate to server-redirect. 401 // 4. Renderer is now in a "provisional load", waiting for navigation to 402 // complete. 403 // 5. Browser sees a redirect response from server-redirect to empty.html, and 404 // transfers that to a new navigation, using RequestTransferURL. 405 // 6. Renderer navigates to empty.html, and finishes loading, counting as the 406 // second load stop 407 ui_test_utils::NavigateToURLBlockUntilNavigationsComplete( 408 browser(), base_url.Resolve("path1/redirect.html"), 2); 409 410 // 3 tabs, including the initial about:blank. The last 2 should be the same 411 // process. 412 ASSERT_EQ(3, browser()->tab_strip_model()->count()); 413 EXPECT_EQ("/extensions/api_test/app_process/path1/empty.html", 414 browser()->tab_strip_model()->GetWebContentsAt(2)-> 415 GetController().GetLastCommittedEntry()->GetURL().path()); 416 EXPECT_EQ(browser()->tab_strip_model()->GetWebContentsAt(1)-> 417 GetRenderProcessHost(), 418 browser()->tab_strip_model()->GetWebContentsAt(2)-> 419 GetRenderProcessHost()); 420 } 421 422 // Ensure that re-navigating to a URL after installing or uninstalling it as an 423 // app correctly swaps the tab to the app process. (http://crbug.com/80621) 424 // 425 // Fails on Windows. http://crbug.com/238670 426 // Added logging to help diagnose the location of the problem. 427 IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) { 428 extensions::ProcessMap* process_map = 429 extensions::ProcessMap::Get(browser()->profile()); 430 431 host_resolver()->AddRule("*", "127.0.0.1"); 432 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 433 434 // The app under test acts on URLs whose host is "localhost", 435 // so the URLs we navigate to must have host "localhost". 436 GURL base_url = GetTestBaseURL("app_process"); 437 438 // Load an app URL before loading the app. 439 LOG(INFO) << "Loading path1/empty.html."; 440 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 441 LOG(INFO) << "Loading path1/empty.html - done."; 442 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 443 EXPECT_FALSE(process_map->Contains( 444 contents->GetRenderProcessHost()->GetID())); 445 446 // Load app and re-navigate to the page. 447 LOG(INFO) << "Loading extension."; 448 const Extension* app = 449 LoadExtension(test_data_dir_.AppendASCII("app_process")); 450 LOG(INFO) << "Loading extension - done."; 451 ASSERT_TRUE(app); 452 LOG(INFO) << "Loading path1/empty.html."; 453 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 454 LOG(INFO) << "Loading path1/empty.html - done."; 455 EXPECT_TRUE(process_map->Contains( 456 contents->GetRenderProcessHost()->GetID())); 457 458 // Disable app and re-navigate to the page. 459 LOG(INFO) << "Disabling extension."; 460 DisableExtension(app->id()); 461 LOG(INFO) << "Disabling extension - done."; 462 LOG(INFO) << "Loading path1/empty.html."; 463 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 464 LOG(INFO) << "Loading path1/empty.html - done."; 465 EXPECT_FALSE(process_map->Contains( 466 contents->GetRenderProcessHost()->GetID())); 467 } 468 469 // Ensure that reloading a URL after installing or uninstalling it as an app 470 // correctly swaps the tab to the app process. (http://crbug.com/80621) 471 // 472 // Added logging to help diagnose the location of the problem. 473 // http://crbug.com/238670 474 IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) { 475 extensions::ProcessMap* process_map = 476 extensions::ProcessMap::Get(browser()->profile()); 477 478 host_resolver()->AddRule("*", "127.0.0.1"); 479 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 480 481 // The app under test acts on URLs whose host is "localhost", 482 // so the URLs we navigate to must have host "localhost". 483 GURL base_url = GetTestBaseURL("app_process"); 484 485 // Load app, disable it, and navigate to the page. 486 LOG(INFO) << "Loading extension."; 487 const Extension* app = 488 LoadExtension(test_data_dir_.AppendASCII("app_process")); 489 LOG(INFO) << "Loading extension - done."; 490 ASSERT_TRUE(app); 491 LOG(INFO) << "Disabling extension."; 492 DisableExtension(app->id()); 493 LOG(INFO) << "Disabling extension - done."; 494 LOG(INFO) << "Navigate to path1/empty.html."; 495 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 496 LOG(INFO) << "Navigate to path1/empty.html - done."; 497 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 498 EXPECT_FALSE(process_map->Contains( 499 contents->GetRenderProcessHost()->GetID())); 500 501 // Enable app and reload the page. 502 LOG(INFO) << "Enabling extension."; 503 EnableExtension(app->id()); 504 LOG(INFO) << "Enabling extension - done."; 505 content::WindowedNotificationObserver reload_observer( 506 content::NOTIFICATION_LOAD_STOP, 507 content::Source<NavigationController>( 508 &browser()->tab_strip_model()->GetActiveWebContents()-> 509 GetController())); 510 LOG(INFO) << "Reloading."; 511 chrome::Reload(browser(), CURRENT_TAB); 512 reload_observer.Wait(); 513 LOG(INFO) << "Reloading - done."; 514 EXPECT_TRUE(process_map->Contains( 515 contents->GetRenderProcessHost()->GetID())); 516 517 // Disable app and reload the page. 518 LOG(INFO) << "Disabling extension."; 519 DisableExtension(app->id()); 520 LOG(INFO) << "Disabling extension - done."; 521 content::WindowedNotificationObserver reload_observer2( 522 content::NOTIFICATION_LOAD_STOP, 523 content::Source<NavigationController>( 524 &browser()->tab_strip_model()->GetActiveWebContents()-> 525 GetController())); 526 LOG(INFO) << "Reloading."; 527 chrome::Reload(browser(), CURRENT_TAB); 528 reload_observer2.Wait(); 529 LOG(INFO) << "Reloading - done."; 530 EXPECT_FALSE(process_map->Contains( 531 contents->GetRenderProcessHost()->GetID())); 532 } 533 534 // Ensure that reloading a URL with JavaScript after installing or uninstalling 535 // it as an app correctly swaps the process. (http://crbug.com/80621) 536 // 537 // Crashes on Windows and Mac. http://crbug.com/238670 538 // Added logging to help diagnose the location of the problem. 539 IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) { 540 extensions::ProcessMap* process_map = 541 extensions::ProcessMap::Get(browser()->profile()); 542 543 host_resolver()->AddRule("*", "127.0.0.1"); 544 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 545 546 // The app under test acts on URLs whose host is "localhost", 547 // so the URLs we navigate to must have host "localhost". 548 GURL base_url = GetTestBaseURL("app_process"); 549 550 // Load app, disable it, and navigate to the page. 551 LOG(INFO) << "Loading extension."; 552 const Extension* app = 553 LoadExtension(test_data_dir_.AppendASCII("app_process")); 554 LOG(INFO) << "Loading extension - done."; 555 ASSERT_TRUE(app); 556 LOG(INFO) << "Disabling extension."; 557 DisableExtension(app->id()); 558 LOG(INFO) << "Disabling extension - done."; 559 LOG(INFO) << "Navigate to path1/empty.html."; 560 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 561 LOG(INFO) << "Navigate to path1/empty.html - done."; 562 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 563 EXPECT_FALSE(process_map->Contains( 564 contents->GetRenderProcessHost()->GetID())); 565 566 // Enable app and reload via JavaScript. 567 LOG(INFO) << "Enabling extension."; 568 EnableExtension(app->id()); 569 LOG(INFO) << "Enabling extension - done."; 570 content::WindowedNotificationObserver js_reload_observer( 571 content::NOTIFICATION_LOAD_STOP, 572 content::Source<NavigationController>( 573 &browser()->tab_strip_model()->GetActiveWebContents()-> 574 GetController())); 575 LOG(INFO) << "Executing location.reload()."; 576 ASSERT_TRUE(content::ExecuteScript(contents, "location.reload();")); 577 js_reload_observer.Wait(); 578 LOG(INFO) << "Executing location.reload() - done."; 579 EXPECT_TRUE(process_map->Contains( 580 contents->GetRenderProcessHost()->GetID())); 581 582 // Disable app and reload via JavaScript. 583 LOG(INFO) << "Disabling extension."; 584 DisableExtension(app->id()); 585 LOG(INFO) << "Disabling extension - done."; 586 content::WindowedNotificationObserver js_reload_observer2( 587 content::NOTIFICATION_LOAD_STOP, 588 content::Source<NavigationController>( 589 &browser()->tab_strip_model()->GetActiveWebContents()-> 590 GetController())); 591 LOG(INFO) << "Executing location = location."; 592 ASSERT_TRUE(content::ExecuteScript(contents, "location = location;")); 593 js_reload_observer2.Wait(); 594 LOG(INFO) << "Executing location = location - done."; 595 EXPECT_FALSE(process_map->Contains( 596 contents->GetRenderProcessHost()->GetID())); 597 } 598 599 // Tests that if we have a non-app process (path3/container.html) that has an 600 // iframe with a URL in the app's extent (path1/iframe.html), then opening a 601 // link from that iframe to a new window to a URL in the app's extent (path1/ 602 // empty.html) results in the new window being in an app process. See 603 // http://crbug.com/89272 for more details. 604 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) { 605 #if defined(OS_WIN) && defined(USE_ASH) 606 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 607 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 608 return; 609 #endif 610 611 extensions::ProcessMap* process_map = 612 extensions::ProcessMap::Get(browser()->profile()); 613 614 host_resolver()->AddRule("*", "127.0.0.1"); 615 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 616 617 GURL base_url = GetTestBaseURL("app_process"); 618 619 // Load app and start URL (not in the app). 620 const Extension* app = 621 LoadExtension(test_data_dir_.AppendASCII("app_process")); 622 ASSERT_TRUE(app); 623 624 ui_test_utils::NavigateToURL(browser(), 625 base_url.Resolve("path3/container.html")); 626 EXPECT_FALSE(process_map->Contains( 627 browser()->tab_strip_model()->GetWebContentsAt(0)-> 628 GetRenderProcessHost()->GetID())); 629 630 const BrowserList* active_browser_list = 631 BrowserList::GetInstance(chrome::GetActiveDesktop()); 632 EXPECT_EQ(2U, active_browser_list->size()); 633 content::WebContents* popup_contents = 634 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents(); 635 content::WaitForLoadStop(popup_contents); 636 637 // Popup window should be in the app's process. 638 RenderViewHost* popup_host = popup_contents->GetRenderViewHost(); 639 EXPECT_TRUE(process_map->Contains(popup_host->GetProcess()->GetID())); 640 } 641 642 // Similar to the previous test, but ensure that popup blocking bypass 643 // isn't granted to the iframe. See crbug.com/117446. 644 #if defined(OS_CHROMEOS) 645 // http://crbug.com/153513 646 #define MAYBE_OpenAppFromIframe DISABLED_OpenAppFromIframe 647 #else 648 #define MAYBE_OpenAppFromIframe OpenAppFromIframe 649 #endif 650 IN_PROC_BROWSER_TEST_F(BlockedAppApiTest, MAYBE_OpenAppFromIframe) { 651 host_resolver()->AddRule("*", "127.0.0.1"); 652 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 653 654 // Load app and start URL (not in the app). 655 const Extension* app = 656 LoadExtension(test_data_dir_.AppendASCII("app_process")); 657 ASSERT_TRUE(app); 658 659 ui_test_utils::NavigateToURL( 660 browser(), GetTestBaseURL("app_process").Resolve("path3/container.html")); 661 662 WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents(); 663 PopupBlockerTabHelper* popup_blocker_tab_helper = 664 PopupBlockerTabHelper::FromWebContents(tab); 665 if (!popup_blocker_tab_helper->GetBlockedPopupsCount()) { 666 content::WindowedNotificationObserver observer( 667 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, 668 content::NotificationService::AllSources()); 669 observer.Wait(); 670 } 671 672 EXPECT_EQ(1u, popup_blocker_tab_helper->GetBlockedPopupsCount()); 673 } 674 675 // Tests that if an extension launches an app via chrome.tabs.create with an URL 676 // that's not in the app's extent but that server redirects to it, we still end 677 // up with an app process. See http://crbug.com/99349 for more details. 678 IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) { 679 host_resolver()->AddRule("*", "127.0.0.1"); 680 ASSERT_TRUE(StartEmbeddedTestServer()); 681 682 LoadExtension(test_data_dir_.AppendASCII("app_process")); 683 const Extension* launcher = 684 LoadExtension(test_data_dir_.AppendASCII("app_launcher")); 685 686 // There should be two navigations by the time the app page is loaded. 687 // 1. The extension launcher page. 688 // 2. The app's URL (which includes a server redirect). 689 // Note that the server redirect does not generate a navigation event. 690 content::TestNavigationObserver test_navigation_observer( 691 browser()->tab_strip_model()->GetActiveWebContents(), 692 2); 693 test_navigation_observer.StartWatchingNewWebContents(); 694 695 // Load the launcher extension, which should launch the app. 696 ui_test_utils::NavigateToURLWithDisposition( 697 browser(), 698 launcher->GetResourceURL("server_redirect.html"), 699 CURRENT_TAB, 700 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 701 702 // Wait for app tab to be created and loaded. 703 test_navigation_observer.Wait(); 704 705 // App has loaded, and chrome.app.isInstalled should be true. 706 bool is_installed = false; 707 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 708 browser()->tab_strip_model()->GetActiveWebContents(), 709 "window.domAutomationController.send(chrome.app.isInstalled)", 710 &is_installed)); 711 ASSERT_TRUE(is_installed); 712 } 713 714 // Tests that if an extension launches an app via chrome.tabs.create with an URL 715 // that's not in the app's extent but that client redirects to it, we still end 716 // up with an app process. 717 IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) { 718 host_resolver()->AddRule("*", "127.0.0.1"); 719 ASSERT_TRUE(StartEmbeddedTestServer()); 720 721 LoadExtension(test_data_dir_.AppendASCII("app_process")); 722 const Extension* launcher = 723 LoadExtension(test_data_dir_.AppendASCII("app_launcher")); 724 725 // There should be three navigations by the time the app page is loaded. 726 // 1. The extension launcher page. 727 // 2. The URL that the extension launches, which client redirects. 728 // 3. The app's URL. 729 content::TestNavigationObserver test_navigation_observer( 730 browser()->tab_strip_model()->GetActiveWebContents(), 731 3); 732 test_navigation_observer.StartWatchingNewWebContents(); 733 734 // Load the launcher extension, which should launch the app. 735 ui_test_utils::NavigateToURLWithDisposition( 736 browser(), 737 launcher->GetResourceURL("client_redirect.html"), 738 CURRENT_TAB, 739 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 740 741 // Wait for app tab to be created and loaded. 742 test_navigation_observer.Wait(); 743 744 // App has loaded, and chrome.app.isInstalled should be true. 745 bool is_installed = false; 746 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 747 browser()->tab_strip_model()->GetActiveWebContents(), 748 "window.domAutomationController.send(chrome.app.isInstalled)", 749 &is_installed)); 750 ASSERT_TRUE(is_installed); 751 } 752 753 // Tests that if we have an app process (path1/container.html) with a non-app 754 // iframe (path3/iframe.html), then opening a link from that iframe to a new 755 // window to a same-origin non-app URL (path3/empty.html) should keep the window 756 // in the app process. 757 // This is in contrast to OpenAppFromIframe, since here the popup will not be 758 // missing special permissions and should be scriptable from the iframe. 759 // See http://crbug.com/92669 for more details. 760 IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) { 761 extensions::ProcessMap* process_map = 762 extensions::ProcessMap::Get(browser()->profile()); 763 764 host_resolver()->AddRule("*", "127.0.0.1"); 765 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 766 767 GURL base_url = GetTestBaseURL("app_process"); 768 769 // Load app and start URL (in the app). 770 const Extension* app = 771 LoadExtension(test_data_dir_.AppendASCII("app_process")); 772 ASSERT_TRUE(app); 773 774 ui_test_utils::NavigateToURL(browser(), 775 base_url.Resolve("path1/container.html")); 776 content::RenderProcessHost* process = 777 browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost(); 778 EXPECT_TRUE(process_map->Contains(process->GetID())); 779 780 // Popup window should be in the app's process. 781 const BrowserList* active_browser_list = 782 BrowserList::GetInstance(chrome::GetActiveDesktop()); 783 EXPECT_EQ(2U, active_browser_list->size()); 784 content::WebContents* popup_contents = 785 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents(); 786 content::WaitForLoadStop(popup_contents); 787 788 RenderViewHost* popup_host = popup_contents->GetRenderViewHost(); 789 EXPECT_EQ(process, popup_host->GetProcess()); 790 } 791 792 // http://crbug.com/118502 793 #if defined(OS_MACOSX) || defined(OS_LINUX) 794 #define MAYBE_ReloadAppAfterCrash DISABLED_ReloadAppAfterCrash 795 #else 796 #define MAYBE_ReloadAppAfterCrash ReloadAppAfterCrash 797 #endif 798 IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_ReloadAppAfterCrash) { 799 extensions::ProcessMap* process_map = 800 extensions::ProcessMap::Get(browser()->profile()); 801 802 host_resolver()->AddRule("*", "127.0.0.1"); 803 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 804 805 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process"))); 806 807 GURL base_url = GetTestBaseURL("app_process"); 808 809 // Load the app, chrome.app.isInstalled should be true. 810 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 811 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 812 EXPECT_TRUE(process_map->Contains( 813 contents->GetRenderProcessHost()->GetID())); 814 bool is_installed = false; 815 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 816 contents, 817 "window.domAutomationController.send(chrome.app.isInstalled)", 818 &is_installed)); 819 ASSERT_TRUE(is_installed); 820 821 // Crash the tab and reload it, chrome.app.isInstalled should still be true. 822 content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents()); 823 content::WindowedNotificationObserver observer( 824 content::NOTIFICATION_LOAD_STOP, 825 content::Source<NavigationController>( 826 &browser()->tab_strip_model()->GetActiveWebContents()-> 827 GetController())); 828 chrome::Reload(browser(), CURRENT_TAB); 829 observer.Wait(); 830 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 831 contents, 832 "window.domAutomationController.send(chrome.app.isInstalled)", 833 &is_installed)); 834 ASSERT_TRUE(is_installed); 835 } 836 837 // Test that a cross-process navigation away from a hosted app stays in the same 838 // BrowsingInstance, so that postMessage calls to the app's other windows still 839 // work. 840 IN_PROC_BROWSER_TEST_F(AppApiTest, SameBrowsingInstanceAfterSwap) { 841 extensions::ProcessMap* process_map = 842 extensions::ProcessMap::Get(browser()->profile()); 843 844 host_resolver()->AddRule("*", "127.0.0.1"); 845 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 846 847 GURL base_url = GetTestBaseURL("app_process"); 848 849 // Load app and start URL (in the app). 850 const Extension* app = 851 LoadExtension(test_data_dir_.AppendASCII("app_process")); 852 ASSERT_TRUE(app); 853 854 ui_test_utils::NavigateToURL(browser(), 855 base_url.Resolve("path1/iframe.html")); 856 content::SiteInstance* app_instance = 857 browser()->tab_strip_model()->GetWebContentsAt(0)->GetSiteInstance(); 858 EXPECT_TRUE(process_map->Contains(app_instance->GetProcess()->GetID())); 859 860 // Popup window should be in the app's process. 861 const BrowserList* active_browser_list = 862 BrowserList::GetInstance(chrome::GetActiveDesktop()); 863 EXPECT_EQ(2U, active_browser_list->size()); 864 content::WebContents* popup_contents = 865 active_browser_list->get(1)->tab_strip_model()->GetActiveWebContents(); 866 content::WaitForLoadStop(popup_contents); 867 868 SiteInstance* popup_instance = popup_contents->GetSiteInstance(); 869 EXPECT_EQ(app_instance, popup_instance); 870 871 // Navigate the popup to another process outside the app. 872 GURL non_app_url(base_url.Resolve("path3/empty.html")); 873 ui_test_utils::NavigateToURL(active_browser_list->get(1), non_app_url); 874 SiteInstance* new_instance = popup_contents->GetSiteInstance(); 875 EXPECT_NE(app_instance, new_instance); 876 877 // It should still be in the same BrowsingInstance, allowing postMessage to 878 // work. 879 EXPECT_TRUE(app_instance->IsRelatedSiteInstance(new_instance)); 880 } 881