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