| // Copyright 2013 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <stddef.h> |
| |
| #include <limits> |
| #include <memory> |
| #include <vector> |
| |
| #include "base/command_line.h" |
| #include "base/run_loop.h" |
| #include "base/test/metrics/histogram_tester.h" |
| #include "base/test/scoped_feature_list.h" |
| #include "build/build_config.h" |
| #include "build/chromeos_buildflags.h" |
| #include "content/browser/child_process_security_policy_impl.h" |
| #include "content/browser/renderer_host/render_process_host_impl.h" |
| #include "content/public/browser/browser_context.h" |
| #include "content/public/browser/content_browser_client.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/test/mock_render_process_host.h" |
| #include "content/public/test/navigation_simulator.h" |
| #include "content/public/test/scoped_web_ui_controller_factory_registration.h" |
| #include "content/public/test/test_browser_context.h" |
| #include "content/public/test/test_utils.h" |
| #include "content/public/test/web_ui_browsertest_util.h" |
| #include "content/test/storage_partition_test_helpers.h" |
| #include "content/test/test_render_frame_host.h" |
| #include "content/test/test_render_view_host.h" |
| #include "content/test/test_web_contents.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "third_party/blink/public/common/frame/frame_policy.h" |
| #include "third_party/blink/public/common/tokens/tokens.h" |
| #include "third_party/blink/public/mojom/frame/frame_owner_properties.mojom.h" |
| #include "ui/webui/untrusted_web_ui_browsertest_util.h" |
| |
| namespace content { |
| |
| class RenderProcessHostUnitTest : public RenderViewHostImplTestHarness { |
| public: |
| scoped_refptr<SiteInstanceImpl> CreateForUrl(const GURL& url) { |
| return SiteInstanceImpl::CreateForTesting(browser_context(), url); |
| } |
| |
| scoped_refptr<SiteInstanceImpl> CreateForServiceWorker( |
| const GURL& url, |
| bool can_reuse_process = false) { |
| return SiteInstanceImpl::CreateForServiceWorker( |
| browser_context(), |
| UrlInfo::CreateForTesting(url, |
| CreateStoragePartitionConfigForTesting()), |
| can_reuse_process); |
| } |
| }; |
| |
| // Tests that guest RenderProcessHosts are not considered suitable hosts when |
| // searching for RenderProcessHost. |
| TEST_F(RenderProcessHostUnitTest, GuestsAreNotSuitableHosts) { |
| GURL test_url("http://foo.com"); |
| |
| MockRenderProcessHost guest_host(browser_context(), |
| /*is_for_guest_only=*/true); |
| |
| scoped_refptr<SiteInstanceImpl> site_instance = CreateForUrl(test_url); |
| EXPECT_FALSE(RenderProcessHostImpl::IsSuitableHost( |
| &guest_host, site_instance->GetIsolationContext(), |
| site_instance->GetSiteInfo())); |
| EXPECT_TRUE(RenderProcessHostImpl::IsSuitableHost( |
| process(), site_instance->GetIsolationContext(), |
| site_instance->GetSiteInfo())); |
| EXPECT_EQ(process(), |
| RenderProcessHostImpl::GetExistingProcessHost(site_instance.get())); |
| } |
| |
| #if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS_ASH) |
| TEST_F(RenderProcessHostUnitTest, RendererProcessLimit) { |
| // This test shouldn't run with --site-per-process mode, which prohibits |
| // the renderer process reuse this test explicitly exercises. |
| if (AreAllSitesIsolatedForTesting()) |
| return; |
| |
| const size_t max_renderer_process_count = |
| RenderProcessHostImpl::GetPlatformMaxRendererProcessCount(); |
| |
| // Verify that the limit is between 1 and |max_renderer_process_count|. |
| EXPECT_GT(RenderProcessHostImpl::GetMaxRendererProcessCount(), 0u); |
| EXPECT_LE(RenderProcessHostImpl::GetMaxRendererProcessCount(), |
| max_renderer_process_count); |
| |
| // Add dummy process hosts to saturate the limit. |
| ASSERT_NE(0u, max_renderer_process_count); |
| std::vector<std::unique_ptr<MockRenderProcessHost>> hosts; |
| for (size_t i = 0; i < max_renderer_process_count; ++i) { |
| hosts.push_back(std::make_unique<MockRenderProcessHost>(browser_context())); |
| } |
| |
| // Verify that the renderer sharing will happen. |
| GURL test_url("http://foo.com"); |
| EXPECT_TRUE(RenderProcessHostImpl::ShouldTryToUseExistingProcessHost( |
| browser_context(), test_url)); |
| } |
| #endif |
| |
| #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_CHROMEOS_ASH) |
| TEST_F(RenderProcessHostUnitTest, NoRendererProcessLimitOnAndroidOrChromeOS) { |
| // Add a few dummy process hosts. |
| static constexpr size_t kMaxRendererProcessCountForTesting = 82; |
| std::vector<std::unique_ptr<MockRenderProcessHost>> hosts; |
| for (size_t i = 0; i < kMaxRendererProcessCountForTesting; ++i) { |
| hosts.push_back(std::make_unique<MockRenderProcessHost>(browser_context())); |
| } |
| |
| // Verify that the renderer sharing still won't happen. |
| GURL test_url("http://foo.com"); |
| EXPECT_FALSE(RenderProcessHostImpl::ShouldTryToUseExistingProcessHost( |
| browser_context(), test_url)); |
| } |
| #endif |
| |
| // Tests that RenderProcessHost reuse considers committed sites correctly. |
| TEST_F(RenderProcessHostUnitTest, ReuseCommittedSite) { |
| const GURL kUrl1("http://foo.com"); |
| const GURL kUrl2("http://bar.com"); |
| |
| // BFCache is disabled for this test because the process for |kUrl1| is |
| // cached and reused after the navigation to |kUrl2| with BFCache enabled. The |
| // test expects that a new process (either spare or created) is used instead. |
| contents()->GetController().GetBackForwardCache().DisableForTesting( |
| BackForwardCache::TEST_REQUIRES_NO_CACHING); |
| |
| // At first, trying to get a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process. |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl1); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| site_instance->GetLastProcessAssignmentOutcome()); |
| |
| // Have the main frame navigate to the first url. Getting a RenderProcessHost |
| // with the REUSE_PENDING_OR_COMMITTED_SITE policy should now return the |
| // process of the main RFH. |
| NavigateAndCommit(kUrl1); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl1); |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| site_instance->GetLastProcessAssignmentOutcome()); |
| |
| // Navigate away. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should again return a new process. |
| NavigateAndCommit(kUrl2); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl1); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| EXPECT_EQ(RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes() |
| ? SiteInstanceProcessAssignment::USED_SPARE_PROCESS |
| : SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| site_instance->GetLastProcessAssignmentOutcome()); |
| |
| // Now add a subframe that navigates to kUrl1. Getting a RenderProcessHost |
| // with the REUSE_PENDING_OR_COMMITTED_SITE policy for kUrl1 should now |
| // return the process of the subframe RFH. |
| std::string unique_name("uniqueName0"); |
| main_test_rfh()->OnCreateChildFrame( |
| process()->GetNextRoutingID(), |
| TestRenderFrameHost::CreateStubFrameRemote(), |
| TestRenderFrameHost::CreateStubBrowserInterfaceBrokerReceiver(), |
| TestRenderFrameHost::CreateStubPolicyContainerBindParams(), |
| TestRenderFrameHost::CreateStubAssociatedInterfaceProviderReceiver(), |
| blink::mojom::TreeScopeType::kDocument, std::string(), unique_name, false, |
| blink::LocalFrameToken(), base::UnguessableToken::Create(), |
| blink::DocumentToken(), blink::FramePolicy(), |
| blink::mojom::FrameOwnerProperties(), |
| blink::FrameOwnerElementType::kIframe, ukm::kInvalidSourceId); |
| TestRenderFrameHost* subframe = |
| static_cast<TestRenderFrameHost*>(contents() |
| ->GetPrimaryFrameTree() |
| .root() |
| ->child_at(0) |
| ->current_frame_host()); |
| subframe = static_cast<TestRenderFrameHost*>( |
| NavigationSimulator::NavigateAndCommitFromDocument(kUrl1, subframe)); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl1); |
| EXPECT_EQ(subframe->GetProcess(), site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| site_instance->GetLastProcessAssignmentOutcome()); |
| } |
| |
| // Check that only new processes that haven't yet hosted any web content are |
| // allowed to be reused to host a site requiring a dedicated process. |
| TEST_F(RenderProcessHostUnitTest, IsUnused) { |
| const GURL kUrl1("http://foo.com"); |
| |
| // A process for a SiteInstance that has no site should be able to host any |
| // site that requires a dedicated process. |
| EXPECT_FALSE(main_test_rfh()->GetSiteInstance()->HasSite()); |
| EXPECT_TRUE(main_test_rfh()->GetProcess()->IsUnused()); |
| { |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::Create(browser_context()); |
| EXPECT_FALSE(site_instance->HasSite()); |
| EXPECT_TRUE(site_instance->GetProcess()->IsUnused()); |
| } |
| |
| // Navigation should mark the process as unable to become a dedicated process |
| // for arbitrary sites. |
| NavigateAndCommit(kUrl1); |
| EXPECT_FALSE(main_test_rfh()->GetProcess()->IsUnused()); |
| |
| // A process for a SiteInstance with a preassigned site should be considered |
| // "used" from the point the process is created via GetProcess(). |
| { |
| scoped_refptr<SiteInstanceImpl> site_instance = CreateForUrl(kUrl1); |
| EXPECT_FALSE(site_instance->GetProcess()->IsUnused()); |
| } |
| } |
| |
| TEST_F(RenderProcessHostUnitTest, ReuseUnmatchedServiceWorkerProcess) { |
| const GURL kUrl("https://foo.com"); |
| |
| // Gets a RenderProcessHost for an unmatched service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_host1 = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker with DEFAULT reuse policy |
| // should not reuse the existing service worker's process. We create this |
| // second service worker to test the "find the newest process" logic later. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_host2 = sw_site_instance2->GetProcess(); |
| EXPECT_NE(sw_host1, sw_host2); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to the same site must reuse |
| // the newest unmatched service worker's process (i.e., sw_host2). |
| scoped_refptr<SiteInstanceImpl> site_instance1 = CreateForUrl(kUrl); |
| EXPECT_EQ(sw_host2, site_instance1->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to the same site must reuse |
| // the newest unmatched service worker's process (i.e., sw_host1). sw_host2 |
| // is no longer unmatched, so sw_host1 is now the newest (and only) process |
| // with a corresponding unmatched service worker. |
| scoped_refptr<SiteInstanceImpl> site_instance2 = CreateForUrl(kUrl); |
| EXPECT_EQ(sw_host1, site_instance2->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| site_instance2->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation should return a new process |
| // because there is no unmatched service worker's process. |
| scoped_refptr<SiteInstanceImpl> site_instance3 = CreateForUrl(kUrl); |
| EXPECT_NE(sw_host1, site_instance3->GetProcess()); |
| EXPECT_NE(sw_host2, site_instance3->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| site_instance3->GetLastProcessAssignmentOutcome()); |
| } |
| |
| class UnsuitableHostContentBrowserClient : public ContentBrowserClient { |
| public: |
| UnsuitableHostContentBrowserClient() {} |
| ~UnsuitableHostContentBrowserClient() override {} |
| |
| private: |
| bool IsSuitableHost(RenderProcessHost* process_host, |
| const GURL& site_url) override { |
| return false; |
| } |
| }; |
| |
| // Check that an unmatched ServiceWorker process is not reused when it's not a |
| // suitable host for the destination URL. See https://crbug.com/782349. |
| TEST_F(RenderProcessHostUnitTest, |
| DontReuseUnsuitableUnmatchedServiceWorkerProcess) { |
| const GURL kUrl("https://foo.com"); |
| |
| // Gets a RenderProcessHost for an unmatched service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_host = sw_site_instance->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance->GetLastProcessAssignmentOutcome()); |
| |
| // Simulate a situation where |sw_host| won't be considered suitable for |
| // future navigations to |kUrl|. In https://crbug.com/782349, this happened |
| // when |kUrl| corresponded to a nonexistent extension, but |
| // chrome-extension:// URLs can't be tested inside content/. Instead, |
| // install a ContentBrowserClient which will return false when IsSuitableHost |
| // is consulted. |
| UnsuitableHostContentBrowserClient modified_client; |
| ContentBrowserClient* regular_client = |
| SetBrowserClientForTesting(&modified_client); |
| |
| // Discard the spare, so it cannot be considered by the GetProcess call below. |
| RenderProcessHostImpl::DiscardSpareRenderProcessHostForTesting(); |
| |
| // Now, getting a RenderProcessHost for a navigation to the same site should |
| // not reuse the unmatched service worker's process (i.e., |sw_host|), as |
| // it's unsuitable. |
| scoped_refptr<SiteInstanceImpl> site_instance = CreateForUrl(kUrl); |
| EXPECT_NE(sw_host, site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance->GetLastProcessAssignmentOutcome()); |
| |
| SetBrowserClientForTesting(regular_client); |
| } |
| |
| TEST_F(RenderProcessHostUnitTest, ReuseServiceWorkerProcessForServiceWorker) { |
| const GURL kUrl("https://foo.com"); |
| |
| // Gets a RenderProcessHost for a service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kUrl, |
| /*can_reuse_process=*/true); |
| RenderProcessHost* sw_host1 = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker with DEFAULT reuse policy |
| // should not reuse the existing service worker's process. This is because |
| // we use DEFAULT reuse policy for a service worker when we have failed to |
| // start the service worker and want to use a new process. We create this |
| // second service worker to test the "find the newest process" logic later. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_host2 = sw_site_instance2->GetProcess(); |
| EXPECT_NE(sw_host1, sw_host2); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker of the same site with |
| // REUSE_PENDING_OR_COMMITTED_SITE reuse policy should reuse the newest |
| // unmatched service worker's process (i.e., sw_host2). |
| scoped_refptr<SiteInstanceImpl> sw_site_instance3 = |
| CreateForServiceWorker(kUrl, |
| /*can_reuse_process=*/true); |
| RenderProcessHost* sw_host3 = sw_site_instance3->GetProcess(); |
| EXPECT_EQ(sw_host2, sw_host3); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| sw_site_instance3->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker of the same site with |
| // REUSE_PENDING_OR_COMMITTED_SITE reuse policy should reuse the newest |
| // unmatched service worker's process (i.e., sw_host2). sw_host3 doesn't cause |
| // sw_host2 to be considered matched, so we can keep putting more service |
| // workers in that process. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance4 = |
| CreateForServiceWorker(kUrl, |
| /*can_reuse_process=*/true); |
| RenderProcessHost* sw_host4 = sw_site_instance4->GetProcess(); |
| EXPECT_EQ(sw_host2, sw_host4); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| sw_site_instance4->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to the same site must reuse |
| // the newest unmatched service worker's process (i.e., sw_host2). |
| scoped_refptr<SiteInstanceImpl> site_instance1 = CreateForUrl(kUrl); |
| EXPECT_EQ(sw_host2, site_instance1->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to the same site must reuse |
| // the newest unmatched service worker's process (i.e., sw_host1). sw_host2 |
| // is no longer unmatched, so sw_host1 is now the newest (and only) process |
| // with a corresponding unmatched service worker. |
| scoped_refptr<SiteInstanceImpl> site_instance2 = CreateForUrl(kUrl); |
| EXPECT_EQ(sw_host1, site_instance2->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| site_instance2->GetLastProcessAssignmentOutcome()); |
| } |
| |
| TEST_F(RenderProcessHostUnitTest, |
| ReuseServiceWorkerProcessInProcessPerSitePolicy) { |
| base::CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kProcessPerSite); |
| const GURL kUrl("http://foo.com"); |
| |
| // Gets a RenderProcessHost for a service worker with process-per-site flag. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_host1 = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker of the same site with |
| // process-per-site flag should reuse the unmatched service worker's process. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_host2 = sw_site_instance2->GetProcess(); |
| EXPECT_EQ(sw_host1, sw_host2); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to the same site with |
| // process-per-site flag should reuse the unmatched service worker's process. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance3 = CreateForUrl(kUrl); |
| RenderProcessHost* sw_host3 = sw_site_instance3->GetProcess(); |
| EXPECT_EQ(sw_host1, sw_host3); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| sw_site_instance3->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to the same site again with |
| // process-per-site flag should reuse the unmatched service worker's process. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance4 = CreateForUrl(kUrl); |
| RenderProcessHost* sw_host4 = sw_site_instance4->GetProcess(); |
| EXPECT_EQ(sw_host1, sw_host4); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| sw_site_instance4->GetLastProcessAssignmentOutcome()); |
| } |
| |
| TEST_F(RenderProcessHostUnitTest, DoNotReuseOtherSiteServiceWorkerProcess) { |
| const GURL kUrl1("https://foo.com"); |
| const GURL kUrl2("https://bar.com"); |
| |
| // Gets a RenderProcessHost for a service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kUrl1); |
| RenderProcessHost* sw_host1 = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker of a different site should |
| // return a new process because there is no reusable process. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = CreateForUrl(kUrl2); |
| EXPECT_NE(sw_host1, sw_site_instance2->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| } |
| |
| class RenderProcessHostWebUIUnitTest : public RenderProcessHostUnitTest { |
| public: |
| void SetUp() override { |
| RenderProcessHostUnitTest::SetUp(); |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kEnableServiceWorkersForChromeScheme); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| TEST_F(RenderProcessHostWebUIUnitTest, |
| DontReuseServiceWorkerProcessForDifferentWebUI) { |
| ScopedWebUIConfigRegistration config_registration1( |
| std::make_unique<TestWebUIConfig>("test-host")); |
| ScopedWebUIConfigRegistration config_registration2( |
| std::make_unique<TestWebUIConfig>("second-host")); |
| |
| const GURL kWebUI1("chrome://test-host/"); |
| const GURL kWebUI2("chrome://second-host/"); |
| |
| // Gets a RenderProcessHost for an unmatched service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kWebUI1); |
| RenderProcessHost* sw_host = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting RenderProcessHost for a service worker for a different WebUI |
| // should return a new process because there is no reusable process. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = CreateForUrl(kWebUI2); |
| EXPECT_NE(sw_host, sw_site_instance2->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| } |
| |
| TEST_F(RenderProcessHostWebUIUnitTest, DontReuseServiceWorkerProcessForWebUrl) { |
| ScopedWebUIConfigRegistration config_registration1( |
| std::make_unique<TestWebUIConfig>("test-host")); |
| |
| const GURL kWebUI1("chrome://test-host/"); |
| |
| // Gets a RenderProcessHost for an unmatched service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kWebUI1); |
| RenderProcessHost* sw_host = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| const GURL kWebUrl("https://test.example/"); |
| |
| // Getting RenderProcessHost for a service worker for a regular site should |
| // return a new process because there is no reusable process. |
| scoped_refptr<SiteInstanceImpl> web_sw_site_instance = |
| CreateForServiceWorker(kWebUrl); |
| EXPECT_NE(sw_host, web_sw_site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| web_sw_site_instance->GetLastProcessAssignmentOutcome()); |
| |
| // Getting RenderProcessHost for a navigation to a regular site should |
| // re-use the Web Service Worker process and not the WebUI one. |
| scoped_refptr<SiteInstanceImpl> web_site_instance = CreateForUrl(kWebUrl); |
| EXPECT_NE(sw_host, web_site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| web_site_instance->GetLastProcessAssignmentOutcome()); |
| } |
| |
| // Tests that Service Worker processes for WebUIs are not re-used even |
| // for the same WebUI. Ideally we would re-use the process if it's for |
| // the same WebUI but we currently don't because of crbug.com/1158277. |
| TEST_F(RenderProcessHostWebUIUnitTest, |
| DontReuseServiceWorkerProcessForSameWebUI) { |
| ScopedWebUIConfigRegistration config_registration( |
| std::make_unique<TestWebUIConfig>("test-host")); |
| const GURL kUrl("chrome://test-host"); |
| |
| // Gets a RenderProcessHost for a service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kUrl, |
| /*can_reuse_process=*/true); |
| RenderProcessHost* sw_host1 = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker with DEFAULT reuse policy |
| // should not reuse the existing service worker's process. This is because |
| // we use DEFAULT reuse policy for a service worker when we have failed to |
| // start the service worker and want to use a new process. We create this |
| // second service worker to test the "find the newest process" logic later. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_host2 = sw_site_instance2->GetProcess(); |
| EXPECT_NE(sw_host1, sw_host2); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker of the same WebUI with |
| // the same WebUI and allow process reuse policy doesn't reuse any service |
| // worker processes. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance3 = |
| CreateForServiceWorker(kUrl, |
| /*can_reuse_process=*/true); |
| RenderProcessHost* sw_host3 = sw_site_instance3->GetProcess(); |
| EXPECT_NE(sw_host1, sw_host3); |
| EXPECT_NE(sw_host2, sw_host3); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance3->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to the same WebUI doesn't |
| // reuse any service worker's processes. |
| scoped_refptr<SiteInstanceImpl> site_instance1 = CreateForUrl(kUrl); |
| EXPECT_NE(sw_host1, site_instance1->GetProcess()); |
| EXPECT_NE(sw_host2, site_instance1->GetProcess()); |
| EXPECT_NE(sw_host3, site_instance1->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to a web URL doesn't reuse any |
| // service worker's processes. |
| const GURL kWebUrl("https://test.example"); |
| scoped_refptr<SiteInstanceImpl> web_site_instance = CreateForUrl(kWebUrl); |
| EXPECT_NE(sw_host1, web_site_instance->GetProcess()); |
| EXPECT_NE(sw_host2, web_site_instance->GetProcess()); |
| EXPECT_NE(sw_host3, web_site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| web_site_instance->GetLastProcessAssignmentOutcome()); |
| } |
| |
| class RenderProcessHostUntrustedWebUIUnitTest |
| : public RenderProcessHostUnitTest { |
| public: |
| void SetUp() override { |
| RenderProcessHostUnitTest::SetUp(); |
| scoped_feature_list_.InitAndEnableFeature( |
| features::kEnableServiceWorkersForChromeUntrusted); |
| } |
| |
| private: |
| base::test::ScopedFeatureList scoped_feature_list_; |
| }; |
| |
| TEST_F(RenderProcessHostUntrustedWebUIUnitTest, |
| DontReuseServiceWorkerProcessForDifferentWebUI) { |
| ScopedWebUIConfigRegistration config_registration1( |
| std::make_unique<ui::TestUntrustedWebUIConfig>("test-host")); |
| ScopedWebUIConfigRegistration config_registration2( |
| std::make_unique<ui::TestUntrustedWebUIConfig>("second-host")); |
| |
| const GURL kWebUI1("chrome-untrusted://test-host/"); |
| const GURL kWebUI2("chrome-untrusted://second-host/"); |
| |
| // Gets a RenderProcessHost for an unmatched service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kWebUI1); |
| RenderProcessHost* sw_host = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting RenderProcessHost for a service worker for a different WebUI |
| // should return a new process because there is no reusable process. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = CreateForUrl(kWebUI2); |
| EXPECT_NE(sw_host, sw_site_instance2->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| } |
| |
| TEST_F(RenderProcessHostUntrustedWebUIUnitTest, |
| DontReuseServiceWorkerProcessForWebUrl) { |
| ScopedWebUIConfigRegistration config_registration1( |
| std::make_unique<ui::TestUntrustedWebUIConfig>("test-host")); |
| |
| const GURL kWebUI1("chrome-untrusted://test-host/"); |
| |
| // Gets a RenderProcessHost for an unmatched service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kWebUI1); |
| RenderProcessHost* sw_host = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| const GURL kWebUrl("https://test.example/"); |
| |
| // Getting RenderProcessHost for a service worker for a regular site should |
| // return a new process because there is no reusable process. |
| scoped_refptr<SiteInstanceImpl> web_sw_site_instance = |
| CreateForServiceWorker(kWebUrl); |
| EXPECT_NE(sw_host, web_sw_site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| web_sw_site_instance->GetLastProcessAssignmentOutcome()); |
| |
| // Getting RenderProcessHost for a navigation to a regular site should |
| // re-use the Web Service Worker process and not the WebUI one. |
| scoped_refptr<SiteInstanceImpl> web_site_instance = CreateForUrl(kWebUrl); |
| EXPECT_NE(sw_host, web_site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| web_site_instance->GetLastProcessAssignmentOutcome()); |
| } |
| |
| // Tests that Service Worker processes for WebUIs are not re-used even |
| // for the same WebUI. Ideally we would re-use the process if it's for |
| // the same WebUI but we currently don't because of crbug.com/1158277. |
| TEST_F(RenderProcessHostUntrustedWebUIUnitTest, |
| DontReuseServiceWorkerProcessForSameWebUI) { |
| ScopedWebUIConfigRegistration config_registration( |
| std::make_unique<ui::TestUntrustedWebUIConfig>("test-host")); |
| const GURL kUrl("chrome-untrusted://test-host"); |
| |
| // Gets a RenderProcessHost for a service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance1 = |
| CreateForServiceWorker(kUrl, |
| /*can_reuse_process=*/true); |
| RenderProcessHost* sw_host1 = sw_site_instance1->GetProcess(); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker with DEFAULT reuse policy |
| // should not reuse the existing service worker's process. This is because |
| // we use DEFAULT reuse policy for a service worker when we have failed to |
| // start the service worker and want to use a new process. We create this |
| // second service worker to test the "find the newest process" logic later. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_host2 = sw_site_instance2->GetProcess(); |
| EXPECT_NE(sw_host1, sw_host2); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a service worker of the same WebUI with |
| // REUSE_PENDING_OR_COMMITTED_SITE reuse policy doesn't reuse any service |
| // worker processes. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance3 = |
| CreateForServiceWorker(kUrl, |
| /*can_reuse_process=*/true); |
| RenderProcessHost* sw_host3 = sw_site_instance3->GetProcess(); |
| EXPECT_NE(sw_host1, sw_host3); |
| EXPECT_NE(sw_host2, sw_host3); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| sw_site_instance3->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to the same WebUI doesn't |
| // reuse any service worker's processes. |
| scoped_refptr<SiteInstanceImpl> site_instance1 = CreateForUrl(kUrl); |
| EXPECT_NE(sw_host1, site_instance1->GetProcess()); |
| EXPECT_NE(sw_host2, site_instance1->GetProcess()); |
| EXPECT_NE(sw_host3, site_instance1->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| site_instance1->GetLastProcessAssignmentOutcome()); |
| |
| // Getting a RenderProcessHost for a navigation to a web URL doesn't reuse any |
| // service worker's processes. |
| const GURL kWebUrl("https://test.example"); |
| scoped_refptr<SiteInstanceImpl> web_site_instance = CreateForUrl(kWebUrl); |
| EXPECT_NE(sw_host1, web_site_instance->GetProcess()); |
| EXPECT_NE(sw_host2, web_site_instance->GetProcess()); |
| EXPECT_NE(sw_host3, web_site_instance->GetProcess()); |
| EXPECT_EQ(SiteInstanceProcessAssignment::CREATED_NEW_PROCESS, |
| web_site_instance->GetLastProcessAssignmentOutcome()); |
| } |
| |
| // Tests that RenderProcessHost will not consider reusing a process that has |
| // committed an error page. |
| TEST_F(RenderProcessHostUnitTest, DoNotReuseError) { |
| // This test depends on a network error occurring on back navigation. |
| // This cannot happen if the page is restored from the back-forward |
| // cache, because no network requests would be made. |
| contents()->GetController().GetBackForwardCache().DisableForTesting( |
| BackForwardCache::TEST_REQUIRES_NO_CACHING); |
| const GURL kUrl1("http://foo.com"); |
| const GURL kUrl2("http://bar.com"); |
| |
| // Isolate |kUrl1| so we can't get a default SiteInstance for it. |
| ChildProcessSecurityPolicyImpl::GetInstance()->AddFutureIsolatedOrigins( |
| {url::Origin::Create(kUrl1)}, |
| ChildProcessSecurityPolicy::IsolatedOriginSource::TEST, |
| browser_context()); |
| |
| // At first, trying to get a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process. |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl1); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Have the main frame navigate to the first url. Getting a RenderProcessHost |
| // with the REUSE_PENDING_OR_COMMITTED_SITE policy should now return the |
| // process of the main RFH. |
| NavigateAndCommit(kUrl1); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl1); |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Navigate away. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should again return a new process. |
| NavigateAndCommit(kUrl2); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl1); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Navigate back and simulate an error. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process. |
| NavigationSimulator::GoBackAndFail(contents(), net::ERR_TIMED_OUT); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl1); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| } |
| |
| // Tests that RenderProcessHost reuse considers navigations correctly. |
| TEST_F(RenderProcessHostUnitTest, ReuseNavigationProcess) { |
| const GURL kUrl1("http://foo.com"); |
| const GURL kUrl2("http://bar.com"); |
| |
| // At first, trying to get a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process. |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl1); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Start a navigation. Now Getting RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return the current process. |
| auto navigation = |
| NavigationSimulator::CreateRendererInitiated(kUrl1, main_test_rfh()); |
| navigation->Start(); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl1); |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Finish the navigation and start a new cross-site one. Getting |
| // RenderProcessHost with the REUSE_PENDING_OR_COMMITTED_SITE policy should |
| // return the process of the speculative RenderFrameHost. |
| navigation->Commit(); |
| navigation = NavigationSimulator::CreateBrowserInitiated(kUrl2, contents()); |
| navigation->Start(); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl2); |
| EXPECT_EQ(contents()->GetSpeculativePrimaryMainFrame()->GetProcess(), |
| site_instance->GetProcess()); |
| |
| // Remember the process id and cancel the navigation. Getting |
| // RenderProcessHost with the REUSE_PENDING_OR_COMMITTED_SITE policy should |
| // no longer return the process of the speculative RenderFrameHost. |
| int speculative_process_host_id = |
| contents()->GetSpeculativePrimaryMainFrame()->GetProcess()->GetID(); |
| navigation->Fail(net::ERR_ABORTED); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl2); |
| EXPECT_NE(speculative_process_host_id, site_instance->GetProcess()->GetID()); |
| } |
| |
| // Tests that RenderProcessHost reuse considers navigations correctly during |
| // redirects in a renderer-initiated navigation. Also ensures that with |
| // --site-per-process, there's no mismatch in origin locks for |
| // https://crbug.com/773809. |
| TEST_F(RenderProcessHostUnitTest, |
| ReuseNavigationProcessRedirectsRendererInitiated) { |
| const GURL kUrl("http://foo.com"); |
| const GURL kRedirectUrl1("http://foo.com/redirect"); |
| const GURL kRedirectUrl2("http://bar.com"); |
| |
| // At first, trying to get a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process. |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Start a navigation. Now getting RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return the current process. |
| auto simulator = |
| NavigationSimulator::CreateRendererInitiated(kUrl, main_test_rfh()); |
| simulator->Start(); |
| |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| // Note that with --site-per-process, the GetProcess() call on |
| // |site_instance| will also lock the current process to http://foo.com. |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Simulate a same-site redirect. Getting RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return the current process. |
| simulator->Redirect(kRedirectUrl1); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Simulate a cross-site redirect. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy for the initial foo.com site should |
| // no longer return the original process. Getting a RenderProcessHost with |
| // the REUSE_PENDING_OR_COMMITTED_SITE policy for the new bar.com site should |
| // return the the original process, unless we're in --site-per-process mode. |
| simulator->Redirect(kRedirectUrl2); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kRedirectUrl2); |
| if (AreAllSitesIsolatedForTesting()) { |
| // In --site-per-process, we should've recognized that we will need to swap |
| // to a new process; however, the new process won't be created until |
| // ready-to-commit time, when the final response comes from bar.com. Thus, |
| // we should have neither foo.com nor bar.com in the original process's |
| // list of pending sites, and |site_instance| should create a brand new |
| // process that does not match any existing one. In |
| // https://crbug.com/773809, the site_instance->GetProcess() call tried to |
| // reuse the original process (already locked to foo.com), leading to |
| // origin lock mismatch. |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| } else { |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| } |
| |
| // Once the navigation is ready to commit, getting RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return the current |
| // process for the final site, but not the initial one. |
| simulator->ReadyToCommit(); |
| RenderProcessHost* post_redirect_process = |
| simulator->GetFinalRenderFrameHost()->GetProcess(); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| EXPECT_NE(post_redirect_process, site_instance->GetProcess()); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kRedirectUrl2); |
| EXPECT_EQ(post_redirect_process, site_instance->GetProcess()); |
| } |
| |
| // Tests that RenderProcessHost reuse considers navigations correctly during |
| // redirects in a browser-initiated navigation. |
| TEST_F(RenderProcessHostUnitTest, |
| ReuseNavigationProcessRedirectsBrowserInitiated) { |
| const GURL kInitialUrl("http://google.com"); |
| const GURL kUrl("http://foo.com"); |
| const GURL kRedirectUrl1("http://foo.com/redirect"); |
| const GURL kRedirectUrl2("http://bar.com"); |
| |
| NavigateAndCommit(kInitialUrl); |
| |
| // At first, trying to get a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process. |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Start a navigation. Now getting RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return the speculative |
| // process. |
| contents()->GetController().LoadURL(kUrl, Referrer(), |
| ui::PAGE_TRANSITION_TYPED, std::string()); |
| main_test_rfh()->SimulateBeforeUnloadCompleted(true); |
| int speculative_process_host_id = |
| contents()->GetSpeculativePrimaryMainFrame()->GetProcess()->GetID(); |
| bool speculative_is_default_site_instance = |
| contents() |
| ->GetSpeculativePrimaryMainFrame() |
| ->GetSiteInstance() |
| ->IsDefaultSiteInstance(); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_EQ(speculative_process_host_id, site_instance->GetProcess()->GetID()); |
| |
| // Simulate a same-site redirect. Getting RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return the speculative |
| // process. |
| main_test_rfh()->SimulateRedirect(kRedirectUrl1); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_EQ(speculative_process_host_id, site_instance->GetProcess()->GetID()); |
| |
| // Simulate a cross-site redirect. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should no longer return the |
| // speculative process: neither for the new site nor for the initial site we |
| // were trying to navigate to. It shouldn't return the current process either. |
| main_test_rfh()->SimulateRedirect(kRedirectUrl2); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| EXPECT_NE(speculative_process_host_id, site_instance->GetProcess()->GetID()); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kRedirectUrl2); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| if (AreDefaultSiteInstancesEnabled()) { |
| EXPECT_TRUE(speculative_is_default_site_instance); |
| // The process ID should be the same as the default SiteInstance because |
| // kRedirectUrl1 and kRedirectUrl2 do not require a dedicated process. |
| EXPECT_EQ(speculative_process_host_id, |
| site_instance->GetProcess()->GetID()); |
| } else { |
| EXPECT_NE(speculative_process_host_id, |
| site_instance->GetProcess()->GetID()); |
| } |
| |
| // Once the navigation is ready to commit, Getting RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return the new speculative |
| // process for the final site, but not the initial one. The current process |
| // shouldn't be returned either. |
| main_test_rfh()->PrepareForCommit(); |
| speculative_process_host_id = |
| contents()->GetSpeculativePrimaryMainFrame()->GetProcess()->GetID(); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| EXPECT_NE(speculative_process_host_id, site_instance->GetProcess()->GetID()); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kRedirectUrl2); |
| EXPECT_EQ(speculative_process_host_id, site_instance->GetProcess()->GetID()); |
| } |
| |
| // Tests that RenderProcessHost reuse works correctly even if the site URL of a |
| // URL we're navigating to changes. |
| TEST_F(RenderProcessHostUnitTest, ReuseExpectedSiteURLChanges) { |
| if (AreAllSitesIsolatedForTesting()) |
| return; |
| |
| const GURL kUrl("http://foo.com"); |
| const GURL kModifiedSiteUrl("custom-scheme://custom"); |
| |
| // At first, trying to get a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return a new process. |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Start a navigation. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should now return the process of the |
| // main RFH. |
| auto navigation = |
| NavigationSimulator::CreateRendererInitiated(kUrl, main_test_rfh()); |
| navigation->Start(); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Install the custom ContentBrowserClient. Site URLs are now modified. |
| // Getting a RenderProcessHost with the REUSE_PENDING_OR_COMMITTED_SITE policy |
| // should no longer return the process of the main RFH, as the RFH is |
| // registered with the normal site URL. |
| EffectiveURLContentBrowserClient modified_client( |
| kUrl, kModifiedSiteUrl, /* requires_dedicated_process */ false); |
| ContentBrowserClient* regular_client = |
| SetBrowserClientForTesting(&modified_client); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Have the navigation commit. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should now return the process of the |
| // main RFH, as it was registered with the modified site URL at commit time. |
| navigation->Commit(); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Start a reload. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should return the process of the |
| // main RFH. |
| contents()->GetController().Reload(ReloadType::NORMAL, false); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Remove the custom ContentBrowserClient. Site URLs are back to normal. |
| // Getting a RenderProcessHost with the REUSE_PENDING_OR_COMMITTED_SITE policy |
| // should no longer return the process of the main RFH, as it is registered |
| // with the modified site URL. |
| SetBrowserClientForTesting(regular_client); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_NE(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| |
| // Finish the reload. Getting a RenderProcessHost with the |
| // REUSE_PENDING_OR_COMMITTED_SITE policy should now return the process of the |
| // main RFH, as it was registered with the regular site URL when it committed. |
| main_test_rfh()->PrepareForCommit(); |
| main_test_rfh()->SendNavigate(0, true, kUrl); |
| site_instance = SiteInstanceImpl::CreateReusableInstanceForTesting( |
| browser_context(), kUrl); |
| EXPECT_EQ(main_test_rfh()->GetProcess(), site_instance->GetProcess()); |
| } |
| |
| // Helper test class to modify the StoragePartition returned for a particular |
| // site URL. |
| class StoragePartitionContentBrowserClient : public ContentBrowserClient { |
| public: |
| StoragePartitionContentBrowserClient(const GURL& site, |
| const std::string& partition_domain, |
| const std::string& partition_name) |
| : site_(site), |
| partition_domain_(partition_domain), |
| partition_name_(partition_name) {} |
| ~StoragePartitionContentBrowserClient() override {} |
| |
| private: |
| StoragePartitionConfig GetStoragePartitionConfigForSite( |
| BrowserContext* browser_context, |
| const GURL& site) override { |
| if (site == site_) { |
| return StoragePartitionConfig::Create(browser_context, partition_domain_, |
| partition_name_, |
| false /* in_memory */); |
| } |
| |
| return StoragePartitionConfig::CreateDefault(browser_context); |
| } |
| |
| GURL site_; |
| std::string partition_domain_; |
| std::string partition_name_; |
| }; |
| |
| // Check that a SiteInstance cannot reuse a RenderProcessHost in a different |
| // StoragePartition. |
| TEST_F(RenderProcessHostUnitTest, |
| DoNotReuseProcessInDifferentStoragePartition) { |
| const GURL kUrl("https://foo.com"); |
| NavigateAndCommit(kUrl); |
| |
| // Change foo.com SiteInstances to use a different StoragePartition. |
| StoragePartitionContentBrowserClient modified_client(kUrl, "foo_domain", |
| "foo_name"); |
| ContentBrowserClient* regular_client = |
| SetBrowserClientForTesting(&modified_client); |
| |
| // Create a foo.com SiteInstance and check that its process does not |
| // reuse the foo process from the first navigation, since it's now in a |
| // different StoragePartition. |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl); |
| RenderProcessHost* process = site_instance->GetProcess(); |
| EXPECT_NE(main_test_rfh()->GetProcess(), process); |
| EXPECT_NE(main_test_rfh()->GetProcess()->GetStoragePartition(), |
| process->GetStoragePartition()); |
| |
| // Commit a navigation to foo.com in a new WebContents, so that there is a |
| // reusable foo.com process in the new StoragePartition. |
| std::unique_ptr<WebContents> contents(CreateTestWebContents()); |
| static_cast<TestWebContents*>(contents.get())->NavigateAndCommit(kUrl); |
| RenderProcessHost* foo_process_in_new_partition = |
| contents->GetPrimaryMainFrame()->GetProcess(); |
| |
| // Create another reusable foo.com SiteInstance in the new StoragePartition, |
| // and ensure that this SiteInstance reuse the process just created in that |
| // same StoragePartition. |
| scoped_refptr<SiteInstanceImpl> site_instance2 = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl); |
| RenderProcessHost* process2 = site_instance2->GetProcess(); |
| EXPECT_EQ(foo_process_in_new_partition, process2); |
| EXPECT_EQ(foo_process_in_new_partition->GetStoragePartition(), |
| process2->GetStoragePartition()); |
| EXPECT_NE(main_test_rfh()->GetProcess(), process2); |
| EXPECT_NE(main_test_rfh()->GetProcess()->GetStoragePartition(), |
| process2->GetStoragePartition()); |
| |
| SetBrowserClientForTesting(regular_client); |
| } |
| |
| // Check that a SiteInstance cannot reuse a ServiceWorker process in a |
| // different StoragePartition. |
| TEST_F(RenderProcessHostUnitTest, |
| DoNotReuseServiceWorkerProcessInDifferentStoragePartition) { |
| const GURL kUrl("https://foo.com"); |
| |
| // Create a RenderProcessHost for a service worker. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance = |
| CreateForServiceWorker(kUrl); |
| RenderProcessHost* sw_process = sw_site_instance->GetProcess(); |
| |
| // Change foo.com SiteInstances to use a different StoragePartition. |
| StoragePartitionContentBrowserClient modified_client(kUrl, "foo_domain", |
| "foo_name"); |
| ContentBrowserClient* regular_client = |
| SetBrowserClientForTesting(&modified_client); |
| |
| // Create a foo.com SiteInstance and check that its process does not reuse |
| // the ServiceWorker foo.com process, since it's now in a different |
| // StoragePartition. |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateReusableInstanceForTesting(browser_context(), |
| kUrl); |
| RenderProcessHost* process = site_instance->GetProcess(); |
| EXPECT_NE(sw_process, process); |
| |
| // Commit a navigation to foo.com in a new WebContents, so that there is a |
| // reusable foo.com process in the new StoragePartition. |
| std::unique_ptr<WebContents> contents(CreateTestWebContents()); |
| static_cast<TestWebContents*>(contents.get())->NavigateAndCommit(kUrl); |
| RenderProcessHost* foo_process_in_new_partition = |
| contents->GetPrimaryMainFrame()->GetProcess(); |
| |
| // Create a second foo.com service worker, this time in the new |
| // StoragePartition. Ensure that it reuses the process registered for foo.com |
| // in that same StoragePartition. |
| scoped_refptr<SiteInstanceImpl> sw_site_instance2 = |
| SiteInstanceImpl::CreateForServiceWorker( |
| browser_context(), |
| UrlInfo::CreateForTesting(kUrl, |
| site_instance->GetStoragePartitionConfig()), |
| /*can_reuse_process=*/true); |
| RenderProcessHost* sw_process2 = sw_site_instance2->GetProcess(); |
| EXPECT_EQ(sw_process2, foo_process_in_new_partition); |
| EXPECT_NE(sw_process2, sw_process); |
| EXPECT_EQ(SiteInstanceProcessAssignment::REUSED_EXISTING_PROCESS, |
| sw_site_instance2->GetLastProcessAssignmentOutcome()); |
| |
| SetBrowserClientForTesting(regular_client); |
| } |
| |
| // Check whether we notify the renderer that it has been locked to a site or |
| // not. This should depend on the URL from the SiteInstance. |
| TEST_F(RenderProcessHostUnitTest, RendererLockedToSite) { |
| struct TestData { |
| GURL test_url; |
| bool should_lock_renderer; |
| } tests[] = {{GURL("http://"), false}, |
| {GURL("http://foo.com"), true}, |
| {GURL("http://bar.foo.com"), true}, |
| {GURL(""), false}, |
| {GURL("google.com"), false}, |
| {GURL("http:"), false}, |
| {GURL("http://user:[email protected]:99/foo;bar?q=a#ref"), true}}; |
| for (const auto& test : tests) { |
| scoped_refptr<SiteInstanceImpl> site_instance = CreateForUrl(test.test_url); |
| auto* host = |
| static_cast<MockRenderProcessHost*>(site_instance->GetProcess()); |
| if (AreAllSitesIsolatedForTesting()) |
| EXPECT_EQ(test.should_lock_renderer, host->is_renderer_locked_to_site()); |
| else |
| EXPECT_EQ(false, host->is_renderer_locked_to_site()); |
| } |
| } |
| |
| // Checks that SiteInstanceProcessAssignment::UNKNOWN is used as the zero-value |
| // when no renderer process has been assigned to the SiteInstance yet. |
| TEST_F(RenderProcessHostUnitTest, ProcessAssignmentDefault) { |
| const GURL kUrl("https://foo.com"); |
| |
| scoped_refptr<SiteInstanceImpl> site_instance = CreateForUrl(kUrl); |
| EXPECT_EQ(SiteInstanceProcessAssignment::UNKNOWN, |
| site_instance->GetLastProcessAssignmentOutcome()); |
| EXPECT_FALSE(site_instance->HasProcess()); |
| } |
| |
| class SpareRenderProcessHostUnitTest : public RenderViewHostImplTestHarness { |
| public: |
| SpareRenderProcessHostUnitTest() {} |
| |
| SpareRenderProcessHostUnitTest(const SpareRenderProcessHostUnitTest&) = |
| delete; |
| SpareRenderProcessHostUnitTest& operator=( |
| const SpareRenderProcessHostUnitTest&) = delete; |
| |
| ~SpareRenderProcessHostUnitTest() override = default; |
| |
| protected: |
| void SetUp() override { |
| SetRenderProcessHostFactory(&rph_factory_); |
| RenderViewHostImplTestHarness::SetUp(); |
| SetContents(nullptr); // Start with no renderers. |
| RenderProcessHostImpl::DiscardSpareRenderProcessHostForTesting(); |
| while (!rph_factory_.GetProcesses()->empty()) { |
| rph_factory_.Remove(rph_factory_.GetProcesses()->back().get()); |
| } |
| } |
| |
| void TearDown() override { |
| // Important: Reset the max renderer count to leave this process in a |
| // pristine state. |
| DeleteContents(); |
| rph_factory_.GetProcesses()->clear(); |
| RenderProcessHost::SetMaxRendererProcessCount(0); |
| RenderViewHostImplTestHarness::TearDown(); |
| } |
| |
| void PruneDeadRenderProcessHosts() { |
| std::list<MockRenderProcessHost*> to_remove; |
| for (auto& host : *rph_factory_.GetProcesses()) { |
| if (!host->IsInitializedAndNotDead()) { |
| to_remove.push_back(host.get()); |
| } |
| } |
| for (auto* host : to_remove) { |
| rph_factory_.Remove(host); |
| } |
| } |
| |
| MockRenderProcessHostFactory rph_factory_; |
| }; |
| |
| using SpareProcessMaybeTakeAction = |
| RenderProcessHostImpl::SpareProcessMaybeTakeAction; |
| void ExpectSpareProcessMaybeTakeActionBucket( |
| const base::HistogramTester& histograms, |
| SpareProcessMaybeTakeAction expected_action) { |
| EXPECT_THAT( |
| histograms.GetAllSamples( |
| "BrowserRenderProcessHost.SpareProcessMaybeTakeAction"), |
| testing::ElementsAre(base::Bucket(static_cast<int>(expected_action), 1))); |
| } |
| |
| TEST_F(SpareRenderProcessHostUnitTest, TestRendererTaken) { |
| RenderProcessHost::WarmupSpareRenderProcessHost(browser_context()); |
| ASSERT_EQ(1U, rph_factory_.GetProcesses()->size()); |
| RenderProcessHost* spare_rph = |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting(); |
| EXPECT_EQ(spare_rph, rph_factory_.GetProcesses()->at(0).get()); |
| |
| const GURL kUrl1("http://foo.com"); |
| base::HistogramTester histograms; |
| SetContents(CreateTestWebContents()); |
| NavigateAndCommit(kUrl1); |
| EXPECT_EQ(spare_rph, main_test_rfh()->GetProcess()); |
| ExpectSpareProcessMaybeTakeActionBucket( |
| histograms, SpareProcessMaybeTakeAction::kSpareTaken); |
| |
| EXPECT_NE(spare_rph, |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()); |
| if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) { |
| EXPECT_NE(nullptr, |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()); |
| EXPECT_EQ(2U, rph_factory_.GetProcesses()->size()); |
| } else { |
| EXPECT_EQ(nullptr, |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()); |
| EXPECT_EQ(1U, rph_factory_.GetProcesses()->size()); |
| } |
| } |
| |
| TEST_F(SpareRenderProcessHostUnitTest, TestRendererNotTaken) { |
| std::unique_ptr<BrowserContext> alternate_context(new TestBrowserContext()); |
| RenderProcessHost::WarmupSpareRenderProcessHost(alternate_context.get()); |
| ASSERT_EQ(1U, rph_factory_.GetProcesses()->size()); |
| RenderProcessHost* old_spare = |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting(); |
| EXPECT_EQ(alternate_context.get(), old_spare->GetBrowserContext()); |
| EXPECT_EQ(old_spare, rph_factory_.GetProcesses()->at(0).get()); |
| |
| const GURL kUrl1("http://foo.com"); |
| base::HistogramTester histograms; |
| SetContents(CreateTestWebContents()); |
| NavigateAndCommit(kUrl1); |
| EXPECT_NE(old_spare, main_test_rfh()->GetProcess()); |
| ExpectSpareProcessMaybeTakeActionBucket( |
| histograms, SpareProcessMaybeTakeAction::kMismatchedBrowserContext); |
| |
| // Pumping the message loop here accounts for the delay between calling |
| // RPH::Cleanup on the spare and the time when the posted delete actually |
| // happens. Without pumping, the spare would still be present in |
| // rph_factory_.GetProcesses(). |
| base::RunLoop().RunUntilIdle(); |
| PruneDeadRenderProcessHosts(); |
| |
| RenderProcessHost* new_spare = |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting(); |
| EXPECT_NE(old_spare, new_spare); |
| if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) { |
| EXPECT_EQ(2U, rph_factory_.GetProcesses()->size()); |
| ASSERT_NE(nullptr, new_spare); |
| EXPECT_EQ(GetBrowserContext(), new_spare->GetBrowserContext()); |
| } else { |
| EXPECT_EQ(1U, rph_factory_.GetProcesses()->size()); |
| EXPECT_EQ(nullptr, new_spare); |
| } |
| } |
| |
| TEST_F(SpareRenderProcessHostUnitTest, SpareMissing) { |
| RenderProcessHostImpl::DiscardSpareRenderProcessHostForTesting(); |
| ASSERT_EQ(0U, rph_factory_.GetProcesses()->size()); |
| RenderProcessHost* spare_rph = |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting(); |
| EXPECT_FALSE(spare_rph); |
| |
| const GURL kUrl1("http://foo.com"); |
| base::HistogramTester histograms; |
| SetContents(CreateTestWebContents()); |
| NavigateAndCommit(kUrl1); |
| EXPECT_TRUE(main_test_rfh()->GetProcess()); |
| ExpectSpareProcessMaybeTakeActionBucket( |
| histograms, SpareProcessMaybeTakeAction::kNoSparePresent); |
| |
| spare_rph = RenderProcessHostImpl::GetSpareRenderProcessHostForTesting(); |
| if (RenderProcessHostImpl::IsSpareProcessKeptAtAllTimes()) { |
| EXPECT_TRUE(spare_rph); |
| EXPECT_EQ(2U, rph_factory_.GetProcesses()->size()); |
| } else { |
| EXPECT_FALSE(spare_rph); |
| EXPECT_EQ(1U, rph_factory_.GetProcesses()->size()); |
| } |
| } |
| |
| TEST_F(SpareRenderProcessHostUnitTest, |
| SpareShouldNotLaunchInParallelWithOtherProcess) { |
| std::unique_ptr<BrowserContext> alternate_context(new TestBrowserContext()); |
| RenderProcessHost::WarmupSpareRenderProcessHost(alternate_context.get()); |
| ASSERT_EQ(1U, rph_factory_.GetProcesses()->size()); |
| RenderProcessHost* old_spare = |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting(); |
| EXPECT_EQ(alternate_context.get(), old_spare->GetBrowserContext()); |
| EXPECT_EQ(old_spare, rph_factory_.GetProcesses()->at(0).get()); |
| |
| // When we try to get a process for foo.com, we won't be able to use the spare |
| // (because it is associated with the alternate, mismatched BrowserContext) |
| // and therefore we will have to spawn a new process. This test verifies that |
| // we don't at the same time try to warm-up a new spare (leading to |
| // unnecessary resource contention when 2 processes try to launch at the same |
| // time). |
| scoped_refptr<SiteInstanceImpl> site_instance = |
| SiteInstanceImpl::CreateForTesting(browser_context(), |
| GURL("http://foo.com")); |
| RenderProcessHost* site_instance_process = site_instance->GetProcess(); |
| |
| // The SiteInstance shouldn't get the old spare, because of BrowserContext |
| // mismatch. The SiteInstance will get a new process instead. |
| EXPECT_NE(old_spare, site_instance_process); |
| EXPECT_FALSE(site_instance_process->IsReady()); |
| |
| // There should be no new spare at this point to avoid launching 2 processes |
| // at the same time. Note that the spare might still be created later during |
| // a navigation (e.g. after cross-site redirects or when committing). |
| RenderProcessHost* new_spare = |
| RenderProcessHostImpl::GetSpareRenderProcessHostForTesting(); |
| if (new_spare != old_spare) |
| EXPECT_FALSE(new_spare); |
| } |
| |
| // This unit test looks at the simplified equivalent of what |
| // CtrlClickShouldEndUpInSameProcessTest.BlankTarget test would have |
| // encountered. The test verifies that the spare RPH is not launched if 1) we |
| // need to create another renderer process anyway (e.g. because the spare is |
| // missing when MaybeTakeSpareRenderProcessHost is called) and 2) creating the |
| // other renderer process will put as at the process limit. Launching the spare |
| // in this scenario would put us over the process limit and is therefore |
| // undesirable. |
| TEST_F(SpareRenderProcessHostUnitTest, JustBelowProcessLimit) { |
| // Override a global. TearDown() will call SetMaxRendererProcessCount() again |
| // to clean up. |
| RenderProcessHost::SetMaxRendererProcessCount(1); |
| |
| // No spare or any other renderer process at the start of the test. |
| EXPECT_FALSE(RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()); |
| EXPECT_EQ(0U, rph_factory_.GetProcesses()->size()); |
| |
| // Navigation can't take a spare (none present) and needs to launch a new |
| // renderer process. |
| const GURL kUrl1("http://foo.com"); |
| SetContents(CreateTestWebContents()); |
| NavigateAndCommit(kUrl1); |
| |
| // We should still be below the process limit. |
| EXPECT_EQ(1U, rph_factory_.GetProcesses()->size()); |
| |
| // There should be no spare - having one would put us over the process limit. |
| EXPECT_FALSE(RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()); |
| } |
| |
| // This unit test verifies that a mismatched spare RenderProcessHost is dropped |
| // before considering process reuse due to the process limit. |
| TEST_F(SpareRenderProcessHostUnitTest, AtProcessLimit) { |
| // Override a global. TearDown() will call SetMaxRendererProcessCount() again |
| // to clean up. |
| RenderProcessHost::SetMaxRendererProcessCount(2); |
| |
| // Create and navigate the 1st WebContents. |
| const GURL kUrl1("http://foo.com"); |
| std::unique_ptr<WebContents> contents1(CreateTestWebContents()); |
| static_cast<TestWebContents*>(contents1.get())->NavigateAndCommit(kUrl1); |
| EXPECT_NE(RenderProcessHostImpl::GetSpareRenderProcessHostForTesting(), |
| contents1->GetPrimaryMainFrame()->GetProcess()); |
| |
| // Warm up a mismatched spare. |
| std::unique_ptr<BrowserContext> alternate_context(new TestBrowserContext()); |
| RenderProcessHost::WarmupSpareRenderProcessHost(alternate_context.get()); |
| base::RunLoop().RunUntilIdle(); |
| PruneDeadRenderProcessHosts(); |
| EXPECT_EQ(2U, rph_factory_.GetProcesses()->size()); |
| |
| // Create and navigate the 2nd WebContents. |
| const GURL kUrl2("http://bar.com"); |
| std::unique_ptr<WebContents> contents2(CreateTestWebContents()); |
| static_cast<TestWebContents*>(contents2.get())->NavigateAndCommit(kUrl2); |
| base::RunLoop().RunUntilIdle(); |
| |
| PruneDeadRenderProcessHosts(); |
| // Creating a 2nd WebContents shouldn't share a renderer process with the 1st |
| // one - instead the spare should be dropped to stay under the process limit. |
| EXPECT_EQ(2U, rph_factory_.GetProcesses()->size()); |
| EXPECT_FALSE(RenderProcessHostImpl::GetSpareRenderProcessHostForTesting()); |
| EXPECT_NE(contents1->GetPrimaryMainFrame()->GetProcess(), |
| contents2->GetPrimaryMainFrame()->GetProcess()); |
| } |
| |
| } // namespace content |