1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758
|
// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/command_line.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "chrome/browser/extensions/browsertest_util.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/window_controller_list.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_commands.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#include "chrome/browser/ui/view_ids.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/blocked_content/popup_blocker_tab_helper.h"
#include "components/embedder_support/switches.h"
#include "components/sync/model/string_ordinal.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/render_view_host.h"
#include "content/public/browser/site_instance.h"
#include "content/public/browser/web_contents.h"
#include "content/public/common/content_features.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "extensions/browser/extension_host.h"
#include "extensions/browser/extension_system.h"
#include "extensions/browser/install_flag.h"
#include "extensions/browser/process_map.h"
#include "extensions/common/extension.h"
#include "extensions/common/file_util.h"
#include "extensions/common/switches.h"
#include "net/dns/mock_host_resolver.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
using content::NavigationController;
using content::RenderViewHost;
using content::SiteInstance;
using content::WebContents;
using extensions::Extension;
using extensions::WindowControllerList;
class AppApiTest : public extensions::ExtensionApiTest {
protected:
// Gets the base URL for files for a specific test, making sure that it uses
// "localhost" as the hostname, since that is what the extent is declared
// as in the test apps manifests.
GURL GetTestBaseURL(const std::string& test_directory) {
GURL::Replacements replace_host;
replace_host.SetHostStr("localhost");
GURL base_url = embedded_test_server()->GetURL(
"/extensions/api_test/" + test_directory + "/");
return base_url.ReplaceComponents(replace_host);
}
// Pass flags to make testing apps easier.
void SetUpCommandLine(base::CommandLine* command_line) override {
extensions::ExtensionApiTest::SetUpCommandLine(command_line);
base::CommandLine::ForCurrentProcess()->AppendSwitch(
embedder_support::kDisablePopupBlocking);
base::CommandLine::ForCurrentProcess()->AppendSwitch(
extensions::switches::kAllowHTTPBackgroundPage);
}
void SetUpOnMainThread() override {
extensions::ExtensionApiTest::SetUpOnMainThread();
host_resolver()->AddRule("*", "127.0.0.1");
ASSERT_TRUE(StartEmbeddedTestServer());
}
// Helper function to test that independent tabs of the named app are loaded
// into separate processes.
void TestAppInstancesHelper(const std::string& app_name) {
LOG(INFO) << "Start of test.";
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(browser()->profile());
ASSERT_TRUE(LoadExtension(
test_data_dir_.AppendASCII(app_name)));
const Extension* extension = GetSingleLoadedExtension();
// Open two tabs in the app, one outside it.
GURL base_url = GetTestBaseURL(app_name);
// Test both opening a URL in a new tab, and opening a tab and then
// navigating it. Either way, app tabs should be considered extension
// processes, but they have no elevated privileges and thus should not
// have WebUI bindings.
ui_test_utils::NavigateToURLWithDisposition(
browser(), base_url.Resolve("path1/empty.html"),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
LOG(INFO) << "Nav 1.";
EXPECT_TRUE(process_map->Contains(browser()
->tab_strip_model()
->GetWebContentsAt(1)
->GetPrimaryMainFrame()
->GetProcess()
->GetDeprecatedID()));
EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI());
ui_test_utils::TabAddedWaiter tab_add(browser());
chrome::NewTab(browser());
tab_add.Wait();
LOG(INFO) << "New tab.";
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path2/empty.html")));
LOG(INFO) << "Nav 2.";
EXPECT_TRUE(process_map->Contains(browser()
->tab_strip_model()
->GetWebContentsAt(2)
->GetPrimaryMainFrame()
->GetProcess()
->GetDeprecatedID()));
EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI());
// We should have opened 2 new extension tabs. Including the original blank
// tab, we now have 3 tabs.
ASSERT_EQ(3, browser()->tab_strip_model()->count());
// The two app tabs don't have the background permission. To improve
// responsiveness, they should not be in the same process unless the
// kProcessPerSiteUpToMainFrameThreshold feature is enabled. The assumption
// of the kProcessPerSiteUpToMainFrameThreshold is that sharing a process
// with a threshold doesn't hurt responsiveness.
WebContents* tab1 = browser()->tab_strip_model()->GetWebContentsAt(1);
WebContents* tab2 = browser()->tab_strip_model()->GetWebContentsAt(2);
if (!base::FeatureList::IsEnabled(
features::kProcessPerSiteUpToMainFrameThreshold)) {
EXPECT_NE(tab1->GetPrimaryMainFrame()->GetProcess(),
tab2->GetPrimaryMainFrame()->GetProcess());
}
// Opening tabs with window.open should keep the page in the opener's
// process.
ASSERT_EQ(1u,
extensions::browsertest_util::GetWindowControllerCountInProfile(
browser()->profile()));
OpenWindow(tab1, base_url.Resolve("path1/empty.html"), true, true, nullptr);
LOG(INFO) << "WindowOpenHelper 1.";
OpenWindow(tab2, base_url.Resolve("path2/empty.html"), true, true, nullptr);
LOG(INFO) << "End of test.";
UnloadExtension(extension->id());
}
};
// Omits the disable-popup-blocking flag so we can cover that case.
class BlockedAppApiTest : public AppApiTest {
protected:
void SetUpCommandLine(base::CommandLine* command_line) override {
extensions::ExtensionApiTest::SetUpCommandLine(command_line);
base::CommandLine::ForCurrentProcess()->AppendSwitch(
extensions::switches::kAllowHTTPBackgroundPage);
}
};
// Tests that hosted apps with the background permission get a process-per-app
// model, since all pages need to be able to script the background page.
IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcess) {
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(browser()->profile());
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
// Open two tabs in the app, one outside it.
GURL base_url = GetTestBaseURL("app_process");
// Test both opening a URL in a new tab, and opening a tab and then navigating
// it. Either way, app tabs should be considered extension processes, but
// they have no elevated privileges and thus should not have WebUI bindings.
ui_test_utils::NavigateToURLWithDisposition(
browser(), base_url.Resolve("path1/empty.html"),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
EXPECT_TRUE(process_map->Contains(browser()
->tab_strip_model()
->GetWebContentsAt(1)
->GetPrimaryMainFrame()
->GetProcess()
->GetDeprecatedID()));
EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(1)->GetWebUI());
ui_test_utils::NavigateToURLWithDisposition(
browser(), base_url.Resolve("path2/empty.html"),
WindowOpenDisposition::NEW_FOREGROUND_TAB,
ui_test_utils::BROWSER_TEST_WAIT_FOR_LOAD_STOP);
EXPECT_TRUE(process_map->Contains(browser()
->tab_strip_model()
->GetWebContentsAt(2)
->GetPrimaryMainFrame()
->GetProcess()
->GetDeprecatedID()));
EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(2)->GetWebUI());
ui_test_utils::TabAddedWaiter tab_add(browser());
chrome::NewTab(browser());
tab_add.Wait();
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path3/empty.html")));
EXPECT_FALSE(process_map->Contains(browser()
->tab_strip_model()
->GetWebContentsAt(3)
->GetPrimaryMainFrame()
->GetProcess()
->GetDeprecatedID()));
EXPECT_FALSE(browser()->tab_strip_model()->GetWebContentsAt(3)->GetWebUI());
// We should have opened 3 new extension tabs. Including the original blank
// tab, we now have 4 tabs. Because the app_process app has the background
// permission, all of its instances are in the same process. Thus two tabs
// should be part of the extension app and grouped in the same process.
ASSERT_EQ(4, browser()->tab_strip_model()->count());
WebContents* tab = browser()->tab_strip_model()->GetWebContentsAt(1);
EXPECT_EQ(tab->GetPrimaryMainFrame()->GetProcess(),
browser()
->tab_strip_model()
->GetWebContentsAt(2)
->GetPrimaryMainFrame()
->GetProcess());
EXPECT_NE(tab->GetPrimaryMainFrame()->GetProcess(),
browser()
->tab_strip_model()
->GetWebContentsAt(3)
->GetPrimaryMainFrame()
->GetProcess());
// Now let's do the same using window.open. The same should happen.
ASSERT_EQ(1u, extensions::browsertest_util::GetWindowControllerCountInProfile(
browser()->profile()));
OpenWindow(tab, base_url.Resolve("path1/empty.html"), true, true, nullptr);
OpenWindow(tab, base_url.Resolve("path2/empty.html"), true, true, nullptr);
// TODO(creis): This should open in a new process (i.e., false for the last
// argument), but we temporarily avoid swapping processes away from a hosted
// app if it has an opener, because some OAuth providers make script calls
// between non-app popups and non-app iframes in the app process.
// See crbug.com/59285.
OpenWindow(tab, base_url.Resolve("path3/empty.html"), true, true, nullptr);
// Now let's have these pages navigate, into or out of the extension web
// extent. They should switch processes.
const GURL& app_url(base_url.Resolve("path1/empty.html"));
const GURL& non_app_url(base_url.Resolve("path3/empty.html"));
EXPECT_TRUE(NavigateInRenderer(
browser()->tab_strip_model()->GetWebContentsAt(2), non_app_url));
EXPECT_TRUE(NavigateInRenderer(
browser()->tab_strip_model()->GetWebContentsAt(3), app_url));
EXPECT_NE(tab->GetPrimaryMainFrame()->GetProcess(),
browser()
->tab_strip_model()
->GetWebContentsAt(2)
->GetPrimaryMainFrame()
->GetProcess());
EXPECT_EQ(tab->GetPrimaryMainFrame()->GetProcess(),
browser()
->tab_strip_model()
->GetWebContentsAt(3)
->GetPrimaryMainFrame()
->GetProcess());
// If one of the popup tabs navigates back to the app, window.opener should
// be valid.
EXPECT_TRUE(NavigateInRenderer(
browser()->tab_strip_model()->GetWebContentsAt(6), app_url));
EXPECT_EQ(tab->GetPrimaryMainFrame()->GetProcess(),
browser()
->tab_strip_model()
->GetWebContentsAt(6)
->GetPrimaryMainFrame()
->GetProcess());
ASSERT_EQ(true,
content::EvalJs(browser()->tab_strip_model()->GetWebContentsAt(6),
"window.opener != null"));
}
// Test that hosted apps without the background permission use a process per app
// instance model, such that separate instances are in separate processes.
IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessInstances) {
TestAppInstancesHelper("app_process_instances");
}
// Test that hosted apps with the background permission but that set
// allow_js_access to false also use a process per app instance model.
// Separate instances should be in separate processes.
IN_PROC_BROWSER_TEST_F(AppApiTest, AppProcessBackgroundInstances) {
TestAppInstancesHelper("app_process_background_instances");
}
// Tests that app process switching works properly in the following scenario:
// 1. navigate to a page1 in the app
// 2. page1 redirects to a page2 outside the app extent (ie, "/server-redirect")
// 3. page2 redirects back to a page in the app
// The final navigation should end up in the app process.
// See http://crbug.com/61757
// Flaky. http://crbug.com/341898
IN_PROC_BROWSER_TEST_F(AppApiTest, DISABLED_AppProcessRedirectBack) {
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
// Open two tabs in the app.
GURL base_url = GetTestBaseURL("app_process");
chrome::NewTab(browser());
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/empty.html")));
chrome::NewTab(browser());
// Wait until the second tab finishes its redirect train (2 hops).
// 1. We navigate to redirect.html
// 2. Renderer navigates and finishes, counting as a load stop.
// 3. Renderer issues the meta refresh to navigate to server-redirect.
// 4. Renderer is now in a "provisional load", waiting for navigation to
// complete.
// 5. Browser sees a redirect response from server-redirect to empty.html, and
// transfers that to a new navigation, using RequestTransferURL.
// 6. Renderer navigates to empty.html, and finishes loading, counting as the
// second load stop
ui_test_utils::NavigateToURLBlockUntilNavigationsComplete(
browser(), base_url.Resolve("path1/redirect.html"), 2);
// 3 tabs, including the initial about:blank. The last 2 should be the same
// process.
ASSERT_EQ(3, browser()->tab_strip_model()->count());
EXPECT_EQ("/extensions/api_test/app_process/path1/empty.html",
browser()->tab_strip_model()->GetWebContentsAt(2)->
GetController().GetLastCommittedEntry()->GetURL().path());
EXPECT_EQ(browser()
->tab_strip_model()
->GetWebContentsAt(1)
->GetPrimaryMainFrame()
->GetProcess(),
browser()
->tab_strip_model()
->GetWebContentsAt(2)
->GetPrimaryMainFrame()
->GetProcess());
}
// Ensure that re-navigating to a URL after installing or uninstalling it as an
// app correctly swaps the tab to the app process. (http://crbug.com/80621)
//
// Fails on Windows. http://crbug.com/238670
// Added logging to help diagnose the location of the problem.
IN_PROC_BROWSER_TEST_F(AppApiTest, NavigateIntoAppProcess) {
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(browser()->profile());
// The app under test acts on URLs whose host is "localhost",
// so the URLs we navigate to must have host "localhost".
GURL base_url = GetTestBaseURL("app_process");
// Load an app URL before loading the app.
LOG(INFO) << "Loading path1/empty.html.";
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/empty.html")));
LOG(INFO) << "Loading path1/empty.html - done.";
WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
EXPECT_FALSE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
// Load app and re-navigate to the page.
LOG(INFO) << "Loading extension.";
const Extension* app =
LoadExtension(test_data_dir_.AppendASCII("app_process"));
LOG(INFO) << "Loading extension - done.";
ASSERT_TRUE(app);
LOG(INFO) << "Loading path1/empty.html.";
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/empty.html")));
LOG(INFO) << "Loading path1/empty.html - done.";
EXPECT_TRUE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
// Disable app and re-navigate to the page.
LOG(INFO) << "Disabling extension.";
DisableExtension(app->id());
LOG(INFO) << "Disabling extension - done.";
LOG(INFO) << "Loading path1/empty.html.";
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/empty.html")));
LOG(INFO) << "Loading path1/empty.html - done.";
EXPECT_FALSE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
}
// Ensure that reloading a URL after installing or uninstalling it as an app
// correctly swaps the tab to the app process. (http://crbug.com/80621)
//
// Added logging to help diagnose the location of the problem.
// http://crbug.com/238670
IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcess) {
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(browser()->profile());
// The app under test acts on URLs whose host is "localhost",
// so the URLs we navigate to must have host "localhost".
GURL base_url = GetTestBaseURL("app_process");
// Load app, disable it, and navigate to the page.
LOG(INFO) << "Loading extension.";
const Extension* app =
LoadExtension(test_data_dir_.AppendASCII("app_process"));
LOG(INFO) << "Loading extension - done.";
ASSERT_TRUE(app);
LOG(INFO) << "Disabling extension.";
DisableExtension(app->id());
LOG(INFO) << "Disabling extension - done.";
LOG(INFO) << "Navigate to path1/empty.html.";
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/empty.html")));
LOG(INFO) << "Navigate to path1/empty.html - done.";
WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
content::NavigationController& controller = contents->GetController();
EXPECT_FALSE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
// The test starts with about:blank, then navigates to path1/empty.html,
// so there should be two entries.
EXPECT_EQ(2, controller.GetEntryCount());
// Enable app and reload the page.
LOG(INFO) << "Enabling extension.";
EnableExtension(app->id());
LOG(INFO) << "Enabling extension - done.";
content::LoadStopObserver reload_observer(
browser()->tab_strip_model()->GetActiveWebContents());
LOG(INFO) << "Reloading.";
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
reload_observer.Wait();
LOG(INFO) << "Reloading - done.";
EXPECT_TRUE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
// Reloading, even with changing SiteInstance/process should not add any
// more entries.
EXPECT_EQ(2, controller.GetEntryCount());
// Disable app and reload the page.
LOG(INFO) << "Disabling extension.";
DisableExtension(app->id());
LOG(INFO) << "Disabling extension - done.";
content::LoadStopObserver reload_observer2(
browser()->tab_strip_model()->GetActiveWebContents());
LOG(INFO) << "Reloading.";
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
reload_observer2.Wait();
LOG(INFO) << "Reloading - done.";
EXPECT_FALSE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
EXPECT_EQ(2, controller.GetEntryCount());
}
// Ensure that reloading a URL with JavaScript after installing or uninstalling
// it as an app correctly swaps the process. (http://crbug.com/80621)
//
// Crashes on Windows and Mac. http://crbug.com/238670
// Added logging to help diagnose the location of the problem.
IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadIntoAppProcessWithJavaScript) {
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(browser()->profile());
// The app under test acts on URLs whose host is "localhost",
// so the URLs we navigate to must have host "localhost".
GURL base_url = GetTestBaseURL("app_process");
// Load app, disable it, and navigate to the page.
LOG(INFO) << "Loading extension.";
const Extension* app =
LoadExtension(test_data_dir_.AppendASCII("app_process"));
LOG(INFO) << "Loading extension - done.";
ASSERT_TRUE(app);
LOG(INFO) << "Disabling extension.";
DisableExtension(app->id());
LOG(INFO) << "Disabling extension - done.";
LOG(INFO) << "Navigate to path1/empty.html.";
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/empty.html")));
LOG(INFO) << "Navigate to path1/empty.html - done.";
WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
EXPECT_FALSE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
// Enable app and reload via JavaScript.
LOG(INFO) << "Enabling extension.";
EnableExtension(app->id());
LOG(INFO) << "Enabling extension - done.";
content::LoadStopObserver js_reload_observer(
browser()->tab_strip_model()->GetActiveWebContents());
LOG(INFO) << "Executing location.reload().";
ASSERT_TRUE(content::ExecJs(contents, "location.reload();"));
js_reload_observer.Wait();
LOG(INFO) << "Executing location.reload() - done.";
EXPECT_TRUE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
// Disable app and reload via JavaScript.
LOG(INFO) << "Disabling extension.";
DisableExtension(app->id());
LOG(INFO) << "Disabling extension - done.";
content::LoadStopObserver js_reload_observer2(
browser()->tab_strip_model()->GetActiveWebContents());
LOG(INFO) << "Executing location = location.";
ASSERT_TRUE(content::ExecJs(contents, "location = location;"));
js_reload_observer2.Wait();
LOG(INFO) << "Executing location = location - done.";
EXPECT_FALSE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
}
// Similar to the previous test, but ensure that popup blocking bypass
// isn't granted to the iframe. See crbug.com/117446.
IN_PROC_BROWSER_TEST_F(BlockedAppApiTest, OpenAppFromIframe) {
// Load app and start URL (not in the app).
const Extension* app =
LoadExtension(test_data_dir_.AppendASCII("app_process"));
ASSERT_TRUE(app);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(),
GetTestBaseURL("app_process").Resolve("path3/container.html")));
ui_test_utils::WaitForViewVisibility(browser(), VIEW_ID_CONTENT_SETTING_POPUP,
true);
WebContents* tab = browser()->tab_strip_model()->GetActiveWebContents();
blocked_content::PopupBlockerTabHelper* popup_blocker_tab_helper =
blocked_content::PopupBlockerTabHelper::FromWebContents(tab);
EXPECT_EQ(1u, popup_blocker_tab_helper->GetBlockedPopupsCount());
}
// Tests that if an extension launches an app via chrome.tabs.create with an URL
// that's not in the app's extent but that server redirects to it, we still end
// up with an app process. See http://crbug.com/99349 for more details.
IN_PROC_BROWSER_TEST_F(AppApiTest, ServerRedirectToAppFromExtension) {
LoadExtension(test_data_dir_.AppendASCII("app_process"));
const Extension* launcher =
LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
// There should be two navigations by the time the app page is loaded.
// 1. The extension launcher page.
// 2. The app's URL (which includes a server redirect).
// Note that the server redirect does not generate a navigation event.
content::TestNavigationObserver test_navigation_observer(
browser()->tab_strip_model()->GetActiveWebContents(),
2);
test_navigation_observer.StartWatchingNewWebContents();
// Load the launcher extension, which should launch the app.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), launcher->GetResourceURL("server_redirect.html")));
// Wait for app tab to be created and loaded.
test_navigation_observer.Wait();
// App has loaded, and chrome.app.isInstalled should be true.
ASSERT_EQ(true, content::EvalJs(
browser()->tab_strip_model()->GetActiveWebContents(),
"chrome.app.isInstalled"));
}
// Tests that if an extension launches an app via chrome.tabs.create with an URL
// that's not in the app's extent but that client redirects to it, we still end
// up with an app process.
IN_PROC_BROWSER_TEST_F(AppApiTest, ClientRedirectToAppFromExtension) {
LoadExtension(test_data_dir_.AppendASCII("app_process"));
const Extension* launcher =
LoadExtension(test_data_dir_.AppendASCII("app_launcher"));
// There should be three navigations by the time the app page is loaded.
// 1. The extension launcher page.
// 2. The URL that the extension launches, which client redirects.
// 3. The app's URL.
content::TestNavigationObserver test_navigation_observer(
browser()->tab_strip_model()->GetActiveWebContents(),
3);
test_navigation_observer.StartWatchingNewWebContents();
// Load the launcher extension, which should launch the app.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), launcher->GetResourceURL("client_redirect.html")));
// Wait for app tab to be created and loaded.
test_navigation_observer.Wait();
// App has loaded, and chrome.app.isInstalled should be true.
ASSERT_EQ(true, content::EvalJs(
browser()->tab_strip_model()->GetActiveWebContents(),
"chrome.app.isInstalled"));
}
// Tests that if we have an app process (path1/container.html) with a non-app
// iframe (path3/iframe.html), then opening a link from that iframe to a new
// window to a same-origin non-app URL (path3/empty.html) should keep the window
// in the app process.
// This is in contrast to OpenAppFromIframe, since here the popup will not be
// missing special permissions and should be scriptable from the iframe.
// See http://crbug.com/92669 for more details.
IN_PROC_BROWSER_TEST_F(AppApiTest, OpenWebPopupFromWebIframe) {
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(browser()->profile());
GURL base_url = GetTestBaseURL("app_process");
// Load app and start URL (in the app).
const Extension* app =
LoadExtension(test_data_dir_.AppendASCII("app_process"));
ASSERT_TRUE(app);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/container.html")));
content::RenderProcessHost* process = browser()
->tab_strip_model()
->GetWebContentsAt(0)
->GetPrimaryMainFrame()
->GetProcess();
EXPECT_TRUE(process_map->Contains(process->GetDeprecatedID()));
// Popup window should be in the app's process.
const WindowControllerList* active_window_list =
WindowControllerList::GetInstance();
EXPECT_EQ(2U, active_window_list->size());
content::WebContents* popup_contents =
active_window_list->get(1)->GetActiveTab();
ASSERT_TRUE(popup_contents);
EXPECT_TRUE(content::WaitForLoadStop(popup_contents));
content::RenderProcessHost* popup_process =
popup_contents->GetPrimaryMainFrame()->GetProcess();
EXPECT_EQ(process, popup_process);
EXPECT_TRUE(process_map->Contains(popup_process->GetDeprecatedID()));
}
IN_PROC_BROWSER_TEST_F(AppApiTest, ReloadAppAfterCrash) {
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(browser()->profile());
ASSERT_TRUE(LoadExtension(test_data_dir_.AppendASCII("app_process")));
GURL base_url = GetTestBaseURL("app_process");
// Load the app, chrome.app.isInstalled should be true.
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/empty.html")));
WebContents* contents = browser()->tab_strip_model()->GetWebContentsAt(0);
EXPECT_TRUE(process_map->Contains(
contents->GetPrimaryMainFrame()->GetProcess()->GetDeprecatedID()));
ASSERT_EQ(true, content::EvalJs(contents, "chrome.app.isInstalled"));
// Crash the tab and reload it, chrome.app.isInstalled should still be true.
content::CrashTab(browser()->tab_strip_model()->GetActiveWebContents());
content::LoadStopObserver observer(
browser()->tab_strip_model()->GetActiveWebContents());
chrome::Reload(browser(), WindowOpenDisposition::CURRENT_TAB);
observer.Wait();
ASSERT_EQ(true, content::EvalJs(contents, "chrome.app.isInstalled"));
}
// Test that a cross-site renderer-initiated navigation away from a hosted app
// stays in the same BrowsingInstance, so that postMessage calls to the app's
// other windows still work, and a cross-site browser-initiated navigation away
// from a hosted app switches BrowsingInstances.
IN_PROC_BROWSER_TEST_F(AppApiTest, NavigatePopupFromAppToOutsideApp) {
extensions::ProcessMap* process_map =
extensions::ProcessMap::Get(browser()->profile());
GURL base_url = GetTestBaseURL("app_process");
// Load app and start URL (in the app).
const Extension* app =
LoadExtension(test_data_dir_.AppendASCII("app_process"));
ASSERT_TRUE(app);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
browser(), base_url.Resolve("path1/iframe.html")));
content::SiteInstance* app_instance =
browser()->tab_strip_model()->GetWebContentsAt(0)->GetSiteInstance();
EXPECT_TRUE(
process_map->Contains(app_instance->GetProcess()->GetDeprecatedID()));
// Popup window should be in the app's process.
const WindowControllerList* active_window_list =
WindowControllerList::GetInstance();
EXPECT_EQ(2U, active_window_list->size());
content::WebContents* popup_contents =
active_window_list->get(1)->GetActiveTab();
ASSERT_TRUE(popup_contents);
EXPECT_TRUE(content::WaitForLoadStop(popup_contents));
SiteInstance* popup_instance = popup_contents->GetSiteInstance();
EXPECT_EQ(app_instance, popup_instance);
// Do a renderer-initiated navigation in the popup to a URL outside the app.
GURL non_app_url(base_url.Resolve("path3/empty.html"));
{
content::TestNavigationObserver observer(popup_contents);
EXPECT_TRUE(ExecJs(
popup_contents,
base::StringPrintf("location = '%s';", non_app_url.spec().c_str())));
observer.Wait();
}
// The popup will stay in the same SiteInstance, even in
// --site-per-process mode, because the popup is still same-site with its
// opener. Staying in same SiteInstance implies that postMessage will still
// work.
EXPECT_TRUE(
app_instance->IsRelatedSiteInstance(popup_contents->GetSiteInstance()));
EXPECT_EQ(app_instance, popup_contents->GetSiteInstance());
// Go back in the popup.
{
content::TestNavigationObserver observer(popup_contents);
popup_contents->GetController().GoBack();
observer.Wait();
EXPECT_EQ(app_instance, popup_contents->GetSiteInstance());
}
// Do a browser-initiated navigation in the popup to a same-site URL outside
// the app.
// TODO(alexmos): This could swap BrowsingInstances, since a
// browser-initiated navigation breaks the scripting relationship between the
// popup and the app, but it currently does not, since we keep the scripting
// relationship regardless of whether the navigation is browser or
// renderer-initiated (see https://crbug.com/828720). Consider changing
// this in the future as part of https://crbug.com/718516.
{
content::TestNavigationObserver observer(popup_contents);
ASSERT_TRUE(ui_test_utils::NavigateToURL(
active_window_list->get(1)->GetBrowser(), non_app_url));
observer.Wait();
EXPECT_EQ(app_instance, popup_contents->GetSiteInstance());
EXPECT_TRUE(
app_instance->IsRelatedSiteInstance(popup_contents->GetSiteInstance()));
}
// Go back in the popup.
{
content::TestNavigationObserver observer(popup_contents);
popup_contents->GetController().GoBack();
observer.Wait();
EXPECT_EQ(app_instance, popup_contents->GetSiteInstance());
}
// Do a browser-initiated navigation in the popup to a cross-site URL outside
// the app. This should swap BrowsingInstances.
{
content::TestNavigationObserver observer(popup_contents);
GURL cross_site_url(
embedded_test_server()->GetURL("foo.com", "/title1.html"));
ASSERT_TRUE(ui_test_utils::NavigateToURL(
active_window_list->get(1)->GetBrowser(), cross_site_url));
observer.Wait();
EXPECT_NE(app_instance, popup_contents->GetSiteInstance());
EXPECT_FALSE(
app_instance->IsRelatedSiteInstance(popup_contents->GetSiteInstance()));
}
}
|