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/extensions/process_map.h" 12#include "chrome/browser/profiles/profile.h" 13#include "chrome/browser/ui/blocked_content/blocked_content_tab_helper.h" 14#include "chrome/browser/ui/blocked_content/popup_blocker_tab_helper.h" 15#include "chrome/browser/ui/browser.h" 16#include "chrome/browser/ui/browser_commands.h" 17#include "chrome/browser/ui/browser_finder.h" 18#include "chrome/browser/ui/browser_list.h" 19#include "chrome/browser/ui/browser_window.h" 20#include "chrome/browser/ui/tabs/tab_strip_model.h" 21#include "chrome/common/chrome_switches.h" 22#include "chrome/common/extensions/extension.h" 23#include "chrome/common/extensions/extension_file_util.h" 24#include "chrome/test/base/test_switches.h" 25#include "chrome/test/base/ui_test_utils.h" 26#include "content/public/browser/navigation_entry.h" 27#include "content/public/browser/notification_service.h" 28#include "content/public/browser/render_process_host.h" 29#include "content/public/browser/render_view_host.h" 30#include "content/public/browser/web_contents.h" 31#include "content/public/test/browser_test_utils.h" 32#include "content/public/test/test_navigation_observer.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 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 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// Ensure that re-navigating to a URL after installing or uninstalling it as an 415// app correctly swaps the tab to the app process. (http://crbug.com/80621) 416// 417// Fails on Windows. http://crbug.com/238670 418// Added logging to help diagnose the location of the problem. 419IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) { 420 extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get( 421 browser()->profile())->extension_service()->process_map(); 422 423 host_resolver()->AddRule("*", "127.0.0.1"); 424 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 425 426 // The app under test acts on URLs whose host is "localhost", 427 // so the URLs we navigate to must have host "localhost". 428 GURL base_url = GetTestBaseURL("app_process"); 429 430 // Load an app URL before loading the app. 431 LOG(INFO) << "Loading path1/empty.html."; 432 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 433 LOG(INFO) << "Loading path1/empty.html - done."; 434 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 435 EXPECT_FALSE(process_map->Contains( 436 contents->GetRenderProcessHost()->GetID())); 437 438 // Load app and re-navigate to the page. 439 LOG(INFO) << "Loading extension."; 440 const Extension* app = 441 LoadExtension(test_data_dir_.AppendASCII("app_process")); 442 LOG(INFO) << "Loading extension - done."; 443 ASSERT_TRUE(app); 444 LOG(INFO) << "Loading path1/empty.html."; 445 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 446 LOG(INFO) << "Loading path1/empty.html - done."; 447 EXPECT_TRUE(process_map->Contains( 448 contents->GetRenderProcessHost()->GetID())); 449 450 // Disable app and re-navigate to the page. 451 LOG(INFO) << "Disabling extension."; 452 DisableExtension(app->id()); 453 LOG(INFO) << "Disabling extension - done."; 454 LOG(INFO) << "Loading path1/empty.html."; 455 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 456 LOG(INFO) << "Loading path1/empty.html - done."; 457 EXPECT_FALSE(process_map->Contains( 458 contents->GetRenderProcessHost()->GetID())); 459} 460 461// Ensure that reloading a URL after installing or uninstalling it as an app 462// correctly swaps the tab to the app process. (http://crbug.com/80621) 463// 464// Added logging to help diagnose the location of the problem. 465// http://crbug.com/238670 466IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) { 467 extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get( 468 browser()->profile())->extension_service()->process_map(); 469 470 host_resolver()->AddRule("*", "127.0.0.1"); 471 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 472 473 // The app under test acts on URLs whose host is "localhost", 474 // so the URLs we navigate to must have host "localhost". 475 GURL base_url = GetTestBaseURL("app_process"); 476 477 // Load app, disable it, and navigate to the page. 478 LOG(INFO) << "Loading extension."; 479 const Extension* app = 480 LoadExtension(test_data_dir_.AppendASCII("app_process")); 481 LOG(INFO) << "Loading extension - done."; 482 ASSERT_TRUE(app); 483 LOG(INFO) << "Disabling extension."; 484 DisableExtension(app->id()); 485 LOG(INFO) << "Disabling extension - done."; 486 LOG(INFO) << "Navigate to path1/empty.html."; 487 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 488 LOG(INFO) << "Navigate to path1/empty.html - done."; 489 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 490 EXPECT_FALSE(process_map->Contains( 491 contents->GetRenderProcessHost()->GetID())); 492 493 // Enable app and reload the page. 494 LOG(INFO) << "Enabling extension."; 495 EnableExtension(app->id()); 496 LOG(INFO) << "Enabling extension - done."; 497 content::WindowedNotificationObserver reload_observer( 498 content::NOTIFICATION_LOAD_STOP, 499 content::Source<NavigationController>( 500 &browser()->tab_strip_model()->GetActiveWebContents()-> 501 GetController())); 502 LOG(INFO) << "Reloading."; 503 chrome::Reload(browser(), CURRENT_TAB); 504 reload_observer.Wait(); 505 LOG(INFO) << "Reloading - done."; 506 EXPECT_TRUE(process_map->Contains( 507 contents->GetRenderProcessHost()->GetID())); 508 509 // Disable app and reload the page. 510 LOG(INFO) << "Disabling extension."; 511 DisableExtension(app->id()); 512 LOG(INFO) << "Disabling extension - done."; 513 content::WindowedNotificationObserver reload_observer2( 514 content::NOTIFICATION_LOAD_STOP, 515 content::Source<NavigationController>( 516 &browser()->tab_strip_model()->GetActiveWebContents()-> 517 GetController())); 518 LOG(INFO) << "Reloading."; 519 chrome::Reload(browser(), CURRENT_TAB); 520 reload_observer2.Wait(); 521 LOG(INFO) << "Reloading - done."; 522 EXPECT_FALSE(process_map->Contains( 523 contents->GetRenderProcessHost()->GetID())); 524} 525 526// Ensure that reloading a URL with JavaScript after installing or uninstalling 527// it as an app correctly swaps the process. (http://crbug.com/80621) 528// 529// Crashes on Windows and Mac. http://crbug.com/238670 530// Added logging to help diagnose the location of the problem. 531IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) { 532 extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get( 533 browser()->profile())->extension_service()->process_map(); 534 535 host_resolver()->AddRule("*", "127.0.0.1"); 536 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 537 538 // The app under test acts on URLs whose host is "localhost", 539 // so the URLs we navigate to must have host "localhost". 540 GURL base_url = GetTestBaseURL("app_process"); 541 542 // Load app, disable it, and navigate to the page. 543 LOG(INFO) << "Loading extension."; 544 const Extension* app = 545 LoadExtension(test_data_dir_.AppendASCII("app_process")); 546 LOG(INFO) << "Loading extension - done."; 547 ASSERT_TRUE(app); 548 LOG(INFO) << "Disabling extension."; 549 DisableExtension(app->id()); 550 LOG(INFO) << "Disabling extension - done."; 551 LOG(INFO) << "Navigate to path1/empty.html."; 552 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 553 LOG(INFO) << "Navigate to path1/empty.html - done."; 554 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 555 EXPECT_FALSE(process_map->Contains( 556 contents->GetRenderProcessHost()->GetID())); 557 558 // Enable app and reload via JavaScript. 559 LOG(INFO) << "Enabling extension."; 560 EnableExtension(app->id()); 561 LOG(INFO) << "Enabling extension - done."; 562 content::WindowedNotificationObserver js_reload_observer( 563 content::NOTIFICATION_LOAD_STOP, 564 content::Source<NavigationController>( 565 &browser()->tab_strip_model()->GetActiveWebContents()-> 566 GetController())); 567 LOG(INFO) << "Executing location.reload()."; 568 ASSERT_TRUE(content::ExecuteScript(contents, "location.reload();")); 569 js_reload_observer.Wait(); 570 LOG(INFO) << "Executing location.reload() - done."; 571 EXPECT_TRUE(process_map->Contains( 572 contents->GetRenderProcessHost()->GetID())); 573 574 // Disable app and reload via JavaScript. 575 LOG(INFO) << "Disabling extension."; 576 DisableExtension(app->id()); 577 LOG(INFO) << "Disabling extension - done."; 578 content::WindowedNotificationObserver js_reload_observer2( 579 content::NOTIFICATION_LOAD_STOP, 580 content::Source<NavigationController>( 581 &browser()->tab_strip_model()->GetActiveWebContents()-> 582 GetController())); 583 LOG(INFO) << "Executing location = location."; 584 ASSERT_TRUE(content::ExecuteScript(contents, "location = location;")); 585 js_reload_observer2.Wait(); 586 LOG(INFO) << "Executing location = location - done."; 587 EXPECT_FALSE(process_map->Contains( 588 contents->GetRenderProcessHost()->GetID())); 589} 590 591namespace { 592 593void RenderViewHostCreated(std::vector<content::RenderViewHost*>* rvh_vector, 594 content::RenderViewHost* rvh) { 595 rvh_vector->push_back(rvh); 596} 597 598} // namespace 599 600// Tests that if we have a non-app process (path3/container.html) that has an 601// iframe with a URL in the app's extent (path1/iframe.html), then opening a 602// link from that iframe to a new window to a URL in the app's extent (path1/ 603// empty.html) results in the new window being in an app process. See 604// http://crbug.com/89272 for more details. 605IN_PROC_BROWSER_TEST_F(AppApiTest, OpenAppFromIframe) { 606#if defined(OS_WIN) && defined(USE_ASH) 607 // Disable this test in Metro+Ash for now (http://crbug.com/262796). 608 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kAshBrowserTests)) 609 return; 610#endif 611 612 extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get( 613 browser()->profile())->extension_service()->process_map(); 614 615 host_resolver()->AddRule("*", "127.0.0.1"); 616 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 617 618 GURL base_url = GetTestBaseURL("app_process"); 619 620 // Load app and start URL (not in the app). 621 const Extension* app = 622 LoadExtension(test_data_dir_.AppendASCII("app_process")); 623 ASSERT_TRUE(app); 624 625 std::vector<content::RenderViewHost*> rvh_vector; 626 content::RenderViewHost::CreatedCallback rvh_callback( 627 base::Bind(&RenderViewHostCreated, &rvh_vector)); 628 content::RenderViewHost::AddCreatedCallback(rvh_callback); 629 ui_test_utils::NavigateToURL(browser(), 630 base_url.Resolve("path3/container.html")); 631 content::RenderViewHost::RemoveCreatedCallback(rvh_callback); 632 EXPECT_FALSE(process_map->Contains( 633 browser()->tab_strip_model()->GetWebContentsAt(0)-> 634 GetRenderProcessHost()->GetID())); 635 636 // Popup window should be in the app's process. 637 ASSERT_EQ(3U, rvh_vector.size()); 638 RenderViewHost* popup_host = rvh_vector[2]; 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 650IN_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 BlockedContentTabHelper* blocked_content_tab_helper = 664 BlockedContentTabHelper::FromWebContents(tab); 665 PopupBlockerTabHelper* popup_blocker_tab_helper = 666 PopupBlockerTabHelper::FromWebContents(tab); 667 if (!blocked_content_tab_helper->GetBlockedContentsCount() && 668 (!popup_blocker_tab_helper || 669 !popup_blocker_tab_helper->GetBlockedPopupsCount())) { 670 content::WindowedNotificationObserver observer( 671 chrome::NOTIFICATION_WEB_CONTENT_SETTINGS_CHANGED, 672 content::NotificationService::AllSources()); 673 observer.Wait(); 674 } 675 676 EXPECT_EQ(1u, 677 blocked_content_tab_helper->GetBlockedContentsCount() + 678 (popup_blocker_tab_helper 679 ? popup_blocker_tab_helper->GetBlockedPopupsCount() 680 : 0)); 681} 682 683// Tests that if an extension launches an app via chrome.tabs.create with an URL 684// that's not in the app's extent but that server redirects to it, we still end 685// up with an app process. See http://crbug.com/99349 for more details. 686IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) { 687 host_resolver()->AddRule("*", "127.0.0.1"); 688 ASSERT_TRUE(StartEmbeddedTestServer()); 689 690 LoadExtension(test_data_dir_.AppendASCII("app_process")); 691 const Extension* launcher = 692 LoadExtension(test_data_dir_.AppendASCII("app_launcher")); 693 694 // There should be two navigations by the time the app page is loaded. 695 // 1. The extension launcher page. 696 // 2. The app's URL (which includes a server redirect). 697 // Note that the server redirect does not generate a navigation event. 698 content::TestNavigationObserver test_navigation_observer( 699 browser()->tab_strip_model()->GetActiveWebContents(), 700 2); 701 test_navigation_observer.StartWatchingNewWebContents(); 702 703 // Load the launcher extension, which should launch the app. 704 ui_test_utils::NavigateToURLWithDisposition( 705 browser(), 706 launcher->GetResourceURL("server_redirect.html"), 707 CURRENT_TAB, 708 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 709 710 // Wait for app tab to be created and loaded. 711 test_navigation_observer.WaitForObservation( 712 base::Bind(&content::RunMessageLoop), 713 base::Bind(&base::MessageLoop::Quit, 714 base::Unretained(base::MessageLoopForUI::current()))); 715 716 // App has loaded, and chrome.app.isInstalled should be true. 717 bool is_installed = false; 718 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 719 browser()->tab_strip_model()->GetActiveWebContents(), 720 "window.domAutomationController.send(chrome.app.isInstalled)", 721 &is_installed)); 722 ASSERT_TRUE(is_installed); 723} 724 725// Tests that if an extension launches an app via chrome.tabs.create with an URL 726// that's not in the app's extent but that client redirects to it, we still end 727// up with an app process. 728IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) { 729 host_resolver()->AddRule("*", "127.0.0.1"); 730 ASSERT_TRUE(StartEmbeddedTestServer()); 731 732 LoadExtension(test_data_dir_.AppendASCII("app_process")); 733 const Extension* launcher = 734 LoadExtension(test_data_dir_.AppendASCII("app_launcher")); 735 736 // There should be three navigations by the time the app page is loaded. 737 // 1. The extension launcher page. 738 // 2. The URL that the extension launches, which client redirects. 739 // 3. The app's URL. 740 content::TestNavigationObserver test_navigation_observer( 741 browser()->tab_strip_model()->GetActiveWebContents(), 742 3); 743 test_navigation_observer.StartWatchingNewWebContents(); 744 745 // Load the launcher extension, which should launch the app. 746 ui_test_utils::NavigateToURLWithDisposition( 747 browser(), 748 launcher->GetResourceURL("client_redirect.html"), 749 CURRENT_TAB, 750 ui_test_utils::BROWSER_TEST_WAIT_FOR_NAVIGATION); 751 752 // Wait for app tab to be created and loaded. 753 test_navigation_observer.WaitForObservation( 754 base::Bind(&content::RunMessageLoop), 755 base::Bind(&base::MessageLoop::Quit, 756 base::Unretained(base::MessageLoopForUI::current()))); 757 758 // App has loaded, and chrome.app.isInstalled should be true. 759 bool is_installed = false; 760 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 761 browser()->tab_strip_model()->GetActiveWebContents(), 762 "window.domAutomationController.send(chrome.app.isInstalled)", 763 &is_installed)); 764 ASSERT_TRUE(is_installed); 765} 766 767// Tests that if we have an app process (path1/container.html) with a non-app 768// iframe (path3/iframe.html), then opening a link from that iframe to a new 769// window to a same-origin non-app URL (path3/empty.html) should keep the window 770// in the app process. 771// This is in contrast to OpenAppFromIframe, since here the popup will not be 772// missing special permissions and should be scriptable from the iframe. 773// See http://crbug.com/92669 for more details. 774IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) { 775 extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get( 776 browser()->profile())->extension_service()->process_map(); 777 778 host_resolver()->AddRule("*", "127.0.0.1"); 779 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 780 781 GURL base_url = GetTestBaseURL("app_process"); 782 783 // Load app and start URL (in the app). 784 const Extension* app = 785 LoadExtension(test_data_dir_.AppendASCII("app_process")); 786 ASSERT_TRUE(app); 787 788 std::vector<content::RenderViewHost*> rvh_vector; 789 content::RenderViewHost::CreatedCallback rvh_callback( 790 base::Bind(&RenderViewHostCreated, &rvh_vector)); 791 content::RenderViewHost::AddCreatedCallback(rvh_callback); 792 ui_test_utils::NavigateToURL(browser(), 793 base_url.Resolve("path1/container.html")); 794 content::RenderViewHost::RemoveCreatedCallback(rvh_callback); 795 content::RenderProcessHost* process = 796 browser()->tab_strip_model()->GetWebContentsAt(0)->GetRenderProcessHost(); 797 EXPECT_TRUE(process_map->Contains(process->GetID())); 798 799 // Popup window should be in the app's process. 800 ASSERT_EQ(2U, rvh_vector.size()); 801 RenderViewHost* popup_host = rvh_vector[1]; 802 EXPECT_EQ(process, popup_host->GetProcess()); 803} 804 805// http://crbug.com/118502 806#if defined(OS_MACOSX) || defined(OS_LINUX) 807#define MAYBE_ReloadAppAfterCrash DISABLED_ReloadAppAfterCrash 808#else 809#define MAYBE_ReloadAppAfterCrash ReloadAppAfterCrash 810#endif 811IN_PROC_BROWSER_TEST_F(AppApiTest, MAYBE_ReloadAppAfterCrash) { 812 extensions::ProcessMap* process_map = extensions::ExtensionSystem::Get( 813 browser()->profile())->extension_service()->process_map(); 814 815 host_resolver()->AddRule("*", "127.0.0.1"); 816 ASSERT_TRUE(embedded_test_server()->InitializeAndWaitUntilReady()); 817 818 ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process"))); 819 820 GURL base_url = GetTestBaseURL("app_process"); 821 822 // Load the app, chrome.app.isInstalled should be true. 823 ui_test_utils::NavigateToURL(browser(), base_url.Resolve("path1/empty.html")); 824 WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0); 825 EXPECT_TRUE(process_map->Contains( 826 contents->GetRenderProcessHost()->GetID())); 827 bool is_installed = false; 828 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 829 contents, 830 "window.domAutomationController.send(chrome.app.isInstalled)", 831 &is_installed)); 832 ASSERT_TRUE(is_installed); 833 834 // Crash the tab and reload it, chrome.app.isInstalled should still be true. 835 content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents()); 836 content::WindowedNotificationObserver observer( 837 content::NOTIFICATION_LOAD_STOP, 838 content::Source<NavigationController>( 839 &browser()->tab_strip_model()->GetActiveWebContents()-> 840 GetController())); 841 chrome::Reload(browser(), CURRENT_TAB); 842 observer.Wait(); 843 ASSERT_TRUE(content::ExecuteScriptAndExtractBool( 844 contents, 845 "window.domAutomationController.send(chrome.app.isInstalled)", 846 &is_installed)); 847 ASSERT_TRUE(is_installed); 848} 849