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 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806
|
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// 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/compiler_specific.h"
#include "base/memory/scoped_vector.h"
#include "base/strings/string16.h"
#include "content/browser/browser_thread_impl.h"
#include "content/browser/browsing_instance.h"
#include "content/browser/child_process_security_policy_impl.h"
#include "content/browser/frame_host/navigation_entry_impl.h"
#include "content/browser/renderer_host/render_process_host_impl.h"
#include "content/browser/renderer_host/render_view_host_impl.h"
#include "content/browser/site_instance_impl.h"
#include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/webui/web_ui_controller_factory_registry.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_constants.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/url_constants.h"
#include "content/public/common/url_utils.h"
#include "content/public/test/mock_render_process_host.h"
#include "content/public/test/test_browser_context.h"
#include "content/public/test/test_browser_thread.h"
#include "content/test/test_content_browser_client.h"
#include "content/test/test_content_client.h"
#include "content/test/test_render_view_host.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "url/url_util.h"
namespace content {
namespace {
const char kPrivilegedScheme[] = "privileged";
class SiteInstanceTestWebUIControllerFactory : public WebUIControllerFactory {
public:
WebUIController* CreateWebUIControllerForURL(WebUI* web_ui,
const GURL& url) const override {
return NULL;
}
WebUI::TypeID GetWebUIType(BrowserContext* browser_context,
const GURL& url) const override {
return WebUI::kNoWebUI;
}
bool UseWebUIForURL(BrowserContext* browser_context,
const GURL& url) const override {
return HasWebUIScheme(url);
}
bool UseWebUIBindingsForURL(BrowserContext* browser_context,
const GURL& url) const override {
return HasWebUIScheme(url);
}
};
class SiteInstanceTestBrowserClient : public TestContentBrowserClient {
public:
SiteInstanceTestBrowserClient()
: privileged_process_id_(-1) {
WebUIControllerFactory::RegisterFactory(&factory_);
}
~SiteInstanceTestBrowserClient() override {
WebUIControllerFactory::UnregisterFactoryForTesting(&factory_);
}
bool IsSuitableHost(RenderProcessHost* process_host,
const GURL& site_url) override {
return (privileged_process_id_ == process_host->GetID()) ==
site_url.SchemeIs(kPrivilegedScheme);
}
void set_privileged_process_id(int process_id) {
privileged_process_id_ = process_id;
}
private:
SiteInstanceTestWebUIControllerFactory factory_;
int privileged_process_id_;
};
class SiteInstanceTest : public testing::Test {
public:
SiteInstanceTest()
: ui_thread_(BrowserThread::UI, &message_loop_),
file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING,
&message_loop_),
io_thread_(BrowserThread::IO, &message_loop_),
old_browser_client_(NULL) {
}
void SetUp() override {
old_browser_client_ = SetBrowserClientForTesting(&browser_client_);
url::AddStandardScheme(kPrivilegedScheme);
url::AddStandardScheme(kChromeUIScheme);
SiteInstanceImpl::set_render_process_host_factory(&rph_factory_);
}
void TearDown() override {
// Ensure that no RenderProcessHosts are left over after the tests.
EXPECT_TRUE(RenderProcessHost::AllHostsIterator().IsAtEnd());
SetBrowserClientForTesting(old_browser_client_);
SiteInstanceImpl::set_render_process_host_factory(NULL);
// http://crbug.com/143565 found SiteInstanceTest leaking an
// AppCacheDatabase. This happens because some part of the test indirectly
// calls StoragePartitionImplMap::PostCreateInitialization(), which posts
// a task to the IO thread to create the AppCacheDatabase. Since the
// message loop is not running, the AppCacheDatabase ends up getting
// created when DrainMessageLoops() gets called at the end of a test case.
// Immediately after, the test case ends and the AppCacheDatabase gets
// scheduled for deletion. Here, call DrainMessageLoops() again so the
// AppCacheDatabase actually gets deleted.
DrainMessageLoops();
}
void set_privileged_process_id(int process_id) {
browser_client_.set_privileged_process_id(process_id);
}
void DrainMessageLoops() {
// We don't just do this in TearDown() because we create TestBrowserContext
// objects in each test, which will be destructed before
// TearDown() is called.
base::MessageLoop::current()->RunUntilIdle();
message_loop_.RunUntilIdle();
}
private:
base::MessageLoopForUI message_loop_;
TestBrowserThread ui_thread_;
TestBrowserThread file_user_blocking_thread_;
TestBrowserThread io_thread_;
SiteInstanceTestBrowserClient browser_client_;
ContentBrowserClient* old_browser_client_;
MockRenderProcessHostFactory rph_factory_;
};
// Subclass of BrowsingInstance that updates a counter when deleted and
// returns TestSiteInstances from GetSiteInstanceForURL.
class TestBrowsingInstance : public BrowsingInstance {
public:
TestBrowsingInstance(BrowserContext* browser_context, int* delete_counter)
: BrowsingInstance(browser_context),
delete_counter_(delete_counter) {
}
// Make a few methods public for tests.
using BrowsingInstance::browser_context;
using BrowsingInstance::HasSiteInstance;
using BrowsingInstance::GetSiteInstanceForURL;
using BrowsingInstance::RegisterSiteInstance;
using BrowsingInstance::UnregisterSiteInstance;
private:
~TestBrowsingInstance() override { (*delete_counter_)++; }
int* delete_counter_;
};
// Subclass of SiteInstanceImpl that updates a counter when deleted.
class TestSiteInstance : public SiteInstanceImpl {
public:
static TestSiteInstance* CreateTestSiteInstance(
BrowserContext* browser_context,
int* site_delete_counter,
int* browsing_delete_counter) {
TestBrowsingInstance* browsing_instance =
new TestBrowsingInstance(browser_context, browsing_delete_counter);
return new TestSiteInstance(browsing_instance, site_delete_counter);
}
private:
TestSiteInstance(BrowsingInstance* browsing_instance, int* delete_counter)
: SiteInstanceImpl(browsing_instance), delete_counter_(delete_counter) {}
~TestSiteInstance() override { (*delete_counter_)++; }
int* delete_counter_;
};
} // namespace
// Test to ensure no memory leaks for SiteInstance objects.
TEST_F(SiteInstanceTest, SiteInstanceDestructor) {
// The existence of this object will cause WebContentsImpl to create our
// test one instead of the real one.
RenderViewHostTestEnabler rvh_test_enabler;
int site_delete_counter = 0;
int browsing_delete_counter = 0;
const GURL url("test:foo");
// Ensure that instances are deleted when their NavigationEntries are gone.
TestSiteInstance* instance =
TestSiteInstance::CreateTestSiteInstance(NULL, &site_delete_counter,
&browsing_delete_counter);
EXPECT_EQ(0, site_delete_counter);
NavigationEntryImpl* e1 = new NavigationEntryImpl(
instance, 0, url, Referrer(), base::string16(), ui::PAGE_TRANSITION_LINK,
false);
// Redundantly setting e1's SiteInstance shouldn't affect the ref count.
e1->set_site_instance(instance);
EXPECT_EQ(0, site_delete_counter);
// Add a second reference
NavigationEntryImpl* e2 = new NavigationEntryImpl(
instance, 0, url, Referrer(), base::string16(), ui::PAGE_TRANSITION_LINK,
false);
// Now delete both entries and be sure the SiteInstance goes away.
delete e1;
EXPECT_EQ(0, site_delete_counter);
EXPECT_EQ(0, browsing_delete_counter);
delete e2;
EXPECT_EQ(1, site_delete_counter);
// instance is now deleted
EXPECT_EQ(1, browsing_delete_counter);
// browsing_instance is now deleted
// Ensure that instances are deleted when their RenderViewHosts are gone.
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
instance =
TestSiteInstance::CreateTestSiteInstance(browser_context.get(),
&site_delete_counter,
&browsing_delete_counter);
{
scoped_ptr<WebContentsImpl> web_contents(static_cast<WebContentsImpl*>(
WebContents::Create(WebContents::CreateParams(
browser_context.get(), instance))));
EXPECT_EQ(1, site_delete_counter);
EXPECT_EQ(1, browsing_delete_counter);
}
// Make sure that we flush any messages related to the above WebContentsImpl
// destruction.
DrainMessageLoops();
EXPECT_EQ(2, site_delete_counter);
EXPECT_EQ(2, browsing_delete_counter);
// contents is now deleted, along with instance and browsing_instance
}
// Test that NavigationEntries with SiteInstances can be cloned, but that their
// SiteInstances can be changed afterwards. Also tests that the ref counts are
// updated properly after the change.
TEST_F(SiteInstanceTest, CloneNavigationEntry) {
int site_delete_counter1 = 0;
int site_delete_counter2 = 0;
int browsing_delete_counter = 0;
const GURL url("test:foo");
SiteInstanceImpl* instance1 =
TestSiteInstance::CreateTestSiteInstance(NULL, &site_delete_counter1,
&browsing_delete_counter);
SiteInstanceImpl* instance2 =
TestSiteInstance::CreateTestSiteInstance(NULL, &site_delete_counter2,
&browsing_delete_counter);
NavigationEntryImpl* e1 = new NavigationEntryImpl(
instance1, 0, url, Referrer(), base::string16(), ui::PAGE_TRANSITION_LINK,
false);
// Clone the entry
NavigationEntryImpl* e2 = new NavigationEntryImpl(*e1);
// Should be able to change the SiteInstance of the cloned entry.
e2->set_site_instance(instance2);
// The first SiteInstance should go away after deleting e1, since e2 should
// no longer be referencing it.
delete e1;
EXPECT_EQ(1, site_delete_counter1);
EXPECT_EQ(0, site_delete_counter2);
// The second SiteInstance should go away after deleting e2.
delete e2;
EXPECT_EQ(1, site_delete_counter1);
EXPECT_EQ(1, site_delete_counter2);
// Both BrowsingInstances are also now deleted
EXPECT_EQ(2, browsing_delete_counter);
DrainMessageLoops();
}
// Test to ensure GetProcess returns and creates processes correctly.
TEST_F(SiteInstanceTest, GetProcess) {
// Ensure that GetProcess returns a process.
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
scoped_ptr<RenderProcessHost> host1;
scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
host1.reset(instance->GetProcess());
EXPECT_TRUE(host1.get() != NULL);
// Ensure that GetProcess creates a new process.
scoped_refptr<SiteInstanceImpl> instance2(static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
scoped_ptr<RenderProcessHost> host2(instance2->GetProcess());
EXPECT_TRUE(host2.get() != NULL);
EXPECT_NE(host1.get(), host2.get());
DrainMessageLoops();
}
// Test to ensure SetSite and site() work properly.
TEST_F(SiteInstanceTest, SetSite) {
scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>(
SiteInstance::Create(NULL)));
EXPECT_FALSE(instance->HasSite());
EXPECT_TRUE(instance->GetSiteURL().is_empty());
instance->SetSite(GURL("http://www.google.com/index.html"));
EXPECT_EQ(GURL("http://google.com"), instance->GetSiteURL());
EXPECT_TRUE(instance->HasSite());
DrainMessageLoops();
}
// Test to ensure GetSiteForURL properly returns sites for URLs.
TEST_F(SiteInstanceTest, GetSiteForURL) {
// Pages are irrelevant.
GURL test_url = GURL("http://www.google.com/index.html");
GURL site_url = SiteInstanceImpl::GetSiteForURL(NULL, test_url);
EXPECT_EQ(GURL("http://google.com"), site_url);
EXPECT_EQ("http", site_url.scheme());
EXPECT_EQ("google.com", site_url.host());
// Ports are irrlevant.
test_url = GURL("https://www.google.com:8080");
site_url = SiteInstanceImpl::GetSiteForURL(NULL, test_url);
EXPECT_EQ(GURL("https://google.com"), site_url);
// Hostnames without TLDs are ok.
test_url = GURL("http://foo/a.html");
site_url = SiteInstanceImpl::GetSiteForURL(NULL, test_url);
EXPECT_EQ(GURL("http://foo"), site_url);
EXPECT_EQ("foo", site_url.host());
// File URLs should include the scheme.
test_url = GURL("file:///C:/Downloads/");
site_url = SiteInstanceImpl::GetSiteForURL(NULL, test_url);
EXPECT_EQ(GURL("file:"), site_url);
EXPECT_EQ("file", site_url.scheme());
EXPECT_FALSE(site_url.has_host());
// Some file URLs have hosts in the path.
test_url = GURL("file://server/path");
site_url = SiteInstanceImpl::GetSiteForURL(NULL, test_url);
EXPECT_EQ(GURL("file://server"), site_url);
EXPECT_EQ("server", site_url.host());
// Data URLs should include the scheme.
test_url = GURL("data:text/html,foo");
site_url = SiteInstanceImpl::GetSiteForURL(NULL, test_url);
EXPECT_EQ(GURL("data:"), site_url);
EXPECT_EQ("data", site_url.scheme());
EXPECT_FALSE(site_url.has_host());
// Javascript URLs should include the scheme.
test_url = GURL("javascript:foo();");
site_url = SiteInstanceImpl::GetSiteForURL(NULL, test_url);
EXPECT_EQ(GURL("javascript:"), site_url);
EXPECT_EQ("javascript", site_url.scheme());
EXPECT_FALSE(site_url.has_host());
// Guest URLs are special and need to have the path in the site as well,
// since it affects the StoragePartition configuration.
std::string guest_url(kGuestScheme);
guest_url.append("://abc123/path");
test_url = GURL(guest_url);
site_url = SiteInstanceImpl::GetSiteForURL(NULL, test_url);
EXPECT_EQ(test_url, site_url);
DrainMessageLoops();
}
// Test of distinguishing URLs from different sites. Most of this logic is
// tested in RegistryControlledDomainTest. This test focuses on URLs with
// different schemes or ports.
TEST_F(SiteInstanceTest, IsSameWebSite) {
GURL url_foo = GURL("http://foo/a.html");
GURL url_foo2 = GURL("http://foo/b.html");
GURL url_foo_https = GURL("https://foo/a.html");
GURL url_foo_port = GURL("http://foo:8080/a.html");
GURL url_javascript = GURL("javascript:alert(1);");
GURL url_blank = GURL(url::kAboutBlankURL);
// Same scheme and port -> same site.
EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo, url_foo2));
// Different scheme -> different site.
EXPECT_FALSE(SiteInstance::IsSameWebSite(NULL, url_foo, url_foo_https));
// Different port -> same site.
// (Changes to document.domain make renderer ignore the port.)
EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo, url_foo_port));
// JavaScript links should be considered same site for anything.
EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_javascript, url_foo));
EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_javascript, url_foo_https));
EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_javascript, url_foo_port));
// Navigating to a blank page is considered the same site.
EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo, url_blank));
EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo_https, url_blank));
EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo_port, url_blank));
// Navigating from a blank site is not considered to be the same site.
EXPECT_FALSE(SiteInstance::IsSameWebSite(NULL, url_blank, url_foo));
EXPECT_FALSE(SiteInstance::IsSameWebSite(NULL, url_blank, url_foo_https));
EXPECT_FALSE(SiteInstance::IsSameWebSite(NULL, url_blank, url_foo_port));
DrainMessageLoops();
}
// Test to ensure that there is only one SiteInstance per site in a given
// BrowsingInstance, when process-per-site is not in use.
TEST_F(SiteInstanceTest, OneSiteInstancePerSite) {
ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kProcessPerSite));
int delete_counter = 0;
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
TestBrowsingInstance* browsing_instance =
new TestBrowsingInstance(browser_context.get(), &delete_counter);
const GURL url_a1("http://www.google.com/1.html");
scoped_refptr<SiteInstanceImpl> site_instance_a1(
static_cast<SiteInstanceImpl*>(
browsing_instance->GetSiteInstanceForURL(url_a1)));
EXPECT_TRUE(site_instance_a1.get() != NULL);
// A separate site should create a separate SiteInstance.
const GURL url_b1("http://www.yahoo.com/");
scoped_refptr<SiteInstanceImpl> site_instance_b1(
static_cast<SiteInstanceImpl*>(
browsing_instance->GetSiteInstanceForURL(url_b1)));
EXPECT_NE(site_instance_a1.get(), site_instance_b1.get());
EXPECT_TRUE(site_instance_a1->IsRelatedSiteInstance(site_instance_b1.get()));
// Getting the new SiteInstance from the BrowsingInstance and from another
// SiteInstance in the BrowsingInstance should give the same result.
EXPECT_EQ(site_instance_b1.get(),
site_instance_a1->GetRelatedSiteInstance(url_b1));
// A second visit to the original site should return the same SiteInstance.
const GURL url_a2("http://www.google.com/2.html");
EXPECT_EQ(site_instance_a1.get(),
browsing_instance->GetSiteInstanceForURL(url_a2));
EXPECT_EQ(site_instance_a1.get(),
site_instance_a1->GetRelatedSiteInstance(url_a2));
// A visit to the original site in a new BrowsingInstance (same or different
// browser context) should return a different SiteInstance.
TestBrowsingInstance* browsing_instance2 =
new TestBrowsingInstance(browser_context.get(), &delete_counter);
// Ensure the new SiteInstance is ref counted so that it gets deleted.
scoped_refptr<SiteInstanceImpl> site_instance_a2_2(
static_cast<SiteInstanceImpl*>(
browsing_instance2->GetSiteInstanceForURL(url_a2)));
EXPECT_NE(site_instance_a1.get(), site_instance_a2_2.get());
EXPECT_FALSE(
site_instance_a1->IsRelatedSiteInstance(site_instance_a2_2.get()));
// The two SiteInstances for http://google.com should not use the same process
// if process-per-site is not enabled.
scoped_ptr<RenderProcessHost> process_a1(site_instance_a1->GetProcess());
scoped_ptr<RenderProcessHost> process_a2_2(site_instance_a2_2->GetProcess());
EXPECT_NE(process_a1.get(), process_a2_2.get());
// Should be able to see that we do have SiteInstances.
EXPECT_TRUE(browsing_instance->HasSiteInstance(
GURL("http://mail.google.com")));
EXPECT_TRUE(browsing_instance2->HasSiteInstance(
GURL("http://mail.google.com")));
EXPECT_TRUE(browsing_instance->HasSiteInstance(
GURL("http://mail.yahoo.com")));
// Should be able to see that we don't have SiteInstances.
EXPECT_FALSE(browsing_instance->HasSiteInstance(
GURL("https://www.google.com")));
EXPECT_FALSE(browsing_instance2->HasSiteInstance(
GURL("http://www.yahoo.com")));
// browsing_instances will be deleted when their SiteInstances are deleted.
// The processes will be unregistered when the RPH scoped_ptrs go away.
DrainMessageLoops();
}
// Test to ensure that there is only one RenderProcessHost per site for an
// entire BrowserContext, if process-per-site is in use.
TEST_F(SiteInstanceTest, OneSiteInstancePerSiteInBrowserContext) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kProcessPerSite);
int delete_counter = 0;
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
TestBrowsingInstance* browsing_instance =
new TestBrowsingInstance(browser_context.get(), &delete_counter);
const GURL url_a1("http://www.google.com/1.html");
scoped_refptr<SiteInstanceImpl> site_instance_a1(
static_cast<SiteInstanceImpl*>(
browsing_instance->GetSiteInstanceForURL(url_a1)));
EXPECT_TRUE(site_instance_a1.get() != NULL);
scoped_ptr<RenderProcessHost> process_a1(site_instance_a1->GetProcess());
// A separate site should create a separate SiteInstance.
const GURL url_b1("http://www.yahoo.com/");
scoped_refptr<SiteInstanceImpl> site_instance_b1(
static_cast<SiteInstanceImpl*>(
browsing_instance->GetSiteInstanceForURL(url_b1)));
EXPECT_NE(site_instance_a1.get(), site_instance_b1.get());
EXPECT_TRUE(site_instance_a1->IsRelatedSiteInstance(site_instance_b1.get()));
// Getting the new SiteInstance from the BrowsingInstance and from another
// SiteInstance in the BrowsingInstance should give the same result.
EXPECT_EQ(site_instance_b1.get(),
site_instance_a1->GetRelatedSiteInstance(url_b1));
// A second visit to the original site should return the same SiteInstance.
const GURL url_a2("http://www.google.com/2.html");
EXPECT_EQ(site_instance_a1.get(),
browsing_instance->GetSiteInstanceForURL(url_a2));
EXPECT_EQ(site_instance_a1.get(),
site_instance_a1->GetRelatedSiteInstance(url_a2));
// A visit to the original site in a new BrowsingInstance (same browser
// context) should return a different SiteInstance with the same process.
TestBrowsingInstance* browsing_instance2 =
new TestBrowsingInstance(browser_context.get(), &delete_counter);
scoped_refptr<SiteInstanceImpl> site_instance_a1_2(
static_cast<SiteInstanceImpl*>(
browsing_instance2->GetSiteInstanceForURL(url_a1)));
EXPECT_TRUE(site_instance_a1.get() != NULL);
EXPECT_NE(site_instance_a1.get(), site_instance_a1_2.get());
EXPECT_EQ(process_a1.get(), site_instance_a1_2->GetProcess());
// A visit to the original site in a new BrowsingInstance (different browser
// context) should return a different SiteInstance with a different process.
scoped_ptr<TestBrowserContext> browser_context2(new TestBrowserContext());
TestBrowsingInstance* browsing_instance3 =
new TestBrowsingInstance(browser_context2.get(), &delete_counter);
scoped_refptr<SiteInstanceImpl> site_instance_a2_3(
static_cast<SiteInstanceImpl*>(
browsing_instance3->GetSiteInstanceForURL(url_a2)));
EXPECT_TRUE(site_instance_a2_3.get() != NULL);
scoped_ptr<RenderProcessHost> process_a2_3(site_instance_a2_3->GetProcess());
EXPECT_NE(site_instance_a1.get(), site_instance_a2_3.get());
EXPECT_NE(process_a1.get(), process_a2_3.get());
// Should be able to see that we do have SiteInstances.
EXPECT_TRUE(browsing_instance->HasSiteInstance(
GURL("http://mail.google.com"))); // visited before
EXPECT_TRUE(browsing_instance2->HasSiteInstance(
GURL("http://mail.google.com"))); // visited before
EXPECT_TRUE(browsing_instance->HasSiteInstance(
GURL("http://mail.yahoo.com"))); // visited before
// Should be able to see that we don't have SiteInstances.
EXPECT_FALSE(browsing_instance2->HasSiteInstance(
GURL("http://www.yahoo.com"))); // different BI, same browser context
EXPECT_FALSE(browsing_instance->HasSiteInstance(
GURL("https://www.google.com"))); // not visited before
EXPECT_FALSE(browsing_instance3->HasSiteInstance(
GURL("http://www.yahoo.com"))); // different BI, different context
// browsing_instances will be deleted when their SiteInstances are deleted.
// The processes will be unregistered when the RPH scoped_ptrs go away.
DrainMessageLoops();
}
static SiteInstanceImpl* CreateSiteInstance(BrowserContext* browser_context,
const GURL& url) {
return static_cast<SiteInstanceImpl*>(
SiteInstance::CreateForURL(browser_context, url));
}
// Test to ensure that pages that require certain privileges are grouped
// in processes with similar pages.
TEST_F(SiteInstanceTest, ProcessSharingByType) {
// This test shouldn't run with --site-per-process or
// --enable-strict-site-isolation modes, since they don't allow render
// process reuse, which this test explicitly exercises.
const base::CommandLine& command_line =
*base::CommandLine::ForCurrentProcess();
if (command_line.HasSwitch(switches::kSitePerProcess) ||
command_line.HasSwitch(switches::kEnableStrictSiteIsolation))
return;
// On Android by default the number of renderer hosts is unlimited and process
// sharing doesn't happen. We set the override so that the test can run
// everywhere.
RenderProcessHost::SetMaxRendererProcessCount(kMaxRendererProcessCount);
ChildProcessSecurityPolicyImpl* policy =
ChildProcessSecurityPolicyImpl::GetInstance();
// Make a bunch of mock renderers so that we hit the limit.
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
ScopedVector<MockRenderProcessHost> hosts;
for (size_t i = 0; i < kMaxRendererProcessCount; ++i)
hosts.push_back(new MockRenderProcessHost(browser_context.get()));
// Create some extension instances and make sure they share a process.
scoped_refptr<SiteInstanceImpl> extension1_instance(
CreateSiteInstance(browser_context.get(),
GURL(kPrivilegedScheme + std::string("://foo/bar"))));
set_privileged_process_id(extension1_instance->GetProcess()->GetID());
scoped_refptr<SiteInstanceImpl> extension2_instance(
CreateSiteInstance(browser_context.get(),
GURL(kPrivilegedScheme + std::string("://baz/bar"))));
scoped_ptr<RenderProcessHost> extension_host(
extension1_instance->GetProcess());
EXPECT_EQ(extension1_instance->GetProcess(),
extension2_instance->GetProcess());
// Create some WebUI instances and make sure they share a process.
scoped_refptr<SiteInstanceImpl> webui1_instance(CreateSiteInstance(
browser_context.get(), GURL(kChromeUIScheme + std::string("://newtab"))));
policy->GrantWebUIBindings(webui1_instance->GetProcess()->GetID());
scoped_refptr<SiteInstanceImpl> webui2_instance(
CreateSiteInstance(browser_context.get(),
GURL(kChromeUIScheme + std::string("://history"))));
scoped_ptr<RenderProcessHost> dom_host(webui1_instance->GetProcess());
EXPECT_EQ(webui1_instance->GetProcess(), webui2_instance->GetProcess());
// Make sure none of differing privilege processes are mixed.
EXPECT_NE(extension1_instance->GetProcess(), webui1_instance->GetProcess());
for (size_t i = 0; i < kMaxRendererProcessCount; ++i) {
EXPECT_NE(extension1_instance->GetProcess(), hosts[i]);
EXPECT_NE(webui1_instance->GetProcess(), hosts[i]);
}
DrainMessageLoops();
// Disable the process limit override.
RenderProcessHost::SetMaxRendererProcessCount(0u);
}
// Test to ensure that HasWrongProcessForURL behaves properly for different
// types of URLs.
TEST_F(SiteInstanceTest, HasWrongProcessForURL) {
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
scoped_ptr<RenderProcessHost> host;
scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
EXPECT_FALSE(instance->HasSite());
EXPECT_TRUE(instance->GetSiteURL().is_empty());
instance->SetSite(GURL("http://evernote.com/"));
EXPECT_TRUE(instance->HasSite());
// Check prior to "assigning" a process to the instance, which is expected
// to return false due to not being attached to any process yet.
EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://google.com")));
// The call to GetProcess actually creates a new real process, which works
// fine, but might be a cause for problems in different contexts.
host.reset(instance->GetProcess());
EXPECT_TRUE(host.get() != NULL);
EXPECT_TRUE(instance->HasProcess());
EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://evernote.com")));
EXPECT_FALSE(instance->HasWrongProcessForURL(
GURL("javascript:alert(document.location.href);")));
EXPECT_TRUE(instance->HasWrongProcessForURL(GURL("chrome://settings")));
// Test that WebUI SiteInstances reject normal web URLs.
const GURL webui_url("chrome://settings");
scoped_refptr<SiteInstanceImpl> webui_instance(static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
webui_instance->SetSite(webui_url);
scoped_ptr<RenderProcessHost> webui_host(webui_instance->GetProcess());
// Simulate granting WebUI bindings for the process.
ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings(
webui_host->GetID());
EXPECT_TRUE(webui_instance->HasProcess());
EXPECT_FALSE(webui_instance->HasWrongProcessForURL(webui_url));
EXPECT_TRUE(webui_instance->HasWrongProcessForURL(GURL("http://google.com")));
// WebUI uses process-per-site, so another instance will use the same process
// even if we haven't called GetProcess yet. Make sure HasWrongProcessForURL
// doesn't crash (http://crbug.com/137070).
scoped_refptr<SiteInstanceImpl> webui_instance2(
static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
webui_instance2->SetSite(webui_url);
EXPECT_FALSE(webui_instance2->HasWrongProcessForURL(webui_url));
EXPECT_TRUE(
webui_instance2->HasWrongProcessForURL(GURL("http://google.com")));
DrainMessageLoops();
}
// Test to ensure that HasWrongProcessForURL behaves properly even when
// --site-per-process is used (http://crbug.com/160671).
TEST_F(SiteInstanceTest, HasWrongProcessForURLInSitePerProcess) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kSitePerProcess);
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
scoped_ptr<RenderProcessHost> host;
scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
instance->SetSite(GURL("http://evernote.com/"));
EXPECT_TRUE(instance->HasSite());
// Check prior to "assigning" a process to the instance, which is expected
// to return false due to not being attached to any process yet.
EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://google.com")));
// The call to GetProcess actually creates a new real process, which works
// fine, but might be a cause for problems in different contexts.
host.reset(instance->GetProcess());
EXPECT_TRUE(host.get() != NULL);
EXPECT_TRUE(instance->HasProcess());
EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://evernote.com")));
EXPECT_FALSE(instance->HasWrongProcessForURL(
GURL("javascript:alert(document.location.href);")));
EXPECT_TRUE(instance->HasWrongProcessForURL(GURL("chrome://settings")));
DrainMessageLoops();
}
// Test that we do not reuse a process in process-per-site mode if it has the
// wrong bindings for its URL. http://crbug.com/174059.
TEST_F(SiteInstanceTest, ProcessPerSiteWithWrongBindings) {
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
scoped_ptr<RenderProcessHost> host;
scoped_ptr<RenderProcessHost> host2;
scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
EXPECT_FALSE(instance->HasSite());
EXPECT_TRUE(instance->GetSiteURL().is_empty());
// Simulate navigating to a WebUI URL in a process that does not have WebUI
// bindings. This already requires bypassing security checks.
const GURL webui_url("chrome://settings");
instance->SetSite(webui_url);
EXPECT_TRUE(instance->HasSite());
// The call to GetProcess actually creates a new real process.
host.reset(instance->GetProcess());
EXPECT_TRUE(host.get() != NULL);
EXPECT_TRUE(instance->HasProcess());
// Without bindings, this should look like the wrong process.
EXPECT_TRUE(instance->HasWrongProcessForURL(webui_url));
// WebUI uses process-per-site, so another instance would normally use the
// same process. Make sure it doesn't use the same process if the bindings
// are missing.
scoped_refptr<SiteInstanceImpl> instance2(
static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
instance2->SetSite(webui_url);
host2.reset(instance2->GetProcess());
EXPECT_TRUE(host2.get() != NULL);
EXPECT_TRUE(instance2->HasProcess());
EXPECT_NE(host.get(), host2.get());
DrainMessageLoops();
}
// Test that we do not register processes with empty sites for process-per-site
// mode.
TEST_F(SiteInstanceTest, NoProcessPerSiteForEmptySite) {
base::CommandLine::ForCurrentProcess()->AppendSwitch(
switches::kProcessPerSite);
scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext());
scoped_ptr<RenderProcessHost> host;
scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>(
SiteInstance::Create(browser_context.get())));
instance->SetSite(GURL());
EXPECT_TRUE(instance->HasSite());
EXPECT_TRUE(instance->GetSiteURL().is_empty());
host.reset(instance->GetProcess());
EXPECT_FALSE(RenderProcessHostImpl::GetProcessHostForSite(
browser_context.get(), GURL()));
DrainMessageLoops();
}
} // namespace content
|