| // Copyright 2020 The Chromium Authors. All rights reserved. | 
 | // Use of this source code is governed by a BSD-style license that can be | 
 | // found in the LICENSE file. | 
 |  | 
 | #include "content/browser/preloading/prerender/prerender_host_registry.h" | 
 |  | 
 | #include "base/test/bind.h" | 
 | #include "base/test/scoped_feature_list.h" | 
 | #include "content/browser/preloading/prerender/prerender_host.h" | 
 | #include "content/browser/preloading/speculation_rules/speculation_host_impl.h" | 
 | #include "content/browser/renderer_host/render_frame_host_impl.h" | 
 | #include "content/browser/site_instance_impl.h" | 
 | #include "content/public/browser/render_frame_host.h" | 
 | #include "content/public/browser/storage_partition.h" | 
 | #include "content/public/test/prerender_test_util.h" | 
 | #include "content/public/test/test_browser_context.h" | 
 | #include "content/test/mock_commit_deferring_condition.h" | 
 | #include "content/test/navigation_simulator_impl.h" | 
 | #include "content/test/test_render_view_host.h" | 
 | #include "content/test/test_web_contents.h" | 
 | #include "net/base/load_flags.h" | 
 | #include "third_party/blink/public/common/features.h" | 
 | #include "third_party/blink/public/mojom/loader/mixed_content.mojom.h" | 
 | #include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom.h" | 
 |  | 
 | namespace content { | 
 | namespace { | 
 |  | 
 | blink::mojom::SpeculationCandidatePtr CreatePrerenderCandidate( | 
 |     const GURL& url) { | 
 |   auto candidate = blink::mojom::SpeculationCandidate::New(); | 
 |   candidate->action = blink::mojom::SpeculationAction::kPrerender; | 
 |   candidate->url = url; | 
 |   candidate->referrer = blink::mojom::Referrer::New(); | 
 |   return candidate; | 
 | } | 
 |  | 
 | void SendCandidates(const std::vector<GURL>& urls, | 
 |                     mojo::Remote<blink::mojom::SpeculationHost>& remote) { | 
 |   std::vector<blink::mojom::SpeculationCandidatePtr> candidates; | 
 |   candidates.resize(urls.size()); | 
 |   base::ranges::transform(urls, candidates.begin(), &CreatePrerenderCandidate); | 
 |   remote->UpdateSpeculationCandidates(std::move(candidates)); | 
 |   remote.FlushForTesting(); | 
 | } | 
 |  | 
 | void SendCandidate(const GURL& url, | 
 |                    mojo::Remote<blink::mojom::SpeculationHost>& remote) { | 
 |   SendCandidates({url}, remote); | 
 | } | 
 |  | 
 | PrerenderAttributes GeneratePrerenderAttributes( | 
 |     const GURL& url, | 
 |     PrerenderTriggerType trigger_type, | 
 |     const std::string& embedder_histogram_suffix, | 
 |     RenderFrameHostImpl* rfh) { | 
 |   if (trigger_type == PrerenderTriggerType::kSpeculationRule) { | 
 |     return PrerenderAttributes( | 
 |         url, trigger_type, embedder_histogram_suffix, Referrer(), | 
 |         rfh->GetLastCommittedOrigin(), rfh->GetLastCommittedURL(), | 
 |         rfh->GetProcess()->GetID(), rfh->GetFrameToken(), | 
 |         rfh->GetFrameTreeNodeId(), rfh->GetPageUkmSourceId(), | 
 |         ui::PAGE_TRANSITION_LINK, | 
 |         /*url_match_predicate=*/absl::nullopt); | 
 |   } else { | 
 |     // TODO(https://crbug.com/1325211): remove initiator_origin and | 
 |     // initiator_frame_token after fixing prerendering activation for | 
 |     // embedder-triggered prerendering in unittests. | 
 |     return PrerenderAttributes( | 
 |         url, trigger_type, embedder_histogram_suffix, Referrer(), | 
 |         /*initiator_origin=*/rfh->GetLastCommittedOrigin(), | 
 |         rfh->GetLastCommittedURL(), | 
 |         /*initiator_process_id=*/ChildProcessHost::kInvalidUniqueID, | 
 |         /*initiator_frame_token=*/rfh->GetFrameToken(), | 
 |         /*initiator_frame_tree_node_id=*/RenderFrameHost::kNoFrameTreeNodeId, | 
 |         /*initiator_ukm_id=*/ukm::kInvalidSourceId, ui::PAGE_TRANSITION_LINK, | 
 |         /*url_match_predicate=*/absl::nullopt); | 
 |   } | 
 | } | 
 |  | 
 | // This definition is needed because this constant is odr-used in gtest macros. | 
 | // https://en.cppreference.com/w/cpp/language/static#Constant_static_members | 
 | const int kNoFrameTreeNodeId = RenderFrameHost::kNoFrameTreeNodeId; | 
 |  | 
 | std::unique_ptr<NavigationSimulatorImpl> CreateActivation( | 
 |     const GURL& prerendering_url, | 
 |     WebContentsImpl& web_contents) { | 
 |   std::unique_ptr<NavigationSimulatorImpl> navigation = | 
 |       NavigationSimulatorImpl::CreateRendererInitiated( | 
 |           prerendering_url, web_contents.GetPrimaryMainFrame()); | 
 |   navigation->SetReferrer(blink::mojom::Referrer::New( | 
 |       web_contents.GetPrimaryMainFrame()->GetLastCommittedURL(), | 
 |       network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin)); | 
 |   return navigation; | 
 | } | 
 |  | 
 | // Finish a prerendering navigation that was already started with | 
 | // CreateAndStartHost(). | 
 | void CommitPrerenderNavigation(PrerenderHost& host) { | 
 |   // Normally we could use EmbeddedTestServer to provide a response, but these | 
 |   // tests use RenderViewHostImplTestHarness so the load goes through a | 
 |   // TestNavigationURLLoader which we don't have access to in order to | 
 |   // complete. Use NavigationSimulator to finish the navigation. | 
 |   FrameTreeNode* ftn = FrameTreeNode::From(host.GetPrerenderedMainFrameHost()); | 
 |   std::unique_ptr<NavigationSimulator> sim = | 
 |       NavigationSimulatorImpl::CreateFromPendingInFrame(ftn); | 
 |   sim->Commit(); | 
 |   EXPECT_TRUE(host.is_ready_for_activation()); | 
 | } | 
 |  | 
 | class PrerenderWebContentsDelegate : public WebContentsDelegate { | 
 |  public: | 
 |   PrerenderWebContentsDelegate() = default; | 
 |  | 
 |   bool IsPrerender2Supported(WebContents& web_contents) override { | 
 |     return true; | 
 |   } | 
 | }; | 
 |  | 
 | class PrerenderHostRegistryTest : public RenderViewHostImplTestHarness { | 
 |  public: | 
 |   PrerenderHostRegistryTest() { | 
 |     scoped_feature_list_.InitWithFeatures( | 
 |         {blink::features::kPrerender2}, | 
 |         // Disable the memory requirement of Prerender2 so the test can run on | 
 |         // any bot. | 
 |         {blink::features::kPrerender2MemoryControls}); | 
 |   } | 
 |   ~PrerenderHostRegistryTest() override = default; | 
 |  | 
 |   std::unique_ptr<TestWebContents> CreateWebContents(const GURL& url) { | 
 |     std::unique_ptr<TestWebContents> web_contents(TestWebContents::Create( | 
 |         GetBrowserContext(), SiteInstanceImpl::Create(GetBrowserContext()))); | 
 |     web_contents->SetDelegate(&web_contents_delegate_); | 
 |     web_contents->NavigateAndCommit(url); | 
 |     return web_contents; | 
 |   } | 
 |  | 
 |   RenderFrameHostImpl* NavigatePrimaryPage(TestWebContents* web_contents, | 
 |                                            const GURL& dest_url) { | 
 |     std::unique_ptr<NavigationSimulatorImpl> navigation = | 
 |         NavigationSimulatorImpl::CreateRendererInitiated( | 
 |             dest_url, web_contents->GetPrimaryMainFrame()); | 
 |     navigation->SetTransition(ui::PAGE_TRANSITION_LINK); | 
 |     navigation->Start(); | 
 |     navigation->Commit(); | 
 |     RenderFrameHostImpl* render_frame_host = | 
 |         web_contents->GetPrimaryMainFrame(); | 
 |     EXPECT_EQ(render_frame_host->GetLastCommittedURL(), dest_url); | 
 |     return render_frame_host; | 
 |   } | 
 |  | 
 |   // Helper method to test the navigation param matching logic which allows a | 
 |   // prerender host to be used in a potential activation navigation only if its | 
 |   // params match the potential activation navigation params. Use setup_callback | 
 |   // to set the parameters. Returns true if the host was selected as a | 
 |   // potential candidate for activation, and false otherwise. | 
 |   [[nodiscard]] bool CheckIsActivatedForParams( | 
 |       base::OnceCallback<void(NavigationSimulatorImpl*)> setup_callback) { | 
 |     const GURL kOriginalUrl("https://example.com/"); | 
 |  | 
 |     std::unique_ptr<TestWebContents> web_contents = | 
 |         CreateWebContents(kOriginalUrl); | 
 |     RenderFrameHostImpl* render_frame_host = | 
 |         web_contents->GetPrimaryMainFrame(); | 
 |  | 
 |     const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |     PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |     registry->CreateAndStartHost( | 
 |         GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                     PrerenderTriggerType::kSpeculationRule, "", | 
 |                                     render_frame_host), | 
 |         *web_contents); | 
 |     PrerenderHost* prerender_host = | 
 |         registry->FindHostByUrlForTesting(kPrerenderingUrl); | 
 |     CommitPrerenderNavigation(*prerender_host); | 
 |  | 
 |     std::unique_ptr<NavigationSimulatorImpl> navigation = | 
 |         NavigationSimulatorImpl::CreateRendererInitiated(kPrerenderingUrl, | 
 |                                                          render_frame_host); | 
 |     // Set a default referrer policy that matches the initial prerender | 
 |     // navigation. | 
 |     // TODO(falken): Fix NavigationSimulatorImpl to do this itself. | 
 |     navigation->SetReferrer(blink::mojom::Referrer::New( | 
 |         web_contents->GetPrimaryMainFrame()->GetLastCommittedURL(), | 
 |         network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin)); | 
 |  | 
 |     // Change a parameter to differentiate the activation request from the | 
 |     // prerendering request. | 
 |     std::move(setup_callback).Run(navigation.get()); | 
 |     navigation->Start(); | 
 |     NavigationRequest* navigation_request = navigation->GetNavigationHandle(); | 
 |     // Use is_potentially_prerendered_page_activation_for_testing() instead of | 
 |     // IsPrerenderedPageActivation() because the NavigationSimulator does not | 
 |     // proceed past CommitDeferringConditions on potential activations, | 
 |     // so IsPrerenderedPageActivation() will fail with a CHECK because | 
 |     // prerender_frame_tree_node_id_ is not populated. | 
 |     // TODO(https://crbug.com/1239220): Fix NavigationSimulator to wait for | 
 |     // commit deferring conditions as it does throttles. | 
 |     return navigation_request | 
 |         ->is_potentially_prerendered_page_activation_for_testing(); | 
 |   } | 
 |  | 
 |   // Helper method to perform a prerender activation that includes specialized | 
 |   // handling or setup on the initial prerender navigation via the | 
 |   // setup_callback parameter. | 
 |   void SetupPrerenderAndCommit( | 
 |       base::OnceCallback<void(NavigationSimulatorImpl*)> setup_callback) { | 
 |     const GURL kOriginalUrl("https://example.com/"); | 
 |  | 
 |     TestWebContents* wc = static_cast<TestWebContents*>(web_contents()); | 
 |     web_contents()->SetDelegate(&web_contents_delegate_); | 
 |     wc->NavigateAndCommit(kOriginalUrl); | 
 |     RenderFrameHostImpl* render_frame_host = wc->GetPrimaryMainFrame(); | 
 |     ASSERT_TRUE(render_frame_host); | 
 |  | 
 |     const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |     PrerenderHostRegistry* registry = wc->GetPrerenderHostRegistry(); | 
 |     const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |         GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                     PrerenderTriggerType::kSpeculationRule, "", | 
 |                                     render_frame_host), | 
 |         *wc); | 
 |     ASSERT_NE(prerender_frame_tree_node_id, kNoFrameTreeNodeId); | 
 |     PrerenderHost* prerender_host = | 
 |         registry->FindNonReservedHostById(prerender_frame_tree_node_id); | 
 |  | 
 |     // Complete the initial prerender navigation. | 
 |     FrameTreeNode* ftn = | 
 |         FrameTreeNode::From(prerender_host->GetPrerenderedMainFrameHost()); | 
 |     std::unique_ptr<NavigationSimulatorImpl> sim = | 
 |         NavigationSimulatorImpl::CreateFromPendingInFrame(ftn); | 
 |     std::move(setup_callback).Run(sim.get()); | 
 |     sim->Commit(); | 
 |     EXPECT_TRUE(prerender_host->is_ready_for_activation()); | 
 |  | 
 |     // Activate the prerendered page. | 
 |     wc->ActivatePrerenderedPage(kPrerenderingUrl); | 
 |   } | 
 |  | 
 |   void ExpectUniqueSampleOfFinalStatus(PrerenderHost::FinalStatus status) { | 
 |     histogram_tester_.ExpectUniqueSample( | 
 |         "Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule", | 
 |         status, 1); | 
 |   } | 
 |  | 
 |   void ExpectBucketCountOfFinalStatus(PrerenderHost::FinalStatus status) { | 
 |     histogram_tester_.ExpectBucketCount( | 
 |         "Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule", | 
 |         status, 1); | 
 |   } | 
 |  | 
 |   void ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch result) { | 
 |     histogram_tester_.ExpectUniqueSample( | 
 |         "Prerender.Experimental.ActivationNavigationParamsMatch." | 
 |         "SpeculationRule", | 
 |         result, 1); | 
 |   } | 
 |  | 
 |   void ExpectBucketCountOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch result, | 
 |       base::HistogramBase::Count count) { | 
 |     histogram_tester_.ExpectBucketCount( | 
 |         "Prerender.Experimental.ActivationNavigationParamsMatch." | 
 |         "SpeculationRule", | 
 |         result, count); | 
 |   } | 
 |  | 
 |  private: | 
 |   base::test::ScopedFeatureList scoped_feature_list_; | 
 |   PrerenderWebContentsDelegate web_contents_delegate_; | 
 |   base::HistogramTester histogram_tester_; | 
 | }; | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, CreateAndStartHost_SpeculationRule) { | 
 |   base::HistogramTester histogram_tester; | 
 |  | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |   const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   ASSERT_NE(prerender_frame_tree_node_id, kNoFrameTreeNodeId); | 
 |   PrerenderHost* prerender_host = | 
 |       registry->FindHostByUrlForTesting(kPrerenderingUrl); | 
 |   CommitPrerenderNavigation(*prerender_host); | 
 |  | 
 |   web_contents->ActivatePrerenderedPage(kPrerenderingUrl); | 
 |  | 
 |   // "Navigation.TimeToActivatePrerender.SpeculationRule" histogram should be | 
 |   // recorded on every prerender activation. | 
 |   histogram_tester.ExpectTotalCount( | 
 |       "Navigation.TimeToActivatePrerender.SpeculationRule", 1u); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, CreateAndStartHost_Embedder_DirectURLInput) { | 
 |   base::HistogramTester histogram_tester; | 
 |  | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |   const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kEmbedder, | 
 |                                   "DirectURLInput", render_frame_host), | 
 |       *web_contents); | 
 |   ASSERT_NE(prerender_frame_tree_node_id, kNoFrameTreeNodeId); | 
 |   PrerenderHost* prerender_host = | 
 |       registry->FindHostByUrlForTesting(kPrerenderingUrl); | 
 |   CommitPrerenderNavigation(*prerender_host); | 
 |  | 
 |   web_contents->ActivatePrerenderedPage(kPrerenderingUrl); | 
 |  | 
 |   // "Navigation.TimeToActivatePrerender.Embedder_DirectURLInput" histogram | 
 |   // should be recorded on every prerender activation. | 
 |   histogram_tester.ExpectTotalCount( | 
 |       "Navigation.TimeToActivatePrerender.Embedder_DirectURLInput", 1u); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, CreateAndStartHostForSameURL) { | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |   const int frame_tree_node_id1 = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   EXPECT_NE(frame_tree_node_id1, RenderFrameHost::kNoFrameTreeNodeId); | 
 |   PrerenderHost* prerender_host1 = | 
 |       registry->FindHostByUrlForTesting(kPrerenderingUrl); | 
 |  | 
 |   // Start the prerender host for the same URL. This second host should be | 
 |   // ignored, and the first host should still be findable. | 
 |   const int frame_tree_node_id2 = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   EXPECT_EQ(frame_tree_node_id2, RenderFrameHost::kNoFrameTreeNodeId); | 
 |   EXPECT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl), | 
 |             prerender_host1); | 
 |   CommitPrerenderNavigation(*prerender_host1); | 
 |  | 
 |   web_contents->ActivatePrerenderedPage(kPrerenderingUrl); | 
 | } | 
 |  | 
 | // Tests that PrerenderHostRegistry limits the number of started prerenders | 
 | // to 1. | 
 | TEST_F(PrerenderHostRegistryTest, NumberLimit_Activation) { | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   // After the first prerender page was activated, PrerenderHostRegistry can | 
 |   // start prerendering a new one. | 
 |   const GURL kPrerenderingUrl1("https://example.com/next1"); | 
 |   const GURL kPrerenderingUrl2("https://example.com/next2"); | 
 |  | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |   int frame_tree_node_id1 = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl1, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   int frame_tree_node_id2 = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl2, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   ExpectUniqueSampleOfFinalStatus( | 
 |       PrerenderHost::FinalStatus::kMaxNumOfRunningPrerendersExceeded); | 
 |  | 
 |   // PrerenderHostRegistry should only start prerendering for kPrerenderingUrl1. | 
 |   EXPECT_NE(frame_tree_node_id1, kNoFrameTreeNodeId); | 
 |   EXPECT_EQ(frame_tree_node_id2, kNoFrameTreeNodeId); | 
 |  | 
 |   // Activate the first prerender. | 
 |   PrerenderHost* prerender_host1 = | 
 |       registry->FindHostByUrlForTesting(kPrerenderingUrl1); | 
 |   CommitPrerenderNavigation(*prerender_host1); | 
 |   web_contents->ActivatePrerenderedPage(kPrerenderingUrl1); | 
 |  | 
 |   // After the first prerender page was activated, PrerenderHostRegistry can | 
 |   // start prerendering a new one. | 
 |   frame_tree_node_id2 = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl2, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   EXPECT_NE(frame_tree_node_id2, kNoFrameTreeNodeId); | 
 |   ExpectBucketCountOfFinalStatus( | 
 |       PrerenderHost::FinalStatus::kMaxNumOfRunningPrerendersExceeded); | 
 | } | 
 |  | 
 | // Tests that PrerenderHostRegistry limits the number of started prerenders | 
 | // to 1, and new candidates can be processed after the initiator page navigates | 
 | // to a new same-origin page. | 
 | TEST_F(PrerenderHostRegistryTest, NumberLimit_SameOriginNavigateAway) { | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   mojo::Remote<blink::mojom::SpeculationHost> remote1; | 
 |   SpeculationHostImpl::Bind(render_frame_host, | 
 |                             remote1.BindNewPipeAndPassReceiver()); | 
 |   ASSERT_TRUE(remote1.is_connected()); | 
 |   const GURL kPrerenderingUrl1("https://example.com/next1"); | 
 |   const GURL kPrerenderingUrl2("https://example.com/next2"); | 
 |   SendCandidates({kPrerenderingUrl1, kPrerenderingUrl2}, remote1); | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |  | 
 |   // PrerenderHostRegistry should only start prerendering for kPrerenderingUrl1. | 
 |   ASSERT_NE(registry->FindHostByUrlForTesting(kPrerenderingUrl1), nullptr); | 
 |   ASSERT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl2), nullptr); | 
 |   ExpectUniqueSampleOfFinalStatus( | 
 |       PrerenderHost::FinalStatus::kMaxNumOfRunningPrerendersExceeded); | 
 |  | 
 |   // The initiator document navigates away. | 
 |   render_frame_host = NavigatePrimaryPage( | 
 |       web_contents.get(), GURL("https://example.com/elsewhere")); | 
 |   EXPECT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl1), nullptr); | 
 |  | 
 |   // After the initiator page navigates away, the started prerendering should be | 
 |   // cancelled, and PrerenderHostRegistry can start prerendering a new one. | 
 |   mojo::Remote<blink::mojom::SpeculationHost> remote2; | 
 |   SpeculationHostImpl::Bind(render_frame_host, | 
 |                             remote2.BindNewPipeAndPassReceiver()); | 
 |   SendCandidate(kPrerenderingUrl2, remote2); | 
 |  | 
 |   EXPECT_NE(registry->FindHostByUrlForTesting(kPrerenderingUrl2), nullptr); | 
 |   ExpectBucketCountOfFinalStatus( | 
 |       PrerenderHost::FinalStatus::kMaxNumOfRunningPrerendersExceeded); | 
 | } | 
 |  | 
 | // Tests that PrerenderHostRegistry limits the number of started prerenders | 
 | // to 1, and new candidates can be processed after the initiator page navigates | 
 | // to a new cross-origin page. | 
 | TEST_F(PrerenderHostRegistryTest, NumberLimit_CrossOriginNavigateAway) { | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |  | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   mojo::Remote<blink::mojom::SpeculationHost> remote1; | 
 |   SpeculationHostImpl::Bind(render_frame_host, | 
 |                             remote1.BindNewPipeAndPassReceiver()); | 
 |   ASSERT_TRUE(remote1.is_connected()); | 
 |   const GURL kPrerenderingUrl1("https://example.com/next1"); | 
 |   const GURL kPrerenderingUrl2("https://example.com/next2"); | 
 |   SendCandidates({kPrerenderingUrl1, kPrerenderingUrl2}, remote1); | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |  | 
 |   // PrerenderHostRegistry should only start prerendering for kPrerenderingUrl1. | 
 |   ASSERT_NE(registry->FindHostByUrlForTesting(kPrerenderingUrl1), nullptr); | 
 |   ASSERT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl2), nullptr); | 
 |   ExpectUniqueSampleOfFinalStatus( | 
 |       PrerenderHost::FinalStatus::kMaxNumOfRunningPrerendersExceeded); | 
 |  | 
 |   // The initiator document navigates away to a cross-origin page. | 
 |   render_frame_host = | 
 |       NavigatePrimaryPage(web_contents.get(), GURL("https://example.org/")); | 
 |   EXPECT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl1), nullptr); | 
 |  | 
 |   // After the initiator page navigates away, the started prerendering should be | 
 |   // cancelled, and PrerenderHostRegistry can start prerendering a new one. | 
 |   mojo::Remote<blink::mojom::SpeculationHost> remote2; | 
 |   SpeculationHostImpl::Bind(render_frame_host, | 
 |                             remote2.BindNewPipeAndPassReceiver()); | 
 |   const GURL kPrerenderingUrl3("https://example.org/next1"); | 
 |   SendCandidate(kPrerenderingUrl3, remote2); | 
 |   EXPECT_NE(registry->FindHostByUrlForTesting(kPrerenderingUrl3), nullptr); | 
 |   ExpectBucketCountOfFinalStatus( | 
 |       PrerenderHost::FinalStatus::kMaxNumOfRunningPrerendersExceeded); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        ReserveHostToActivateBeforeReadyForActivation) { | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |   const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   ASSERT_NE(prerender_frame_tree_node_id, kNoFrameTreeNodeId); | 
 |   PrerenderHost* prerender_host = | 
 |       registry->FindHostByUrlForTesting(kPrerenderingUrl); | 
 |   FrameTreeNode* ftn = | 
 |       FrameTreeNode::From(prerender_host->GetPrerenderedMainFrameHost()); | 
 |   std::unique_ptr<NavigationSimulatorImpl> sim = | 
 |       NavigationSimulatorImpl::CreateFromPendingInFrame(ftn); | 
 |   // Ensure that navigation in prerendering frame tree does not commit and | 
 |   // PrerenderHost doesn't become ready for activation. | 
 |   sim->SetAutoAdvance(false); | 
 |  | 
 |   EXPECT_FALSE(prerender_host->is_ready_for_activation()); | 
 |  | 
 |   test::PrerenderHostObserver prerender_host_observer(*web_contents, | 
 |                                                       kPrerenderingUrl); | 
 |  | 
 |   // Start activation. | 
 |   std::unique_ptr<NavigationSimulatorImpl> navigation = | 
 |       CreateActivation(kPrerenderingUrl, *web_contents); | 
 |   navigation->Start(); | 
 |  | 
 |   // Wait until PrerenderCommitDeferringCondition runs. | 
 |   // TODO(nhiroki): Avoid using base::RunUntilIdle() and instead use some | 
 |   // explicit signal of the running condition. | 
 |   base::RunLoop().RunUntilIdle(); | 
 |  | 
 |   // The activation should be deferred by PrerenderCommitDeferringCondition | 
 |   // until the main frame navigation in the prerendering frame tree finishes. | 
 |   NavigationRequest* navigation_request = navigation->GetNavigationHandle(); | 
 |   EXPECT_TRUE( | 
 |       navigation_request->IsCommitDeferringConditionDeferredForTesting()); | 
 |   EXPECT_FALSE(prerender_host_observer.was_activated()); | 
 |   EXPECT_EQ(web_contents->GetPrimaryMainFrame()->GetLastCommittedURL(), | 
 |             kOriginalUrl); | 
 |  | 
 |   // Finish the main frame navigation. | 
 |   sim->Commit(); | 
 |  | 
 |   // Finish the activation. | 
 |   prerender_host_observer.WaitForDestroyed(); | 
 |   EXPECT_TRUE(prerender_host_observer.was_activated()); | 
 |   EXPECT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl), nullptr); | 
 |   EXPECT_EQ(web_contents->GetPrimaryMainFrame()->GetLastCommittedURL(), | 
 |             kPrerenderingUrl); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, CancelHost) { | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(GURL("https://example.com/")); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |  | 
 |   const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   EXPECT_NE(registry->FindHostByUrlForTesting(kPrerenderingUrl), nullptr); | 
 |  | 
 |   registry->CancelHost(prerender_frame_tree_node_id, | 
 |                        PrerenderHost::FinalStatus::kDestroyed); | 
 |   EXPECT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl), nullptr); | 
 | } | 
 |  | 
 | // Test cancelling a prerender while a CommitDeferringCondition is running. | 
 | // This activation should fall back to a regular navigation. | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CancelHostWhileCommitDeferringConditionIsRunning) { | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   // Start prerendering. | 
 |   const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |  | 
 |   const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   ASSERT_NE(prerender_frame_tree_node_id, kNoFrameTreeNodeId); | 
 |   PrerenderHost* prerender_host = | 
 |       registry->FindHostByUrlForTesting(kPrerenderingUrl); | 
 |   CommitPrerenderNavigation(*prerender_host); | 
 |  | 
 |   test::PrerenderHostObserver prerender_host_observer(*web_contents, | 
 |                                                       kPrerenderingUrl); | 
 |  | 
 |   // Now navigate the primary page to the prerendered URL so that we activate | 
 |   // the prerender. Use a CommitDeferringCondition to pause activation | 
 |   // before it completes. | 
 |   std::unique_ptr<NavigationSimulatorImpl> navigation; | 
 |  | 
 |   { | 
 |     MockCommitDeferringConditionInstaller installer( | 
 |         kPrerenderingUrl, | 
 |         /*is_ready_to_commit=*/false); | 
 |     // Start trying to activate the prerendered page. | 
 |     navigation = CreateActivation(kPrerenderingUrl, *web_contents); | 
 |     navigation->Start(); | 
 |  | 
 |     // Wait for the condition to pause the activation. | 
 |     installer.WaitUntilInstalled(); | 
 |     installer.condition().WaitUntilInvoked(); | 
 |  | 
 |     // The request should be deferred by the condition. | 
 |     NavigationRequest* navigation_request = | 
 |         static_cast<NavigationRequest*>(navigation->GetNavigationHandle()); | 
 |     EXPECT_TRUE( | 
 |         navigation_request->IsCommitDeferringConditionDeferredForTesting()); | 
 |  | 
 |     // The primary page should still be the original page. | 
 |     EXPECT_EQ(web_contents->GetLastCommittedURL(), kOriginalUrl); | 
 |  | 
 |     // Cancel the prerender while the CommitDeferringCondition is running. | 
 |     registry->CancelHost(prerender_frame_tree_node_id, | 
 |                          PrerenderHost::FinalStatus::kDestroyed); | 
 |     prerender_host_observer.WaitForDestroyed(); | 
 |     EXPECT_FALSE(prerender_host_observer.was_activated()); | 
 |     EXPECT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl), nullptr); | 
 |  | 
 |     // Resume the activation. This should fall back to a regular navigation. | 
 |     installer.condition().CallResumeClosure(); | 
 |   } | 
 |  | 
 |   navigation->Commit(); | 
 |   EXPECT_EQ(web_contents->GetPrimaryMainFrame()->GetLastCommittedURL(), | 
 |             kPrerenderingUrl); | 
 | } | 
 |  | 
 | // Test cancelling a prerender and then starting a new prerender for the same | 
 | // URL while a CommitDeferringCondition is running. This activation should not | 
 | // reserve the second prerender and should fall back to a regular navigation. | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CancelAndStartHostWhileCommitDeferringConditionIsRunning) { | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   const GURL kPrerenderingUrl("https://example.com/next"); | 
 |  | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |  | 
 |   const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   render_frame_host), | 
 |       *web_contents); | 
 |   ASSERT_NE(prerender_frame_tree_node_id, kNoFrameTreeNodeId); | 
 |   PrerenderHost* prerender_host = | 
 |       registry->FindHostByUrlForTesting(kPrerenderingUrl); | 
 |   CommitPrerenderNavigation(*prerender_host); | 
 |  | 
 |   test::PrerenderHostObserver prerender_host_observer(*web_contents, | 
 |                                                       kPrerenderingUrl); | 
 |  | 
 |   // Now navigate the primary page to the prerendered URL so that we activate | 
 |   // the prerender. Use a CommitDeferringCondition to pause activation | 
 |   // before it completes. | 
 |   std::unique_ptr<NavigationSimulatorImpl> navigation; | 
 |   base::OnceClosure resume_navigation; | 
 |  | 
 |   { | 
 |     MockCommitDeferringConditionInstaller installer( | 
 |         kPrerenderingUrl, | 
 |         /*is_ready_to_commit=*/false); | 
 |     // Start trying to activate the prerendered page. | 
 |     navigation = CreateActivation(kPrerenderingUrl, *web_contents); | 
 |     navigation->Start(); | 
 |  | 
 |     // Wait for the condition to pause the activation. | 
 |     installer.WaitUntilInstalled(); | 
 |     installer.condition().WaitUntilInvoked(); | 
 |     resume_navigation = installer.condition().TakeResumeClosure(); | 
 |  | 
 |     // The request should be deferred by the condition. | 
 |     NavigationRequest* navigation_request = | 
 |         static_cast<NavigationRequest*>(navigation->GetNavigationHandle()); | 
 |     EXPECT_TRUE( | 
 |         navigation_request->IsCommitDeferringConditionDeferredForTesting()); | 
 |  | 
 |     // The primary page should still be the original page. | 
 |     EXPECT_EQ(web_contents->GetLastCommittedURL(), kOriginalUrl); | 
 |  | 
 |     // Cancel the prerender while the CommitDeferringCondition is running. | 
 |     registry->CancelHost(prerender_frame_tree_node_id, | 
 |                          PrerenderHost::FinalStatus::kDestroyed); | 
 |     prerender_host_observer.WaitForDestroyed(); | 
 |     EXPECT_FALSE(prerender_host_observer.was_activated()); | 
 |     EXPECT_EQ(registry->FindHostByUrlForTesting(kPrerenderingUrl), nullptr); | 
 |   } | 
 |  | 
 |   { | 
 |     // Start the second prerender for the same URL. | 
 |     const int prerender_frame_tree_node_id2 = registry->CreateAndStartHost( | 
 |         GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                     PrerenderTriggerType::kSpeculationRule, "", | 
 |                                     render_frame_host), | 
 |         *web_contents); | 
 |     ASSERT_NE(prerender_frame_tree_node_id2, kNoFrameTreeNodeId); | 
 |     PrerenderHost* prerender_host2 = | 
 |         registry->FindHostByUrlForTesting(kPrerenderingUrl); | 
 |     CommitPrerenderNavigation(*prerender_host2); | 
 |  | 
 |     EXPECT_NE(prerender_frame_tree_node_id, prerender_frame_tree_node_id2); | 
 |   } | 
 |  | 
 |   // Resume the initial activation. This should not reserve the second | 
 |   // prerender and should fall back to a regular navigation. | 
 |   std::move(resume_navigation).Run(); | 
 |   navigation->Commit(); | 
 |   EXPECT_EQ(web_contents->GetPrimaryMainFrame()->GetLastCommittedURL(), | 
 |             kPrerenderingUrl); | 
 |  | 
 |   // The second prerender should still exist. | 
 |   EXPECT_NE(registry->FindHostByUrlForTesting(kPrerenderingUrl), nullptr); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        DontStartPrerenderWhenTriggerIsAlreadyHidden) { | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(GURL("https://example.com/")); | 
 |   // The visibility state to be HIDDEN will cause prerendering not started. | 
 |   web_contents->WasHidden(); | 
 |  | 
 |   const GURL kPrerenderingUrl = GURL("https://example.com/empty.html"); | 
 |   RenderFrameHostImpl* initiator_rfh = web_contents->GetPrimaryMainFrame(); | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |   const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   initiator_rfh), | 
 |       *web_contents); | 
 |   EXPECT_EQ(prerender_frame_tree_node_id, RenderFrameHost::kNoFrameTreeNodeId); | 
 |   PrerenderHost* prerender_host = | 
 |       registry->FindNonReservedHostById(prerender_frame_tree_node_id); | 
 |   EXPECT_EQ(prerender_host, nullptr); | 
 |   ExpectUniqueSampleOfFinalStatus( | 
 |       PrerenderHost::FinalStatus::kTriggerBackgrounded); | 
 | } | 
 |  | 
 | // ------------------------------------------------- | 
 | // Activation navigation parameter matching unit tests. | 
 | // These tests change a parameter to differentiate the activation request from | 
 | // the prerendering request. | 
 |  | 
 | // A positive test to show that if the navigation params are equal then the | 
 | // prerender host is selected for activation. | 
 | TEST_F(PrerenderHostRegistryTest, SameInitialAndActivationParams) { | 
 |   EXPECT_TRUE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         // Do not change any params, so activation happens. | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kOk); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationBeginParams_InitiatorFrameToken) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         const GURL kOriginalUrl("https://example.com/"); | 
 |         navigation->SetInitiatorFrame(nullptr); | 
 |         navigation->set_initiator_origin(url::Origin::Create(kOriginalUrl)); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kInitiatorFrameToken); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationBeginParams_Headers) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_request_headers("User-Agent: Test"); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kHttpRequestHeader); | 
 | } | 
 |  | 
 | // Tests that the Purpose header is ignored when comparing request headers. | 
 | TEST_F(PrerenderHostRegistryTest, PurposeHeaderIsIgnoredForParamMatching) { | 
 |   EXPECT_TRUE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_request_headers("Purpose: Test"); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kOk); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationBeginParams_LoadFlags) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_load_flags(net::LOAD_ONLY_FROM_CACHE); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kLoadFlags); | 
 |  | 
 |   // If the potential activation request requires validation or bypass of the | 
 |   // browser cache, the prerendered page should not be activated. | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_load_flags(net::LOAD_VALIDATE_CACHE); | 
 |       }))); | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_load_flags(net::LOAD_BYPASS_CACHE); | 
 |       }))); | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_load_flags(net::LOAD_DISABLE_CACHE); | 
 |       }))); | 
 |   ExpectBucketCountOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kCacheLoadFlags, 3); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationBeginParams_SkipServiceWorker) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_skip_service_worker(true); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kSkipServiceWorker); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationBeginParams_MixedContentContextType) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_mixed_content_context_type( | 
 |             blink::mojom::MixedContentContextType::kNotMixedContent); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kMixedContentContextType); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationBeginParams_IsFormSubmission) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->SetIsFormSubmission(true); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kIsFormSubmission); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationBeginParams_SearchableFormUrl) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         const GURL kOriginalUrl("https://example.com/"); | 
 |         navigation->set_searchable_form_url(kOriginalUrl); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kSearchableFormUrl); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationBeginParams_SearchableFormEncoding) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_searchable_form_encoding("Test encoding"); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kSearchableFormEncoding); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationCommonParams_InitiatorOrigin) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_initiator_origin(url::Origin()); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kInitiatorOrigin); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationCommonParams_ShouldNotCheckMainWorldCSP) { | 
 |   // Initial navigation blocked by the main world CSP cancels prerendering. | 
 |   // So, it's safe to match the page for CSP bypassing requests from isolated | 
 |   // worlds (e.g., extensions). | 
 |   EXPECT_TRUE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_should_check_main_world_csp( | 
 |             network::mojom::CSPDisposition::DO_NOT_CHECK); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kOk); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationCommonParams_Method) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->SetMethod("POST"); | 
 |       }))); | 
 |   // The method parameter change is detected as a HTTP request header change. | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kHttpRequestHeader); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationCommonParams_HrefTranslate) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_href_translate("test"); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kHrefTranslate); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationCommonParams_Transition) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->SetTransition(ui::PAGE_TRANSITION_FORM_SUBMIT); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kTransition); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationCommonParams_RequestContextType) { | 
 |   EXPECT_FALSE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_request_context_type( | 
 |             blink::mojom::RequestContextType::AUDIO); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kRequestContextType); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        CompareInitialAndActivationCommonParams_ReferrerPolicy) { | 
 |   EXPECT_TRUE(CheckIsActivatedForParams( | 
 |       base::BindLambdaForTesting([&](NavigationSimulatorImpl* navigation) { | 
 |         navigation->SetReferrer(blink::mojom::Referrer::New( | 
 |             web_contents()->GetPrimaryMainFrame()->GetLastCommittedURL(), | 
 |             network::mojom::ReferrerPolicy::kAlways)); | 
 |       }))); | 
 |   ExpectUniqueSampleOfActivationNavigationParamsMatch( | 
 |       PrerenderHost::ActivationNavigationParamsMatch::kOk); | 
 | } | 
 |  | 
 | // End navigation parameter matching tests --------- | 
 |  | 
 | // Begin replication state matching tests ---------- | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, InsecureRequestPolicyIsSetWhilePrerendering) { | 
 |   SetupPrerenderAndCommit( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_insecure_request_policy( | 
 |             blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent); | 
 |       })); | 
 |   EXPECT_EQ(static_cast<TestWebContents*>(web_contents()) | 
 |                 ->GetPrimaryMainFrame() | 
 |                 ->frame_tree_node() | 
 |                 ->current_replication_state() | 
 |                 .insecure_request_policy, | 
 |             blink::mojom::InsecureRequestPolicy::kBlockAllMixedContent); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        InsecureNavigationsSetIsSetWhilePrerendering) { | 
 |   SetupPrerenderAndCommit( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         const std::vector<uint32_t> insecure_navigations = {1, 2}; | 
 |         navigation->set_insecure_navigations_set(insecure_navigations); | 
 |       })); | 
 |   const std::vector<uint32_t> insecure_navigations = {1, 2}; | 
 |   EXPECT_EQ(static_cast<TestWebContents*>(web_contents()) | 
 |                 ->GetPrimaryMainFrame() | 
 |                 ->frame_tree_node() | 
 |                 ->current_replication_state() | 
 |                 .insecure_navigations_set, | 
 |             insecure_navigations); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, | 
 |        HasPotentiallyTrustworthyUniqueOriginIsSetWhilePrerendering) { | 
 |   SetupPrerenderAndCommit( | 
 |       base::BindLambdaForTesting([](NavigationSimulatorImpl* navigation) { | 
 |         navigation->set_has_potentially_trustworthy_unique_origin(true); | 
 |       })); | 
 |   EXPECT_TRUE(static_cast<TestWebContents*>(web_contents()) | 
 |                   ->GetPrimaryMainFrame() | 
 |                   ->frame_tree_node() | 
 |                   ->current_replication_state() | 
 |                   .has_potentially_trustworthy_unique_origin); | 
 | } | 
 |  | 
 | TEST_F(PrerenderHostRegistryTest, DisallowPageHavingEffectiveUrl) { | 
 |   const GURL kOriginalUrl("https://example.com/"); | 
 |   const GURL kModifiedSiteUrl("custom-scheme://custom"); | 
 |  | 
 |   EffectiveURLContentBrowserClient modified_client( | 
 |       kOriginalUrl, kModifiedSiteUrl, | 
 |       /* requires_dedicated_process */ false); | 
 |   ContentBrowserClient* old_client = | 
 |       SetBrowserClientForTesting(&modified_client); | 
 |  | 
 |   std::unique_ptr<TestWebContents> web_contents = | 
 |       CreateWebContents(kOriginalUrl); | 
 |   RenderFrameHostImpl* render_frame_host = web_contents->GetPrimaryMainFrame(); | 
 |   ASSERT_TRUE(render_frame_host); | 
 |  | 
 |   const GURL kPrerenderingUrl = GURL("https://example.com/empty.html"); | 
 |   RenderFrameHostImpl* initiator_rfh = web_contents->GetPrimaryMainFrame(); | 
 |   PrerenderHostRegistry* registry = web_contents->GetPrerenderHostRegistry(); | 
 |   const int prerender_frame_tree_node_id = registry->CreateAndStartHost( | 
 |       GeneratePrerenderAttributes(kPrerenderingUrl, | 
 |                                   PrerenderTriggerType::kSpeculationRule, "", | 
 |                                   initiator_rfh), | 
 |       *web_contents); | 
 |   EXPECT_EQ(prerender_frame_tree_node_id, RenderFrameHost::kNoFrameTreeNodeId); | 
 |   PrerenderHost* prerender_host = | 
 |       registry->FindNonReservedHostById(prerender_frame_tree_node_id); | 
 |   EXPECT_EQ(prerender_host, nullptr); | 
 |   ExpectUniqueSampleOfFinalStatus(PrerenderHost::FinalStatus::kHasEffectiveUrl); | 
 |  | 
 |   SetBrowserClientForTesting(old_client); | 
 | } | 
 |  | 
 | // End replication state matching tests ------------ | 
 |  | 
 | }  // namespace | 
 | }  // namespace content |