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 "base/compiler_specific.h" 7 #include "base/memory/scoped_vector.h" 8 #include "base/strings/string16.h" 9 #include "content/browser/browser_thread_impl.h" 10 #include "content/browser/browsing_instance.h" 11 #include "content/browser/child_process_security_policy_impl.h" 12 #include "content/browser/frame_host/navigation_entry_impl.h" 13 #include "content/browser/renderer_host/render_process_host_impl.h" 14 #include "content/browser/renderer_host/render_view_host_impl.h" 15 #include "content/browser/site_instance_impl.h" 16 #include "content/browser/web_contents/web_contents_impl.h" 17 #include "content/browser/webui/web_ui_controller_factory_registry.h" 18 #include "content/public/common/content_client.h" 19 #include "content/public/common/content_constants.h" 20 #include "content/public/common/content_switches.h" 21 #include "content/public/common/url_constants.h" 22 #include "content/public/common/url_utils.h" 23 #include "content/public/test/mock_render_process_host.h" 24 #include "content/public/test/test_browser_context.h" 25 #include "content/public/test/test_browser_thread.h" 26 #include "content/test/test_content_browser_client.h" 27 #include "content/test/test_content_client.h" 28 #include "content/test/test_render_view_host.h" 29 #include "testing/gtest/include/gtest/gtest.h" 30 #include "url/url_util.h" 31 32 namespace content { 33 namespace { 34 35 const char kPrivilegedScheme[] = "privileged"; 36 37 class SiteInstanceTestWebUIControllerFactory : public WebUIControllerFactory { 38 public: 39 virtual WebUIController* CreateWebUIControllerForURL( 40 WebUI* web_ui, const GURL& url) const OVERRIDE { 41 return NULL; 42 } 43 virtual WebUI::TypeID GetWebUIType(BrowserContext* browser_context, 44 const GURL& url) const OVERRIDE { 45 return WebUI::kNoWebUI; 46 } 47 virtual bool UseWebUIForURL(BrowserContext* browser_context, 48 const GURL& url) const OVERRIDE { 49 return HasWebUIScheme(url); 50 } 51 virtual bool UseWebUIBindingsForURL(BrowserContext* browser_context, 52 const GURL& url) const OVERRIDE { 53 return HasWebUIScheme(url); 54 } 55 }; 56 57 class SiteInstanceTestBrowserClient : public TestContentBrowserClient { 58 public: 59 SiteInstanceTestBrowserClient() 60 : privileged_process_id_(-1) { 61 WebUIControllerFactory::RegisterFactory(&factory_); 62 } 63 64 virtual ~SiteInstanceTestBrowserClient() { 65 WebUIControllerFactory::UnregisterFactoryForTesting(&factory_); 66 } 67 68 virtual bool IsSuitableHost(RenderProcessHost* process_host, 69 const GURL& site_url) OVERRIDE { 70 return (privileged_process_id_ == process_host->GetID()) == 71 site_url.SchemeIs(kPrivilegedScheme); 72 } 73 74 void set_privileged_process_id(int process_id) { 75 privileged_process_id_ = process_id; 76 } 77 78 private: 79 SiteInstanceTestWebUIControllerFactory factory_; 80 int privileged_process_id_; 81 }; 82 83 class SiteInstanceTest : public testing::Test { 84 public: 85 SiteInstanceTest() 86 : ui_thread_(BrowserThread::UI, &message_loop_), 87 file_user_blocking_thread_(BrowserThread::FILE_USER_BLOCKING, 88 &message_loop_), 89 io_thread_(BrowserThread::IO, &message_loop_), 90 old_browser_client_(NULL) { 91 } 92 93 virtual void SetUp() { 94 old_browser_client_ = SetBrowserClientForTesting(&browser_client_); 95 url::AddStandardScheme(kPrivilegedScheme); 96 url::AddStandardScheme(kChromeUIScheme); 97 98 SiteInstanceImpl::set_render_process_host_factory(&rph_factory_); 99 } 100 101 virtual void TearDown() { 102 // Ensure that no RenderProcessHosts are left over after the tests. 103 EXPECT_TRUE(RenderProcessHost::AllHostsIterator().IsAtEnd()); 104 105 SetBrowserClientForTesting(old_browser_client_); 106 SiteInstanceImpl::set_render_process_host_factory(NULL); 107 108 // http://crbug.com/143565 found SiteInstanceTest leaking an 109 // AppCacheDatabase. This happens because some part of the test indirectly 110 // calls StoragePartitionImplMap::PostCreateInitialization(), which posts 111 // a task to the IO thread to create the AppCacheDatabase. Since the 112 // message loop is not running, the AppCacheDatabase ends up getting 113 // created when DrainMessageLoops() gets called at the end of a test case. 114 // Immediately after, the test case ends and the AppCacheDatabase gets 115 // scheduled for deletion. Here, call DrainMessageLoops() again so the 116 // AppCacheDatabase actually gets deleted. 117 DrainMessageLoops(); 118 } 119 120 void set_privileged_process_id(int process_id) { 121 browser_client_.set_privileged_process_id(process_id); 122 } 123 124 void DrainMessageLoops() { 125 // We don't just do this in TearDown() because we create TestBrowserContext 126 // objects in each test, which will be destructed before 127 // TearDown() is called. 128 base::MessageLoop::current()->RunUntilIdle(); 129 message_loop_.RunUntilIdle(); 130 } 131 132 private: 133 base::MessageLoopForUI message_loop_; 134 TestBrowserThread ui_thread_; 135 TestBrowserThread file_user_blocking_thread_; 136 TestBrowserThread io_thread_; 137 138 SiteInstanceTestBrowserClient browser_client_; 139 ContentBrowserClient* old_browser_client_; 140 MockRenderProcessHostFactory rph_factory_; 141 }; 142 143 // Subclass of BrowsingInstance that updates a counter when deleted and 144 // returns TestSiteInstances from GetSiteInstanceForURL. 145 class TestBrowsingInstance : public BrowsingInstance { 146 public: 147 TestBrowsingInstance(BrowserContext* browser_context, int* delete_counter) 148 : BrowsingInstance(browser_context), 149 delete_counter_(delete_counter) { 150 } 151 152 // Make a few methods public for tests. 153 using BrowsingInstance::browser_context; 154 using BrowsingInstance::HasSiteInstance; 155 using BrowsingInstance::GetSiteInstanceForURL; 156 using BrowsingInstance::RegisterSiteInstance; 157 using BrowsingInstance::UnregisterSiteInstance; 158 159 private: 160 virtual ~TestBrowsingInstance() { 161 (*delete_counter_)++; 162 } 163 164 int* delete_counter_; 165 }; 166 167 // Subclass of SiteInstanceImpl that updates a counter when deleted. 168 class TestSiteInstance : public SiteInstanceImpl { 169 public: 170 static TestSiteInstance* CreateTestSiteInstance( 171 BrowserContext* browser_context, 172 int* site_delete_counter, 173 int* browsing_delete_counter) { 174 TestBrowsingInstance* browsing_instance = 175 new TestBrowsingInstance(browser_context, browsing_delete_counter); 176 return new TestSiteInstance(browsing_instance, site_delete_counter); 177 } 178 179 private: 180 TestSiteInstance(BrowsingInstance* browsing_instance, int* delete_counter) 181 : SiteInstanceImpl(browsing_instance), delete_counter_(delete_counter) {} 182 virtual ~TestSiteInstance() { 183 (*delete_counter_)++; 184 } 185 186 int* delete_counter_; 187 }; 188 189 } // namespace 190 191 // Test to ensure no memory leaks for SiteInstance objects. 192 TEST_F(SiteInstanceTest, SiteInstanceDestructor) { 193 // The existence of this object will cause WebContentsImpl to create our 194 // test one instead of the real one. 195 RenderViewHostTestEnabler rvh_test_enabler; 196 int site_delete_counter = 0; 197 int browsing_delete_counter = 0; 198 const GURL url("test:foo"); 199 200 // Ensure that instances are deleted when their NavigationEntries are gone. 201 TestSiteInstance* instance = 202 TestSiteInstance::CreateTestSiteInstance(NULL, &site_delete_counter, 203 &browsing_delete_counter); 204 EXPECT_EQ(0, site_delete_counter); 205 206 NavigationEntryImpl* e1 = new NavigationEntryImpl( 207 instance, 0, url, Referrer(), base::string16(), ui::PAGE_TRANSITION_LINK, 208 false); 209 210 // Redundantly setting e1's SiteInstance shouldn't affect the ref count. 211 e1->set_site_instance(instance); 212 EXPECT_EQ(0, site_delete_counter); 213 214 // Add a second reference 215 NavigationEntryImpl* e2 = new NavigationEntryImpl( 216 instance, 0, url, Referrer(), base::string16(), ui::PAGE_TRANSITION_LINK, 217 false); 218 219 // Now delete both entries and be sure the SiteInstance goes away. 220 delete e1; 221 EXPECT_EQ(0, site_delete_counter); 222 EXPECT_EQ(0, browsing_delete_counter); 223 delete e2; 224 EXPECT_EQ(1, site_delete_counter); 225 // instance is now deleted 226 EXPECT_EQ(1, browsing_delete_counter); 227 // browsing_instance is now deleted 228 229 // Ensure that instances are deleted when their RenderViewHosts are gone. 230 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 231 instance = 232 TestSiteInstance::CreateTestSiteInstance(browser_context.get(), 233 &site_delete_counter, 234 &browsing_delete_counter); 235 { 236 scoped_ptr<WebContentsImpl> web_contents(static_cast<WebContentsImpl*>( 237 WebContents::Create(WebContents::CreateParams( 238 browser_context.get(), instance)))); 239 EXPECT_EQ(1, site_delete_counter); 240 EXPECT_EQ(1, browsing_delete_counter); 241 } 242 243 // Make sure that we flush any messages related to the above WebContentsImpl 244 // destruction. 245 DrainMessageLoops(); 246 247 EXPECT_EQ(2, site_delete_counter); 248 EXPECT_EQ(2, browsing_delete_counter); 249 // contents is now deleted, along with instance and browsing_instance 250 } 251 252 // Test that NavigationEntries with SiteInstances can be cloned, but that their 253 // SiteInstances can be changed afterwards. Also tests that the ref counts are 254 // updated properly after the change. 255 TEST_F(SiteInstanceTest, CloneNavigationEntry) { 256 int site_delete_counter1 = 0; 257 int site_delete_counter2 = 0; 258 int browsing_delete_counter = 0; 259 const GURL url("test:foo"); 260 261 SiteInstanceImpl* instance1 = 262 TestSiteInstance::CreateTestSiteInstance(NULL, &site_delete_counter1, 263 &browsing_delete_counter); 264 SiteInstanceImpl* instance2 = 265 TestSiteInstance::CreateTestSiteInstance(NULL, &site_delete_counter2, 266 &browsing_delete_counter); 267 268 NavigationEntryImpl* e1 = new NavigationEntryImpl( 269 instance1, 0, url, Referrer(), base::string16(), ui::PAGE_TRANSITION_LINK, 270 false); 271 // Clone the entry 272 NavigationEntryImpl* e2 = new NavigationEntryImpl(*e1); 273 274 // Should be able to change the SiteInstance of the cloned entry. 275 e2->set_site_instance(instance2); 276 277 // The first SiteInstance should go away after deleting e1, since e2 should 278 // no longer be referencing it. 279 delete e1; 280 EXPECT_EQ(1, site_delete_counter1); 281 EXPECT_EQ(0, site_delete_counter2); 282 283 // The second SiteInstance should go away after deleting e2. 284 delete e2; 285 EXPECT_EQ(1, site_delete_counter1); 286 EXPECT_EQ(1, site_delete_counter2); 287 288 // Both BrowsingInstances are also now deleted 289 EXPECT_EQ(2, browsing_delete_counter); 290 291 DrainMessageLoops(); 292 } 293 294 // Test to ensure GetProcess returns and creates processes correctly. 295 TEST_F(SiteInstanceTest, GetProcess) { 296 // Ensure that GetProcess returns a process. 297 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 298 scoped_ptr<RenderProcessHost> host1; 299 scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>( 300 SiteInstance::Create(browser_context.get()))); 301 host1.reset(instance->GetProcess()); 302 EXPECT_TRUE(host1.get() != NULL); 303 304 // Ensure that GetProcess creates a new process. 305 scoped_refptr<SiteInstanceImpl> instance2(static_cast<SiteInstanceImpl*>( 306 SiteInstance::Create(browser_context.get()))); 307 scoped_ptr<RenderProcessHost> host2(instance2->GetProcess()); 308 EXPECT_TRUE(host2.get() != NULL); 309 EXPECT_NE(host1.get(), host2.get()); 310 311 DrainMessageLoops(); 312 } 313 314 // Test to ensure SetSite and site() work properly. 315 TEST_F(SiteInstanceTest, SetSite) { 316 scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>( 317 SiteInstance::Create(NULL))); 318 EXPECT_FALSE(instance->HasSite()); 319 EXPECT_TRUE(instance->GetSiteURL().is_empty()); 320 321 instance->SetSite(GURL("http://www.google.com/index.html")); 322 EXPECT_EQ(GURL("http://google.com"), instance->GetSiteURL()); 323 324 EXPECT_TRUE(instance->HasSite()); 325 326 DrainMessageLoops(); 327 } 328 329 // Test to ensure GetSiteForURL properly returns sites for URLs. 330 TEST_F(SiteInstanceTest, GetSiteForURL) { 331 // Pages are irrelevant. 332 GURL test_url = GURL("http://www.google.com/index.html"); 333 EXPECT_EQ(GURL("http://google.com"), 334 SiteInstanceImpl::GetSiteForURL(NULL, test_url)); 335 336 // Ports are irrlevant. 337 test_url = GURL("https://www.google.com:8080"); 338 EXPECT_EQ(GURL("https://google.com"), 339 SiteInstanceImpl::GetSiteForURL(NULL, test_url)); 340 341 // Javascript URLs have no site. 342 test_url = GURL("javascript:foo();"); 343 EXPECT_EQ(GURL(), SiteInstanceImpl::GetSiteForURL(NULL, test_url)); 344 345 test_url = GURL("http://foo/a.html"); 346 EXPECT_EQ(GURL("http://foo"), SiteInstanceImpl::GetSiteForURL( 347 NULL, test_url)); 348 349 test_url = GURL("file:///C:/Downloads/"); 350 EXPECT_EQ(GURL(), SiteInstanceImpl::GetSiteForURL(NULL, test_url)); 351 352 std::string guest_url(kGuestScheme); 353 guest_url.append("://abc123"); 354 test_url = GURL(guest_url); 355 EXPECT_EQ(test_url, SiteInstanceImpl::GetSiteForURL(NULL, test_url)); 356 357 // TODO(creis): Do we want to special case file URLs to ensure they have 358 // either no site or a special "file://" site? We currently return 359 // "file://home/" as the site, which seems broken. 360 // test_url = GURL("file://home/"); 361 // EXPECT_EQ(GURL(), SiteInstanceImpl::GetSiteForURL(NULL, test_url)); 362 363 DrainMessageLoops(); 364 } 365 366 // Test of distinguishing URLs from different sites. Most of this logic is 367 // tested in RegistryControlledDomainTest. This test focuses on URLs with 368 // different schemes or ports. 369 TEST_F(SiteInstanceTest, IsSameWebSite) { 370 GURL url_foo = GURL("http://foo/a.html"); 371 GURL url_foo2 = GURL("http://foo/b.html"); 372 GURL url_foo_https = GURL("https://foo/a.html"); 373 GURL url_foo_port = GURL("http://foo:8080/a.html"); 374 GURL url_javascript = GURL("javascript:alert(1);"); 375 GURL url_blank = GURL(url::kAboutBlankURL); 376 377 // Same scheme and port -> same site. 378 EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo, url_foo2)); 379 380 // Different scheme -> different site. 381 EXPECT_FALSE(SiteInstance::IsSameWebSite(NULL, url_foo, url_foo_https)); 382 383 // Different port -> same site. 384 // (Changes to document.domain make renderer ignore the port.) 385 EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo, url_foo_port)); 386 387 // JavaScript links should be considered same site for anything. 388 EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_javascript, url_foo)); 389 EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_javascript, url_foo_https)); 390 EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_javascript, url_foo_port)); 391 392 // Navigating to a blank page is considered the same site. 393 EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo, url_blank)); 394 EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo_https, url_blank)); 395 EXPECT_TRUE(SiteInstance::IsSameWebSite(NULL, url_foo_port, url_blank)); 396 397 // Navigating from a blank site is not considered to be the same site. 398 EXPECT_FALSE(SiteInstance::IsSameWebSite(NULL, url_blank, url_foo)); 399 EXPECT_FALSE(SiteInstance::IsSameWebSite(NULL, url_blank, url_foo_https)); 400 EXPECT_FALSE(SiteInstance::IsSameWebSite(NULL, url_blank, url_foo_port)); 401 402 DrainMessageLoops(); 403 } 404 405 // Test to ensure that there is only one SiteInstance per site in a given 406 // BrowsingInstance, when process-per-site is not in use. 407 TEST_F(SiteInstanceTest, OneSiteInstancePerSite) { 408 ASSERT_FALSE(base::CommandLine::ForCurrentProcess()->HasSwitch( 409 switches::kProcessPerSite)); 410 int delete_counter = 0; 411 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 412 TestBrowsingInstance* browsing_instance = 413 new TestBrowsingInstance(browser_context.get(), &delete_counter); 414 415 const GURL url_a1("http://www.google.com/1.html"); 416 scoped_refptr<SiteInstanceImpl> site_instance_a1( 417 static_cast<SiteInstanceImpl*>( 418 browsing_instance->GetSiteInstanceForURL(url_a1))); 419 EXPECT_TRUE(site_instance_a1.get() != NULL); 420 421 // A separate site should create a separate SiteInstance. 422 const GURL url_b1("http://www.yahoo.com/"); 423 scoped_refptr<SiteInstanceImpl> site_instance_b1( 424 static_cast<SiteInstanceImpl*>( 425 browsing_instance->GetSiteInstanceForURL(url_b1))); 426 EXPECT_NE(site_instance_a1.get(), site_instance_b1.get()); 427 EXPECT_TRUE(site_instance_a1->IsRelatedSiteInstance(site_instance_b1.get())); 428 429 // Getting the new SiteInstance from the BrowsingInstance and from another 430 // SiteInstance in the BrowsingInstance should give the same result. 431 EXPECT_EQ(site_instance_b1.get(), 432 site_instance_a1->GetRelatedSiteInstance(url_b1)); 433 434 // A second visit to the original site should return the same SiteInstance. 435 const GURL url_a2("http://www.google.com/2.html"); 436 EXPECT_EQ(site_instance_a1.get(), 437 browsing_instance->GetSiteInstanceForURL(url_a2)); 438 EXPECT_EQ(site_instance_a1.get(), 439 site_instance_a1->GetRelatedSiteInstance(url_a2)); 440 441 // A visit to the original site in a new BrowsingInstance (same or different 442 // browser context) should return a different SiteInstance. 443 TestBrowsingInstance* browsing_instance2 = 444 new TestBrowsingInstance(browser_context.get(), &delete_counter); 445 // Ensure the new SiteInstance is ref counted so that it gets deleted. 446 scoped_refptr<SiteInstanceImpl> site_instance_a2_2( 447 static_cast<SiteInstanceImpl*>( 448 browsing_instance2->GetSiteInstanceForURL(url_a2))); 449 EXPECT_NE(site_instance_a1.get(), site_instance_a2_2.get()); 450 EXPECT_FALSE( 451 site_instance_a1->IsRelatedSiteInstance(site_instance_a2_2.get())); 452 453 // The two SiteInstances for http://google.com should not use the same process 454 // if process-per-site is not enabled. 455 scoped_ptr<RenderProcessHost> process_a1(site_instance_a1->GetProcess()); 456 scoped_ptr<RenderProcessHost> process_a2_2(site_instance_a2_2->GetProcess()); 457 EXPECT_NE(process_a1.get(), process_a2_2.get()); 458 459 // Should be able to see that we do have SiteInstances. 460 EXPECT_TRUE(browsing_instance->HasSiteInstance( 461 GURL("http://mail.google.com"))); 462 EXPECT_TRUE(browsing_instance2->HasSiteInstance( 463 GURL("http://mail.google.com"))); 464 EXPECT_TRUE(browsing_instance->HasSiteInstance( 465 GURL("http://mail.yahoo.com"))); 466 467 // Should be able to see that we don't have SiteInstances. 468 EXPECT_FALSE(browsing_instance->HasSiteInstance( 469 GURL("https://www.google.com"))); 470 EXPECT_FALSE(browsing_instance2->HasSiteInstance( 471 GURL("http://www.yahoo.com"))); 472 473 // browsing_instances will be deleted when their SiteInstances are deleted. 474 // The processes will be unregistered when the RPH scoped_ptrs go away. 475 476 DrainMessageLoops(); 477 } 478 479 // Test to ensure that there is only one RenderProcessHost per site for an 480 // entire BrowserContext, if process-per-site is in use. 481 TEST_F(SiteInstanceTest, OneSiteInstancePerSiteInBrowserContext) { 482 base::CommandLine::ForCurrentProcess()->AppendSwitch( 483 switches::kProcessPerSite); 484 int delete_counter = 0; 485 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 486 TestBrowsingInstance* browsing_instance = 487 new TestBrowsingInstance(browser_context.get(), &delete_counter); 488 489 const GURL url_a1("http://www.google.com/1.html"); 490 scoped_refptr<SiteInstanceImpl> site_instance_a1( 491 static_cast<SiteInstanceImpl*>( 492 browsing_instance->GetSiteInstanceForURL(url_a1))); 493 EXPECT_TRUE(site_instance_a1.get() != NULL); 494 scoped_ptr<RenderProcessHost> process_a1(site_instance_a1->GetProcess()); 495 496 // A separate site should create a separate SiteInstance. 497 const GURL url_b1("http://www.yahoo.com/"); 498 scoped_refptr<SiteInstanceImpl> site_instance_b1( 499 static_cast<SiteInstanceImpl*>( 500 browsing_instance->GetSiteInstanceForURL(url_b1))); 501 EXPECT_NE(site_instance_a1.get(), site_instance_b1.get()); 502 EXPECT_TRUE(site_instance_a1->IsRelatedSiteInstance(site_instance_b1.get())); 503 504 // Getting the new SiteInstance from the BrowsingInstance and from another 505 // SiteInstance in the BrowsingInstance should give the same result. 506 EXPECT_EQ(site_instance_b1.get(), 507 site_instance_a1->GetRelatedSiteInstance(url_b1)); 508 509 // A second visit to the original site should return the same SiteInstance. 510 const GURL url_a2("http://www.google.com/2.html"); 511 EXPECT_EQ(site_instance_a1.get(), 512 browsing_instance->GetSiteInstanceForURL(url_a2)); 513 EXPECT_EQ(site_instance_a1.get(), 514 site_instance_a1->GetRelatedSiteInstance(url_a2)); 515 516 // A visit to the original site in a new BrowsingInstance (same browser 517 // context) should return a different SiteInstance with the same process. 518 TestBrowsingInstance* browsing_instance2 = 519 new TestBrowsingInstance(browser_context.get(), &delete_counter); 520 scoped_refptr<SiteInstanceImpl> site_instance_a1_2( 521 static_cast<SiteInstanceImpl*>( 522 browsing_instance2->GetSiteInstanceForURL(url_a1))); 523 EXPECT_TRUE(site_instance_a1.get() != NULL); 524 EXPECT_NE(site_instance_a1.get(), site_instance_a1_2.get()); 525 EXPECT_EQ(process_a1.get(), site_instance_a1_2->GetProcess()); 526 527 // A visit to the original site in a new BrowsingInstance (different browser 528 // context) should return a different SiteInstance with a different process. 529 scoped_ptr<TestBrowserContext> browser_context2(new TestBrowserContext()); 530 TestBrowsingInstance* browsing_instance3 = 531 new TestBrowsingInstance(browser_context2.get(), &delete_counter); 532 scoped_refptr<SiteInstanceImpl> site_instance_a2_3( 533 static_cast<SiteInstanceImpl*>( 534 browsing_instance3->GetSiteInstanceForURL(url_a2))); 535 EXPECT_TRUE(site_instance_a2_3.get() != NULL); 536 scoped_ptr<RenderProcessHost> process_a2_3(site_instance_a2_3->GetProcess()); 537 EXPECT_NE(site_instance_a1.get(), site_instance_a2_3.get()); 538 EXPECT_NE(process_a1.get(), process_a2_3.get()); 539 540 // Should be able to see that we do have SiteInstances. 541 EXPECT_TRUE(browsing_instance->HasSiteInstance( 542 GURL("http://mail.google.com"))); // visited before 543 EXPECT_TRUE(browsing_instance2->HasSiteInstance( 544 GURL("http://mail.google.com"))); // visited before 545 EXPECT_TRUE(browsing_instance->HasSiteInstance( 546 GURL("http://mail.yahoo.com"))); // visited before 547 548 // Should be able to see that we don't have SiteInstances. 549 EXPECT_FALSE(browsing_instance2->HasSiteInstance( 550 GURL("http://www.yahoo.com"))); // different BI, same browser context 551 EXPECT_FALSE(browsing_instance->HasSiteInstance( 552 GURL("https://www.google.com"))); // not visited before 553 EXPECT_FALSE(browsing_instance3->HasSiteInstance( 554 GURL("http://www.yahoo.com"))); // different BI, different context 555 556 // browsing_instances will be deleted when their SiteInstances are deleted. 557 // The processes will be unregistered when the RPH scoped_ptrs go away. 558 559 DrainMessageLoops(); 560 } 561 562 static SiteInstanceImpl* CreateSiteInstance(BrowserContext* browser_context, 563 const GURL& url) { 564 return static_cast<SiteInstanceImpl*>( 565 SiteInstance::CreateForURL(browser_context, url)); 566 } 567 568 // Test to ensure that pages that require certain privileges are grouped 569 // in processes with similar pages. 570 TEST_F(SiteInstanceTest, ProcessSharingByType) { 571 // This test shouldn't run with --site-per-process or 572 // --enable-strict-site-isolation modes, since they don't allow render 573 // process reuse, which this test explicitly exercises. 574 const base::CommandLine& command_line = 575 *base::CommandLine::ForCurrentProcess(); 576 if (command_line.HasSwitch(switches::kSitePerProcess) || 577 command_line.HasSwitch(switches::kEnableStrictSiteIsolation)) 578 return; 579 580 // On Android by default the number of renderer hosts is unlimited and process 581 // sharing doesn't happen. We set the override so that the test can run 582 // everywhere. 583 RenderProcessHost::SetMaxRendererProcessCount(kMaxRendererProcessCount); 584 585 ChildProcessSecurityPolicyImpl* policy = 586 ChildProcessSecurityPolicyImpl::GetInstance(); 587 588 // Make a bunch of mock renderers so that we hit the limit. 589 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 590 ScopedVector<MockRenderProcessHost> hosts; 591 for (size_t i = 0; i < kMaxRendererProcessCount; ++i) 592 hosts.push_back(new MockRenderProcessHost(browser_context.get())); 593 594 // Create some extension instances and make sure they share a process. 595 scoped_refptr<SiteInstanceImpl> extension1_instance( 596 CreateSiteInstance(browser_context.get(), 597 GURL(kPrivilegedScheme + std::string("://foo/bar")))); 598 set_privileged_process_id(extension1_instance->GetProcess()->GetID()); 599 600 scoped_refptr<SiteInstanceImpl> extension2_instance( 601 CreateSiteInstance(browser_context.get(), 602 GURL(kPrivilegedScheme + std::string("://baz/bar")))); 603 604 scoped_ptr<RenderProcessHost> extension_host( 605 extension1_instance->GetProcess()); 606 EXPECT_EQ(extension1_instance->GetProcess(), 607 extension2_instance->GetProcess()); 608 609 // Create some WebUI instances and make sure they share a process. 610 scoped_refptr<SiteInstanceImpl> webui1_instance(CreateSiteInstance( 611 browser_context.get(), GURL(kChromeUIScheme + std::string("://newtab")))); 612 policy->GrantWebUIBindings(webui1_instance->GetProcess()->GetID()); 613 614 scoped_refptr<SiteInstanceImpl> webui2_instance( 615 CreateSiteInstance(browser_context.get(), 616 GURL(kChromeUIScheme + std::string("://history")))); 617 618 scoped_ptr<RenderProcessHost> dom_host(webui1_instance->GetProcess()); 619 EXPECT_EQ(webui1_instance->GetProcess(), webui2_instance->GetProcess()); 620 621 // Make sure none of differing privilege processes are mixed. 622 EXPECT_NE(extension1_instance->GetProcess(), webui1_instance->GetProcess()); 623 624 for (size_t i = 0; i < kMaxRendererProcessCount; ++i) { 625 EXPECT_NE(extension1_instance->GetProcess(), hosts[i]); 626 EXPECT_NE(webui1_instance->GetProcess(), hosts[i]); 627 } 628 629 DrainMessageLoops(); 630 631 // Disable the process limit override. 632 RenderProcessHost::SetMaxRendererProcessCount(0u); 633 } 634 635 // Test to ensure that HasWrongProcessForURL behaves properly for different 636 // types of URLs. 637 TEST_F(SiteInstanceTest, HasWrongProcessForURL) { 638 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 639 scoped_ptr<RenderProcessHost> host; 640 scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>( 641 SiteInstance::Create(browser_context.get()))); 642 643 EXPECT_FALSE(instance->HasSite()); 644 EXPECT_TRUE(instance->GetSiteURL().is_empty()); 645 646 instance->SetSite(GURL("http://evernote.com/")); 647 EXPECT_TRUE(instance->HasSite()); 648 649 // Check prior to "assigning" a process to the instance, which is expected 650 // to return false due to not being attached to any process yet. 651 EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://google.com"))); 652 653 // The call to GetProcess actually creates a new real process, which works 654 // fine, but might be a cause for problems in different contexts. 655 host.reset(instance->GetProcess()); 656 EXPECT_TRUE(host.get() != NULL); 657 EXPECT_TRUE(instance->HasProcess()); 658 659 EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://evernote.com"))); 660 EXPECT_FALSE(instance->HasWrongProcessForURL( 661 GURL("javascript:alert(document.location.href);"))); 662 663 EXPECT_TRUE(instance->HasWrongProcessForURL(GURL("chrome://settings"))); 664 665 // Test that WebUI SiteInstances reject normal web URLs. 666 const GURL webui_url("chrome://settings"); 667 scoped_refptr<SiteInstanceImpl> webui_instance(static_cast<SiteInstanceImpl*>( 668 SiteInstance::Create(browser_context.get()))); 669 webui_instance->SetSite(webui_url); 670 scoped_ptr<RenderProcessHost> webui_host(webui_instance->GetProcess()); 671 672 // Simulate granting WebUI bindings for the process. 673 ChildProcessSecurityPolicyImpl::GetInstance()->GrantWebUIBindings( 674 webui_host->GetID()); 675 676 EXPECT_TRUE(webui_instance->HasProcess()); 677 EXPECT_FALSE(webui_instance->HasWrongProcessForURL(webui_url)); 678 EXPECT_TRUE(webui_instance->HasWrongProcessForURL(GURL("http://google.com"))); 679 680 // WebUI uses process-per-site, so another instance will use the same process 681 // even if we haven't called GetProcess yet. Make sure HasWrongProcessForURL 682 // doesn't crash (http://crbug.com/137070). 683 scoped_refptr<SiteInstanceImpl> webui_instance2( 684 static_cast<SiteInstanceImpl*>( 685 SiteInstance::Create(browser_context.get()))); 686 webui_instance2->SetSite(webui_url); 687 EXPECT_FALSE(webui_instance2->HasWrongProcessForURL(webui_url)); 688 EXPECT_TRUE( 689 webui_instance2->HasWrongProcessForURL(GURL("http://google.com"))); 690 691 DrainMessageLoops(); 692 } 693 694 // Test to ensure that HasWrongProcessForURL behaves properly even when 695 // --site-per-process is used (http://crbug.com/160671). 696 TEST_F(SiteInstanceTest, HasWrongProcessForURLInSitePerProcess) { 697 base::CommandLine::ForCurrentProcess()->AppendSwitch( 698 switches::kSitePerProcess); 699 700 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 701 scoped_ptr<RenderProcessHost> host; 702 scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>( 703 SiteInstance::Create(browser_context.get()))); 704 705 instance->SetSite(GURL("http://evernote.com/")); 706 EXPECT_TRUE(instance->HasSite()); 707 708 // Check prior to "assigning" a process to the instance, which is expected 709 // to return false due to not being attached to any process yet. 710 EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://google.com"))); 711 712 // The call to GetProcess actually creates a new real process, which works 713 // fine, but might be a cause for problems in different contexts. 714 host.reset(instance->GetProcess()); 715 EXPECT_TRUE(host.get() != NULL); 716 EXPECT_TRUE(instance->HasProcess()); 717 718 EXPECT_FALSE(instance->HasWrongProcessForURL(GURL("http://evernote.com"))); 719 EXPECT_FALSE(instance->HasWrongProcessForURL( 720 GURL("javascript:alert(document.location.href);"))); 721 722 EXPECT_TRUE(instance->HasWrongProcessForURL(GURL("chrome://settings"))); 723 724 DrainMessageLoops(); 725 } 726 727 // Test that we do not reuse a process in process-per-site mode if it has the 728 // wrong bindings for its URL. http://crbug.com/174059. 729 TEST_F(SiteInstanceTest, ProcessPerSiteWithWrongBindings) { 730 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 731 scoped_ptr<RenderProcessHost> host; 732 scoped_ptr<RenderProcessHost> host2; 733 scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>( 734 SiteInstance::Create(browser_context.get()))); 735 736 EXPECT_FALSE(instance->HasSite()); 737 EXPECT_TRUE(instance->GetSiteURL().is_empty()); 738 739 // Simulate navigating to a WebUI URL in a process that does not have WebUI 740 // bindings. This already requires bypassing security checks. 741 const GURL webui_url("chrome://settings"); 742 instance->SetSite(webui_url); 743 EXPECT_TRUE(instance->HasSite()); 744 745 // The call to GetProcess actually creates a new real process. 746 host.reset(instance->GetProcess()); 747 EXPECT_TRUE(host.get() != NULL); 748 EXPECT_TRUE(instance->HasProcess()); 749 750 // Without bindings, this should look like the wrong process. 751 EXPECT_TRUE(instance->HasWrongProcessForURL(webui_url)); 752 753 // WebUI uses process-per-site, so another instance would normally use the 754 // same process. Make sure it doesn't use the same process if the bindings 755 // are missing. 756 scoped_refptr<SiteInstanceImpl> instance2( 757 static_cast<SiteInstanceImpl*>( 758 SiteInstance::Create(browser_context.get()))); 759 instance2->SetSite(webui_url); 760 host2.reset(instance2->GetProcess()); 761 EXPECT_TRUE(host2.get() != NULL); 762 EXPECT_TRUE(instance2->HasProcess()); 763 EXPECT_NE(host.get(), host2.get()); 764 765 DrainMessageLoops(); 766 } 767 768 // Test that we do not register processes with empty sites for process-per-site 769 // mode. 770 TEST_F(SiteInstanceTest, NoProcessPerSiteForEmptySite) { 771 base::CommandLine::ForCurrentProcess()->AppendSwitch( 772 switches::kProcessPerSite); 773 scoped_ptr<TestBrowserContext> browser_context(new TestBrowserContext()); 774 scoped_ptr<RenderProcessHost> host; 775 scoped_refptr<SiteInstanceImpl> instance(static_cast<SiteInstanceImpl*>( 776 SiteInstance::Create(browser_context.get()))); 777 778 instance->SetSite(GURL()); 779 EXPECT_TRUE(instance->HasSite()); 780 EXPECT_TRUE(instance->GetSiteURL().is_empty()); 781 host.reset(instance->GetProcess()); 782 783 EXPECT_FALSE(RenderProcessHostImpl::GetProcessHostForSite( 784 browser_context.get(), GURL())); 785 786 DrainMessageLoops(); 787 } 788 789 } // namespace content 790