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