blob: b3342dfe789ef2f9e82d0aeaee302df7fa85d683 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2020 The Chromium Authors
Hiroki Nakagawa968139e22020-10-22 15:03:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Sreeja Kamishettyc227f7a2022-07-08 22:33:155#include "content/browser/preloading/prerender/prerender_host.h"
kenoss3bd73b82024-10-10 20:33:496
Miina Koyamad0e41f32023-11-13 06:50:097#include <memory>
Hiroki Nakagawa968139e22020-10-22 15:03:568
Lingqi Chi8098c5e92022-09-21 13:24:509#include "base/functional/bind.h"
Sean Maher52fa5a72022-11-14 15:53:2510#include "base/task/sequenced_task_runner.h"
Lingqi Chi8098c5e92022-09-21 13:24:5011#include "base/test/bind.h"
Miina Koyamad0e41f32023-11-13 06:50:0912#include "base/test/gmock_expected_support.h"
Robert Lin1ae40562021-09-17 02:48:0113#include "build/build_config.h"
Asami Doi2dfb2672022-02-21 10:45:5214#include "components/ukm/test_ukm_recorder.h"
kenossc3f9a2c2025-03-06 15:35:5115#include "content/browser/preloading/preload_pipeline_info_impl.h"
Lingqi Chi8098c5e92022-09-21 13:24:5016#include "content/browser/preloading/preloading.h"
Sreeja Kamishettyc227f7a2022-07-08 22:33:1517#include "content/browser/preloading/prerender/prerender_attributes.h"
Yoshiki Tanioka49b4cfb2022-10-20 09:25:3118#include "content/browser/preloading/prerender/prerender_final_status.h"
Miina Koyamad0e41f32023-11-13 06:50:0919#include "content/browser/preloading/prerender/prerender_host.h"
Sreeja Kamishettyc227f7a2022-07-08 22:33:1520#include "content/browser/preloading/prerender/prerender_host_registry.h"
Miina Koyamad0e41f32023-11-13 06:50:0921#include "content/browser/preloading/prerender/prerender_metrics.h"
kenossc3f9a2c2025-03-06 15:35:5122#include "content/public/browser/preload_pipeline_info.h"
Lingqi Chi8098c5e92022-09-21 13:24:5023#include "content/public/browser/preloading.h"
24#include "content/public/browser/preloading_data.h"
Sreeja Kamishetty0be3b1b2021-08-12 17:04:1525#include "content/public/test/mock_web_contents_observer.h"
Matt Falkenhagen37d91a9162021-01-08 08:13:2826#include "content/public/test/navigation_simulator.h"
Sreeja Kamishetty26e40d22022-10-17 17:54:3527#include "content/public/test/preloading_test_util.h"
Takashi Toyoshima9c8b08c2022-05-17 10:11:3328#include "content/public/test/prerender_test_util.h"
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:3629#include "content/test/mock_commit_deferring_condition.h"
Harkiran Bolaria59290d62021-03-17 01:53:0130#include "content/test/navigation_simulator_impl.h"
Lingqi Chi82efa95e2020-12-29 05:31:1931#include "content/test/test_render_frame_host.h"
Hiroki Nakagawa968139e22020-10-22 15:03:5632#include "content/test/test_render_view_host.h"
33#include "content/test/test_web_contents.h"
Miina Koyamad0e41f32023-11-13 06:50:0934#include "net/http/http_request_headers.h"
Asami Doi2dfb2672022-02-21 10:45:5235#include "services/metrics/public/cpp/ukm_builders.h"
Sreeja Kamishetty0be3b1b2021-08-12 17:04:1536#include "third_party/blink/public/common/loader/loader_constants.h"
Taiyo Mizuhashia19f037d2023-07-28 06:39:4337#include "third_party/blink/public/mojom/speculation_rules/speculation_rules.mojom-shared.h"
Hiroki Nakagawa968139e22020-10-22 15:03:5638
39namespace content {
40namespace {
41
Sreeja Kamishetty0be3b1b2021-08-12 17:04:1542using ::testing::_;
lingqi5e583f1f2023-05-14 06:16:4043
44TEST(IsActivationHeaderMatchTest, OrderInsensitive) {
Miina Koyamad0e41f32023-11-13 06:50:0945 PrerenderCancellationReason reason = PrerenderCancellationReason(
46 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
lingqi5e583f1f2023-05-14 06:16:4047 net::HttpRequestHeaders prerender_headers;
Doug Turnerb204399e2025-07-30 16:51:1648 prerender_headers.SetHeader("name1", "value1");
49 prerender_headers.SetHeader("name2", "value2");
50 prerender_headers.SetHeader("name3", "value3");
lingqi5e583f1f2023-05-14 06:16:4051 net::HttpRequestHeaders potential_activation_headers;
Doug Turnerb204399e2025-07-30 16:51:1652 potential_activation_headers.SetHeader("name2", "value2");
53 potential_activation_headers.SetHeader("name3", "value3");
54 potential_activation_headers.SetHeader("name1", "value1");
lingqi5e583f1f2023-05-14 06:16:4055 EXPECT_TRUE(PrerenderHost::IsActivationHeaderMatch(
Miina Koyamad0e41f32023-11-13 06:50:0956 potential_activation_headers, prerender_headers, reason));
lingqi5e583f1f2023-05-14 06:16:4057}
58
59TEST(IsActivationHeaderMatchTest, KeyCaseInsensitive) {
Miina Koyamad0e41f32023-11-13 06:50:0960 PrerenderCancellationReason reason = PrerenderCancellationReason(
61 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
lingqi5e583f1f2023-05-14 06:16:4062 net::HttpRequestHeaders prerender_headers;
Doug Turnerb204399e2025-07-30 16:51:1663 prerender_headers.SetHeader("NAME1", "value1");
64 prerender_headers.SetHeader("name2", "value2");
65 prerender_headers.SetHeader("name3", "value3");
lingqi5e583f1f2023-05-14 06:16:4066 net::HttpRequestHeaders potential_activation_headers;
Doug Turnerb204399e2025-07-30 16:51:1667 potential_activation_headers.SetHeader("name1", "value1");
68 potential_activation_headers.SetHeader("name2", "value2");
69 potential_activation_headers.SetHeader("name3", "value3");
lingqi5e583f1f2023-05-14 06:16:4070 EXPECT_TRUE(PrerenderHost::IsActivationHeaderMatch(
Miina Koyamad0e41f32023-11-13 06:50:0971 potential_activation_headers, prerender_headers, reason));
lingqi5e583f1f2023-05-14 06:16:4072}
73
74TEST(IsActivationHeaderMatchTest, ValueCaseInsensitive) {
Miina Koyamad0e41f32023-11-13 06:50:0975 PrerenderCancellationReason reason = PrerenderCancellationReason(
76 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
lingqi5e583f1f2023-05-14 06:16:4077 net::HttpRequestHeaders prerender_headers;
Doug Turnerb204399e2025-07-30 16:51:1678 prerender_headers.SetHeader("name1", "value1");
79 prerender_headers.SetHeader("name2", "value2");
80 prerender_headers.SetHeader("name3", "value3");
lingqi5e583f1f2023-05-14 06:16:4081 net::HttpRequestHeaders potential_activation_headers;
Doug Turnerb204399e2025-07-30 16:51:1682 potential_activation_headers.SetHeader("name1", "value1");
83 potential_activation_headers.SetHeader("name2", "VALUE2");
84 potential_activation_headers.SetHeader("name3", "value3");
lingqi5e583f1f2023-05-14 06:16:4085 EXPECT_TRUE(PrerenderHost::IsActivationHeaderMatch(
Miina Koyamad0e41f32023-11-13 06:50:0986 potential_activation_headers, prerender_headers, reason));
87}
88
89TEST(IsActivationHeaderMatchTest, CalculateMismatchedHeaders) {
90 auto same_key_value = [](const PrerenderMismatchedHeaders& a,
91 const PrerenderMismatchedHeaders& b) {
92 return a.header_name == b.header_name &&
93 a.initial_value == b.initial_value &&
94 a.activation_value == b.activation_value;
95 };
96 {
97 PrerenderCancellationReason reason = PrerenderCancellationReason(
98 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
99 net::HttpRequestHeaders prerender_headers;
Doug Turnerb204399e2025-07-30 16:51:16100 prerender_headers.SetHeader("name1", "value1");
101 prerender_headers.SetHeader("name2", "value2");
102 prerender_headers.SetHeader("name3", "value3");
Miina Koyamad0e41f32023-11-13 06:50:09103 net::HttpRequestHeaders potential_headers;
Doug Turnerb204399e2025-07-30 16:51:16104 potential_headers.SetHeader("name1", "value1");
105 potential_headers.SetHeader("name2", "value2");
106 potential_headers.SetHeader("name3", "value3");
Miina Koyamad0e41f32023-11-13 06:50:09107 EXPECT_TRUE(PrerenderHost::IsActivationHeaderMatch(
108 potential_headers, prerender_headers, reason));
109 EXPECT_FALSE(reason.GetPrerenderMismatchedHeaders());
110 }
111 {
112 PrerenderCancellationReason reason = PrerenderCancellationReason(
113 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
114 net::HttpRequestHeaders prerender_headers;
Miina Koyamad0e41f32023-11-13 06:50:09115 net::HttpRequestHeaders potential_headers;
Miina Koyamad0e41f32023-11-13 06:50:09116 EXPECT_TRUE(PrerenderHost::IsActivationHeaderMatch(
117 potential_headers, prerender_headers, reason));
118 EXPECT_FALSE(reason.GetPrerenderMismatchedHeaders());
119 }
120 {
121 PrerenderCancellationReason reason = PrerenderCancellationReason(
122 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
123 net::HttpRequestHeaders prerender_headers;
Doug Turnerb204399e2025-07-30 16:51:16124 prerender_headers.SetHeader("name1", "value1");
125 prerender_headers.SetHeader("name2", "value2");
126 prerender_headers.SetHeader("name3", "value3");
127 prerender_headers.SetHeader("name5", "value3");
Miina Koyamad0e41f32023-11-13 06:50:09128 net::HttpRequestHeaders potential_headers;
Doug Turnerb204399e2025-07-30 16:51:16129 potential_headers.SetHeader("name1", "value1");
130 potential_headers.SetHeader("name3", "value2");
131 potential_headers.SetHeader("name4", "value4");
132 potential_headers.SetHeader("name5", "value3");
Miina Koyamad0e41f32023-11-13 06:50:09133 EXPECT_FALSE(PrerenderHost::IsActivationHeaderMatch(
134 potential_headers, prerender_headers, reason));
135 std::vector<PrerenderMismatchedHeaders> mismatched_headers_expected;
Arthur Sonzognic686e8f2024-01-11 08:36:37136 mismatched_headers_expected.emplace_back("name2", "value2", std::nullopt);
Miina Koyamad0e41f32023-11-13 06:50:09137 mismatched_headers_expected.emplace_back("name3", "value3", "value2");
Arthur Sonzognic686e8f2024-01-11 08:36:37138 mismatched_headers_expected.emplace_back("name4", std::nullopt, "value4");
Miina Koyamad0e41f32023-11-13 06:50:09139
140 EXPECT_TRUE(std::equal(reason.GetPrerenderMismatchedHeaders()->begin(),
141 reason.GetPrerenderMismatchedHeaders()->end(),
142 mismatched_headers_expected.begin(),
143 mismatched_headers_expected.end(), same_key_value));
144 }
145 {
146 PrerenderCancellationReason reason = PrerenderCancellationReason(
147 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
148 net::HttpRequestHeaders prerender_headers;
Doug Turnerb204399e2025-07-30 16:51:16149 prerender_headers.SetHeader("name5", "value1");
150 prerender_headers.SetHeader("name6", "value2");
151 prerender_headers.SetHeader("name7", "value3");
Miina Koyamad0e41f32023-11-13 06:50:09152 net::HttpRequestHeaders potential_headers;
Doug Turnerb204399e2025-07-30 16:51:16153 potential_headers.SetHeader("name2", "value1");
Miina Koyamad0e41f32023-11-13 06:50:09154 EXPECT_FALSE(PrerenderHost::IsActivationHeaderMatch(
155 potential_headers, prerender_headers, reason));
156 std::vector<PrerenderMismatchedHeaders> mismatched_headers_expected;
Arthur Sonzognic686e8f2024-01-11 08:36:37157 mismatched_headers_expected.emplace_back("name2", std::nullopt, "value1");
158 mismatched_headers_expected.emplace_back("name5", "value1", std::nullopt);
159 mismatched_headers_expected.emplace_back("name6", "value2", std::nullopt);
160 mismatched_headers_expected.emplace_back("name7", "value3", std::nullopt);
Miina Koyamad0e41f32023-11-13 06:50:09161
162 EXPECT_TRUE(std::equal(reason.GetPrerenderMismatchedHeaders()->begin(),
163 reason.GetPrerenderMismatchedHeaders()->end(),
164 mismatched_headers_expected.begin(),
165 mismatched_headers_expected.end(), same_key_value));
166 }
167 {
168 PrerenderCancellationReason reason = PrerenderCancellationReason(
169 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
170 net::HttpRequestHeaders prerender_headers;
Doug Turnerb204399e2025-07-30 16:51:16171 prerender_headers.SetHeader("name5", "value1");
172 prerender_headers.SetHeader("name6", "value2");
Miina Koyamad0e41f32023-11-13 06:50:09173 net::HttpRequestHeaders potential_headers;
Doug Turnerb204399e2025-07-30 16:51:16174 potential_headers.SetHeader("name2", "value1");
175 potential_headers.SetHeader("name6", "value2");
176 potential_headers.SetHeader("name7", "value3");
177 potential_headers.SetHeader("name8", "value3");
Miina Koyamad0e41f32023-11-13 06:50:09178 EXPECT_FALSE(PrerenderHost::IsActivationHeaderMatch(
179 potential_headers, prerender_headers, reason));
180 std::vector<PrerenderMismatchedHeaders> mismatched_headers_expected;
Arthur Sonzognic686e8f2024-01-11 08:36:37181 mismatched_headers_expected.emplace_back("name2", std::nullopt, "value1");
182 mismatched_headers_expected.emplace_back("name5", "value1", std::nullopt);
183 mismatched_headers_expected.emplace_back("name7", std::nullopt, "value3");
184 mismatched_headers_expected.emplace_back("name8", std::nullopt, "value3");
Miina Koyamad0e41f32023-11-13 06:50:09185
186 EXPECT_TRUE(std::equal(reason.GetPrerenderMismatchedHeaders()->begin(),
187 reason.GetPrerenderMismatchedHeaders()->end(),
188 mismatched_headers_expected.begin(),
189 mismatched_headers_expected.end(), same_key_value));
190 }
191 {
192 PrerenderCancellationReason reason = PrerenderCancellationReason(
193 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
194 net::HttpRequestHeaders prerender_headers;
Miina Koyamad0e41f32023-11-13 06:50:09195 net::HttpRequestHeaders potential_headers;
Doug Turnerb204399e2025-07-30 16:51:16196 potential_headers.SetHeader("name1", "value1");
197 potential_headers.SetHeader("name2", "value2");
198 potential_headers.SetHeader("name3", "value3");
Miina Koyamad0e41f32023-11-13 06:50:09199 EXPECT_FALSE(PrerenderHost::IsActivationHeaderMatch(
200 potential_headers, prerender_headers, reason));
201 std::vector<PrerenderMismatchedHeaders> mismatched_headers_expected;
Arthur Sonzognic686e8f2024-01-11 08:36:37202 mismatched_headers_expected.emplace_back("name1", std::nullopt, "value1");
203 mismatched_headers_expected.emplace_back("name2", std::nullopt, "value2");
204 mismatched_headers_expected.emplace_back("name3", std::nullopt, "value3");
Miina Koyamad0e41f32023-11-13 06:50:09205
206 EXPECT_TRUE(std::equal(reason.GetPrerenderMismatchedHeaders()->begin(),
207 reason.GetPrerenderMismatchedHeaders()->end(),
208 mismatched_headers_expected.begin(),
209 mismatched_headers_expected.end(), same_key_value));
210 }
211 {
212 PrerenderCancellationReason reason = PrerenderCancellationReason(
213 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
214 net::HttpRequestHeaders prerender_headers;
Doug Turnerb204399e2025-07-30 16:51:16215 prerender_headers.SetHeader("name1", "value1");
216 prerender_headers.SetHeader("name2", "value2");
217 prerender_headers.SetHeader("name3", "value3");
Miina Koyamad0e41f32023-11-13 06:50:09218 net::HttpRequestHeaders potential_headers;
Miina Koyamad0e41f32023-11-13 06:50:09219 EXPECT_FALSE(PrerenderHost::IsActivationHeaderMatch(
220 potential_headers, prerender_headers, reason));
221 std::vector<PrerenderMismatchedHeaders> mismatched_headers_expected;
Arthur Sonzognic686e8f2024-01-11 08:36:37222 mismatched_headers_expected.emplace_back("name1", "value1", std::nullopt);
223 mismatched_headers_expected.emplace_back("name2", "value2", std::nullopt);
224 mismatched_headers_expected.emplace_back("name3", "value3", std::nullopt);
Miina Koyamad0e41f32023-11-13 06:50:09225
226 EXPECT_TRUE(std::equal(reason.GetPrerenderMismatchedHeaders()->begin(),
227 reason.GetPrerenderMismatchedHeaders()->end(),
228 mismatched_headers_expected.begin(),
229 mismatched_headers_expected.end(), same_key_value));
230 }
lingqi5e583f1f2023-05-14 06:16:40231}
232
Lingqi Chi8098c5e92022-09-21 13:24:50233using ExpectedReadyForActivationState =
234 base::StrongAlias<class ExpectedReadyForActivationStateType, bool>;
Sreeja Kamishetty0be3b1b2021-08-12 17:04:15235
Matt Falkenhagen21ee4f82021-04-13 08:14:28236// Finish a prerendering navigation that was already started with
237// CreateAndStartHost().
Lingqi Chi8098c5e92022-09-21 13:24:50238void CommitPrerenderNavigation(
239 PrerenderHost& host,
240 ExpectedReadyForActivationState ready_for_activation =
Liviu Tinta0fcb29022024-03-14 14:03:56241 ExpectedReadyForActivationState(true),
242 scoped_refptr<net::HttpResponseHeaders> headers = nullptr) {
Matt Falkenhagen21ee4f82021-04-13 08:14:28243 // Normally we could use EmbeddedTestServer to provide a response, but these
244 // tests use RenderViewHostImplTestHarness so the load goes through a
245 // TestNavigationURLLoader which we don't have access to in order to
246 // complete. Use NavigationSimulator to finish the navigation.
247 FrameTreeNode* ftn = FrameTreeNode::From(host.GetPrerenderedMainFrameHost());
248 std::unique_ptr<NavigationSimulator> sim =
249 NavigationSimulatorImpl::CreateFromPendingInFrame(ftn);
Liviu Tinta0fcb29022024-03-14 14:03:56250 sim->SetResponseHeaders(headers);
Matt Falkenhagen21ee4f82021-04-13 08:14:28251 sim->Commit();
Lingqi Chi8098c5e92022-09-21 13:24:50252 EXPECT_EQ(host.is_ready_for_activation(), ready_for_activation.value());
Matt Falkenhagen21ee4f82021-04-13 08:14:28253}
254
Harkiran Bolaria2fb0e0e2021-08-09 19:13:52255std::unique_ptr<NavigationSimulatorImpl> CreateActivation(
256 const GURL& prerendering_url,
257 WebContentsImpl& web_contents) {
258 std::unique_ptr<NavigationSimulatorImpl> navigation =
259 NavigationSimulatorImpl::CreateRendererInitiated(
Dave Tapuska327c06c92022-06-13 20:31:51260 prerendering_url, web_contents.GetPrimaryMainFrame());
Harkiran Bolaria2fb0e0e2021-08-09 19:13:52261 navigation->SetReferrer(blink::mojom::Referrer::New(
Dave Tapuska327c06c92022-06-13 20:31:51262 web_contents.GetPrimaryMainFrame()->GetLastCommittedURL(),
Harkiran Bolaria2fb0e0e2021-08-09 19:13:52263 network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
264 return navigation;
265}
266
Hiroki Nakagawa0a90bd42021-04-21 01:53:05267class PrerenderHostTest : public RenderViewHostImplTestHarness {
Hiroki Nakagawa968139e22020-10-22 15:03:56268 public:
Asami Doi39651732023-01-31 05:25:26269 ~PrerenderHostTest() override = default;
Hiroki Nakagawa968139e22020-10-22 15:03:56270
271 void SetUp() override {
Hiroki Nakagawa968139e22020-10-22 15:03:56272 RenderViewHostImplTestHarness::SetUp();
Johannbecce082023-01-17 05:14:32273 web_contents_delegate_ =
274 std::make_unique<test::ScopedPrerenderWebContentsDelegate>(*contents());
Hiroki Nakagawae72cf55c2022-11-30 05:58:14275 contents()->NavigateAndCommit(GURL("https://example.com"));
Hiroki Nakagawa968139e22020-10-22 15:03:56276 }
277
Hiroki Nakagawae72cf55c2022-11-30 05:58:14278 PrerenderAttributes GeneratePrerenderAttributes(const GURL& url) {
Arthur Sonzognifd1e08d2024-03-05 15:59:36279 return GeneratePrerenderAttributesWithPredicate(url,
280 /*url_match_predicate=*/{});
Hiroki Nakagawae72cf55c2022-11-30 05:58:14281 }
282
283 PrerenderAttributes GeneratePrerenderAttributesWithPredicate(
284 const GURL& url,
Liviu Tintacf066d52024-07-24 16:00:55285 base::RepeatingCallback<bool(const GURL&,
286 const std::optional<content::UrlMatchType>&)>
287 url_match_predicate) {
Hiroki Nakagawae72cf55c2022-11-30 05:58:14288 RenderFrameHostImpl* rfh = contents()->GetPrimaryMainFrame();
289 return PrerenderAttributes(
Kouhei Ueno3f37992b2023-11-09 23:29:02290 url, PreloadingTriggerType::kSpeculationRule,
Hiroki Nakagawaeeecc56e2025-03-27 02:57:30291 /*embedder_histogram_suffix=*/"", SpeculationRulesParams(), Referrer(),
Rulong Chen(陈汝龙)bf12169c2024-12-16 05:38:16292 /*no_vary_search_hint=*/std::nullopt, rfh, contents()->GetWeakPtr(),
Lingqi Chi8ebe71e2024-10-22 07:08:39293 ui::PAGE_TRANSITION_LINK,
Lingqi Chia2efa8c2024-11-08 10:20:29294 /*should_warm_up_compositor=*/false,
Lingqi Chibc88ab52025-07-24 00:54:14295 /*should_prepare_paint_tree=*/false,
296 /*should_pause_javascript_execution=*/false,
297 std::move(url_match_predicate),
kenoss3bd73b82024-10-10 20:33:49298 /*prerender_navigation_handle_callback=*/{},
kenossc3f9a2c2025-03-06 15:35:51299 PreloadPipelineInfoImpl::Create(
Jiacheng Guoea20d392025-07-07 01:08:14300 /*planned_max_preloading_type=*/PreloadingType::kPrerender),
301 /*allow_reuse=*/false);
Hiroki Nakagawa968139e22020-10-22 15:03:56302 }
303
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31304 void ExpectFinalStatus(PrerenderFinalStatus status) {
Asami Doi2dfb2672022-02-21 10:45:52305 // Check FinalStatus in UMA.
Matt Falkenhagen9c56f5a02021-01-23 04:51:49306 histogram_tester_.ExpectUniqueSample(
Asami Doi4584b3b2021-12-13 04:17:14307 "Prerender.Experimental.PrerenderHostFinalStatus.SpeculationRule",
308 status, 1);
Asami Doi2dfb2672022-02-21 10:45:52309
310 // Check all entries in UKM to make sure that the recorded FinalStatus is
311 // equal to `status`. At least one entry should exist.
312 bool final_status_entry_found = false;
313 const auto entries = ukm_recorder_.GetEntriesByName(
314 ukm::builders::PrerenderPageLoad::kEntryName);
Ali Hijazie63cbaf62023-12-20 19:29:35315 for (const ukm::mojom::UkmEntry* entry : entries) {
Asami Doi2dfb2672022-02-21 10:45:52316 if (ukm_recorder_.EntryHasMetric(
317 entry, ukm::builders::PrerenderPageLoad::kFinalStatusName)) {
318 final_status_entry_found = true;
319 ukm_recorder_.ExpectEntryMetric(
320 entry, ukm::builders::PrerenderPageLoad::kFinalStatusName,
321 static_cast<int>(status));
322 }
323 }
324
325 EXPECT_TRUE(final_status_entry_found);
Matt Falkenhagen9c56f5a02021-01-23 04:51:49326 }
327
Hiroki Nakagawae72cf55c2022-11-30 05:58:14328 PrerenderHostRegistry& registry() {
329 return *contents()->GetPrerenderHostRegistry();
Hiroki Nakagawa968139e22020-10-22 15:03:56330 }
331
332 private:
Johanna4bf1cd2022-11-30 23:59:39333 test::ScopedPrerenderFeatureList prerender_feature_list_;
Johannbecce082023-01-17 05:14:32334 std::unique_ptr<test::ScopedPrerenderWebContentsDelegate>
335 web_contents_delegate_;
Matt Falkenhagen9c56f5a02021-01-23 04:51:49336 base::HistogramTester histogram_tester_;
Asami Doi2dfb2672022-02-21 10:45:52337 ukm::TestAutoSetUkmRecorder ukm_recorder_;
Hiroki Nakagawa968139e22020-10-22 15:03:56338};
339
Hiroki Nakagawad176addb2025-03-07 07:04:15340TEST_F(PrerenderHostTest, IsNoVarySearchHeaderSet) {
Liviu Tinta0fcb29022024-03-14 14:03:56341 // Start prerendering a page.
342 const GURL kPrerenderingUrl("https://example.com/next");
Avi Drissmandcc8e682024-09-04 14:14:48343 FrameTreeNodeId prerender_frame_tree_node_id =
344 contents()->AddPrerender(kPrerenderingUrl);
Liviu Tinta0fcb29022024-03-14 14:03:56345 PrerenderHost* prerender_host =
346 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
347 CommitPrerenderNavigation(
348 *prerender_host, ExpectedReadyForActivationState(true),
349 net::HttpResponseHeaders::Builder(net::HttpVersion(1, 1), "200 OK")
350 .AddHeader("No-Vary-Search", "params=(\"a\")")
351 .Build());
Hiroki Nakagawad176addb2025-03-07 07:04:15352 EXPECT_TRUE(prerender_host->no_vary_search().has_value());
Liviu Tinta0fcb29022024-03-14 14:03:56353}
354
Hiroki Nakagawa0a90bd42021-04-21 01:53:05355TEST_F(PrerenderHostTest, Activate) {
Matt Falkenhagen98b83732021-03-23 00:40:16356 // Start prerendering a page.
Hiroki Nakagawa968139e22020-10-22 15:03:56357 const GURL kPrerenderingUrl("https://example.com/next");
Avi Drissmandcc8e682024-09-04 14:14:48358 FrameTreeNodeId prerender_frame_tree_node_id =
359 contents()->AddPrerender(kPrerenderingUrl);
Hiroki Nakagawa9ba66692021-03-09 07:26:55360 PrerenderHost* prerender_host =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14361 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
Matt Falkenhagen21ee4f82021-04-13 08:14:28362 CommitPrerenderNavigation(*prerender_host);
Hiroki Nakagawa968139e22020-10-22 15:03:56363
Matt Falkenhagen98b83732021-03-23 00:40:16364 // Perform a navigation in the primary frame tree which activates the
365 // prerendered page.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14366 contents()->ActivatePrerenderedPage(kPrerenderingUrl);
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31367 ExpectFinalStatus(PrerenderFinalStatus::kActivated);
Matt Falkenhagen9c56f5a02021-01-23 04:51:49368}
369
Hiroki Nakagawa0a90bd42021-04-21 01:53:05370TEST_F(PrerenderHostTest, DontActivate) {
Matt Falkenhagen9c56f5a02021-01-23 04:51:49371 // Start the prerendering navigation, but don't activate it.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14372 const GURL kPrerenderingUrl("https://example.com/next");
Avi Drissmandcc8e682024-09-04 14:14:48373 const FrameTreeNodeId prerender_frame_tree_node_id =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14374 contents()->AddPrerender(kPrerenderingUrl);
375 registry().CancelHost(prerender_frame_tree_node_id,
376 PrerenderFinalStatus::kDestroyed);
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31377 ExpectFinalStatus(PrerenderFinalStatus::kDestroyed);
Hiroki Nakagawa968139e22020-10-22 15:03:56378}
379
Asami Doi39651732023-01-31 05:25:26380// Tests that cross-site main frame navigations in a prerendered page cannot
381// occur even if they start after the prerendered page has been reserved for
382// activation.
Hiroki Nakagawa0a90bd42021-04-21 01:53:05383TEST_F(PrerenderHostTest, MainFrameNavigationForReservedHost) {
Matt Falkenhagen21ee4f82021-04-13 08:14:28384 // Start prerendering a page.
385 const GURL kPrerenderingUrl("https://example.com/next");
Adithya Srinivasan8bda0b22021-06-14 20:41:37386 RenderFrameHostImpl* prerender_rfh =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14387 contents()->AddPrerenderAndCommitNavigation(kPrerenderingUrl);
Matt Falkenhagen16414e02021-06-22 05:58:54388 FrameTreeNode* ftn = prerender_rfh->frame_tree_node();
389 EXPECT_FALSE(ftn->HasNavigation());
Matt Falkenhagen21ee4f82021-04-13 08:14:28390
Hiroki Nakagawae72cf55c2022-11-30 05:58:14391 test::PrerenderHostObserver prerender_host_observer(*contents(),
Takashi Toyoshima9c8b08c2022-05-17 10:11:33392 kPrerenderingUrl);
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36393
394 // Now navigate the primary page to the prerendered URL so that we activate
395 // the prerender. Use a CommitDeferringCondition to pause activation
396 // before it completes.
397 std::unique_ptr<NavigationSimulatorImpl> navigation;
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36398
David Bokanb650c952022-02-17 19:56:40399 {
400 MockCommitDeferringConditionInstaller installer(
Mingyu Leifec7c7d2023-07-21 16:45:26401 kPrerenderingUrl, CommitDeferringCondition::Result::kDefer);
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36402 // Start trying to activate the prerendered page.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14403 navigation = CreateActivation(kPrerenderingUrl, *contents());
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36404 navigation->Start();
405
406 // Wait for the condition to pause the activation.
David Bokanb650c952022-02-17 19:56:40407 installer.WaitUntilInstalled();
408 installer.condition().WaitUntilInvoked();
409
410 // The request should be deferred by the condition.
411 NavigationRequest* navigation_request =
412 static_cast<NavigationRequest*>(navigation->GetNavigationHandle());
413 EXPECT_TRUE(
414 navigation_request->IsCommitDeferringConditionDeferredForTesting());
415
416 // The primary page should still be the original page.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14417 EXPECT_EQ(contents()->GetLastCommittedURL(), "https://example.com/");
David Bokanb650c952022-02-17 19:56:40418
419 const GURL kBadUrl("https://example2.test/");
Hiroki Nakagawae72cf55c2022-11-30 05:58:14420 TestNavigationManager tno(contents(), kBadUrl);
David Bokanb650c952022-02-17 19:56:40421
Asami Doi39651732023-01-31 05:25:26422 // Start a cross-site navigation in the prerendered page. It should be
David Bokanb650c952022-02-17 19:56:40423 // cancelled.
424 auto navigation_2 = NavigationSimulatorImpl::CreateRendererInitiated(
425 kBadUrl, prerender_rfh);
426 navigation_2->Start();
427 EXPECT_EQ(NavigationThrottle::CANCEL,
428 navigation_2->GetLastThrottleCheckResult());
Fergal Daly664780d32023-01-11 08:26:23429 ASSERT_TRUE(tno.WaitForNavigationFinished());
David Bokanb650c952022-02-17 19:56:40430 EXPECT_FALSE(tno.was_committed());
431
Asami Doi39651732023-01-31 05:25:26432 // The cross-site navigation cancels the activation.
David Bokanb650c952022-02-17 19:56:40433 installer.condition().CallResumeClosure();
Takashi Toyoshima9c8b08c2022-05-17 10:11:33434 prerender_host_observer.WaitForDestroyed();
435 EXPECT_FALSE(prerender_host_observer.was_activated());
Hiroki Nakagawae72cf55c2022-11-30 05:58:14436 EXPECT_EQ(registry().FindHostByUrlForTesting(kPrerenderingUrl), nullptr);
Hiroki Nakagawad04aaaf92023-03-20 17:13:28437 ExpectFinalStatus(
438 PrerenderFinalStatus::kCrossSiteNavigationInMainFrameNavigation);
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36439 }
440
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36441 // The activation falls back to regular navigation.
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36442 navigation->Commit();
Hiroki Nakagawae72cf55c2022-11-30 05:58:14443 EXPECT_EQ(contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36444 kPrerenderingUrl);
Matt Falkenhagen21ee4f82021-04-13 08:14:28445}
446
Alexander Timin46fb6eec2021-06-04 15:46:30447// Tests that an activation can successfully commit after the prerendering page
448// has updated its PageState.
449TEST_F(PrerenderHostTest, ActivationAfterPageStateUpdate) {
Alexander Timin46fb6eec2021-06-04 15:46:30450 // Start prerendering a page.
451 const GURL kPrerenderingUrl("https://example.com/next");
Avi Drissmandcc8e682024-09-04 14:14:48452 const FrameTreeNodeId prerender_frame_tree_node_id =
453 registry().CreateAndStartHost(
454 GeneratePrerenderAttributes(kPrerenderingUrl));
Alexander Timin46fb6eec2021-06-04 15:46:30455 PrerenderHost* prerender_host =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14456 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
Alexander Timin46fb6eec2021-06-04 15:46:30457 CommitPrerenderNavigation(*prerender_host);
458
Hiroki Nakagawae72cf55c2022-11-30 05:58:14459 auto* prerender_root_ftn =
Alexander Timin46fb6eec2021-06-04 15:46:30460 FrameTreeNode::GloballyFindByID(prerender_frame_tree_node_id);
461 RenderFrameHostImpl* prerender_rfh = prerender_root_ftn->current_frame_host();
462 NavigationEntryImpl* prerender_nav_entry =
Arthur Sonzognif6785ec2022-12-05 10:11:50463 prerender_root_ftn->frame_tree().controller().GetLastCommittedEntry();
Alexander Timin46fb6eec2021-06-04 15:46:30464 FrameNavigationEntry* prerender_root_fne =
465 prerender_nav_entry->GetFrameEntry(prerender_root_ftn);
466
Hiroki Nakagawae72cf55c2022-11-30 05:58:14467 auto page_state = blink::PageState::CreateForTestingWithSequenceNumbers(
468 GURL("about:blank"), prerender_root_fne->item_sequence_number(),
469 prerender_root_fne->document_sequence_number());
Alexander Timin46fb6eec2021-06-04 15:46:30470
471 // Update PageState for prerender RFH, causing it to become different from
472 // the one stored in RFH's last commit params.
473 static_cast<mojom::FrameHost*>(prerender_rfh)->UpdateState(page_state);
474
475 // Perform a navigation in the primary frame tree which activates the
476 // prerendered page. The main expectation is that this navigation commits
Ho Cheung13d432c22023-03-28 08:39:42477 // successfully and doesn't hit any CHECKs.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14478 contents()->ActivatePrerenderedPage(kPrerenderingUrl);
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31479 ExpectFinalStatus(PrerenderFinalStatus::kActivated);
Alexander Timin46fb6eec2021-06-04 15:46:30480
481 // Ensure that the the page_state was preserved.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14482 EXPECT_EQ(contents()->GetPrimaryMainFrame(), prerender_rfh);
Alexander Timin46fb6eec2021-06-04 15:46:30483 NavigationEntryImpl* activated_nav_entry =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14484 contents()->GetController().GetLastCommittedEntry();
Carlos Caballero15caeeb2021-10-27 09:57:55485 EXPECT_EQ(page_state,
486 activated_nav_entry
Hiroki Nakagawae72cf55c2022-11-30 05:58:14487 ->GetFrameEntry(contents()->GetPrimaryFrameTree().root())
Carlos Caballero15caeeb2021-10-27 09:57:55488 ->page_state());
Alexander Timin46fb6eec2021-06-04 15:46:30489}
490
Sreeja Kamishetty0be3b1b2021-08-12 17:04:15491// Test that WebContentsObserver::LoadProgressChanged is not invoked when the
492// page gets loaded while prerendering but is invoked on prerender activation.
493// Check that in case the load is incomplete with load progress
494// `kPartialLoadProgress`, we would see
495// LoadProgressChanged(kPartialLoadProgress) called on activation.
496TEST_F(PrerenderHostTest, LoadProgressChangedInvokedOnActivation) {
Hiroki Nakagawae72cf55c2022-11-30 05:58:14497 contents()->set_minimum_delay_between_loading_updates_for_testing(
Peter Kastinge5a38ed2021-10-02 03:06:35498 base::Milliseconds(0));
Sreeja Kamishetty0be3b1b2021-08-12 17:04:15499
500 // Initialize a MockWebContentsObserver and ensure that LoadProgressChanged is
501 // not invoked while prerendering.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14502 testing::NiceMock<MockWebContentsObserver> observer(contents());
Sreeja Kamishetty0be3b1b2021-08-12 17:04:15503 testing::InSequence s;
504 EXPECT_CALL(observer, LoadProgressChanged(testing::_)).Times(0);
505
506 // Start prerendering a page and commit prerender navigation.
507 const GURL kPrerenderingUrl("https://example.com/next");
508 constexpr double kPartialLoadProgress = 0.7;
509 RenderFrameHostImpl* prerender_rfh =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14510 contents()->AddPrerenderAndCommitNavigation(kPrerenderingUrl);
Sreeja Kamishetty0be3b1b2021-08-12 17:04:15511 FrameTreeNode* ftn = prerender_rfh->frame_tree_node();
512 EXPECT_FALSE(ftn->HasNavigation());
513
514 // Verify and clear all expectations on the mock observer before setting new
515 // ones.
516 testing::Mock::VerifyAndClearExpectations(&observer);
517
518 // Activate the prerendered page. This should result in invoking
519 // LoadProgressChanged for the following cases:
520 {
521 // 1) During DidStartLoading LoadProgressChanged is invoked with
522 // kInitialLoadProgress value.
523 EXPECT_CALL(observer, LoadProgressChanged(blink::kInitialLoadProgress));
524
525 // Verify that DidFinishNavigation is invoked before final load progress
526 // notification.
527 EXPECT_CALL(observer, DidFinishNavigation(testing::_));
528
529 // 2) After DidCommitNavigationInternal on activation with
530 // LoadProgressChanged is invoked with kPartialLoadProgress value.
531 EXPECT_CALL(observer, LoadProgressChanged(kPartialLoadProgress));
532
533 // 3) During DidStopLoading LoadProgressChanged is invoked with
534 // kFinalLoadProgress.
535 EXPECT_CALL(observer, LoadProgressChanged(blink::kFinalLoadProgress));
536 }
537
538 // Set load_progress value to kPartialLoadProgress in prerendering state,
539 // this should result in invoking LoadProgressChanged(kPartialLoadProgress) on
540 // activation.
541 prerender_rfh->GetPage().set_load_progress(kPartialLoadProgress);
542
543 // Perform a navigation in the primary frame tree which activates the
544 // prerendered page.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14545 contents()->ActivatePrerenderedPage(kPrerenderingUrl);
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31546 ExpectFinalStatus(PrerenderFinalStatus::kActivated);
Sreeja Kamishetty0be3b1b2021-08-12 17:04:15547}
548
Asami Doi7e33f2f2022-12-12 16:15:09549TEST_F(PrerenderHostTest, DontCancelPrerenderWhenTriggerGetsHidden) {
Robert Lin1ae40562021-09-17 02:48:01550 const GURL kPrerenderingUrl = GURL("https://example.com/empty.html");
Avi Drissmandcc8e682024-09-04 14:14:48551 const FrameTreeNodeId prerender_frame_tree_node_id =
552 registry().CreateAndStartHost(
553 GeneratePrerenderAttributes(kPrerenderingUrl));
Robert Lin1ae40562021-09-17 02:48:01554 PrerenderHost* prerender_host =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14555 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
Robert Lin1ae40562021-09-17 02:48:01556 ASSERT_NE(prerender_host, nullptr);
557 CommitPrerenderNavigation(*prerender_host);
558
Asami Doi7e33f2f2022-12-12 16:15:09559 // Changing the visibility state to HIDDEN will not stop prerendering.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14560 contents()->WasHidden();
Asami Doi7e33f2f2022-12-12 16:15:09561
562 // Activation from the foreground page should succeed.
563 contents()->WasShown();
564 contents()->ActivatePrerenderedPage(kPrerenderingUrl);
565 ExpectFinalStatus(PrerenderFinalStatus::kActivated);
566}
567
568TEST_F(PrerenderHostTest, CancelActivationFromHiddenPage) {
569 const GURL kPrerenderingUrl = GURL("https://example.com/empty.html");
Avi Drissmandcc8e682024-09-04 14:14:48570 const FrameTreeNodeId prerender_frame_tree_node_id =
571 registry().CreateAndStartHost(
572 GeneratePrerenderAttributes(kPrerenderingUrl));
Asami Doi7e33f2f2022-12-12 16:15:09573 PrerenderHost* prerender_host =
574 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
575 ASSERT_NE(prerender_host, nullptr);
576 CommitPrerenderNavigation(*prerender_host);
577
578 // Changing the visibility state to HIDDEN will not stop prerendering.
579 contents()->WasHidden();
580
581 // Activation from the background page should fail.
582 test::PrerenderHostObserver prerender_host_observer(
583 *contents(), prerender_frame_tree_node_id);
584 std::unique_ptr<NavigationSimulatorImpl> navigation =
585 NavigationSimulatorImpl::CreateRendererInitiated(
586 kPrerenderingUrl, contents()->GetPrimaryMainFrame());
587 navigation->SetReferrer(blink::mojom::Referrer::New(
588 contents()->GetPrimaryMainFrame()->GetLastCommittedURL(),
589 network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin));
590 navigation->Commit();
591 prerender_host_observer.WaitForDestroyed();
592
593 EXPECT_FALSE(prerender_host_observer.was_activated());
594 ExpectFinalStatus(PrerenderFinalStatus::kActivatedInBackground);
Robert Lin1ae40562021-09-17 02:48:01595}
596
597TEST_F(PrerenderHostTest, DontCancelPrerenderWhenTriggerGetsVisible) {
Robert Lin1ae40562021-09-17 02:48:01598 const GURL kPrerenderingUrl = GURL("https://example.com/empty.html");
Avi Drissmandcc8e682024-09-04 14:14:48599 const FrameTreeNodeId prerender_frame_tree_node_id =
600 registry().CreateAndStartHost(
601 GeneratePrerenderAttributes(kPrerenderingUrl));
Robert Lin1ae40562021-09-17 02:48:01602 PrerenderHost* prerender_host =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14603 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
Robert Lin1ae40562021-09-17 02:48:01604 ASSERT_NE(prerender_host, nullptr);
605 CommitPrerenderNavigation(*prerender_host);
606
Asami Doid1baa4422022-10-04 09:04:07607 // Changing the visibility state to VISIBLE will not stop prerendering.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14608 contents()->WasShown();
609 contents()->ActivatePrerenderedPage(kPrerenderingUrl);
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31610 ExpectFinalStatus(PrerenderFinalStatus::kActivated);
Robert Lin1ae40562021-09-17 02:48:01611}
612
613// Skip this test on Android as it doesn't support the OCCLUDED state.
Xiaohan Wang1e4ebde2022-01-15 17:29:12614#if !BUILDFLAG(IS_ANDROID)
Robert Lin1ae40562021-09-17 02:48:01615TEST_F(PrerenderHostTest, DontCancelPrerenderWhenTriggerGetsOcculded) {
Robert Lin1ae40562021-09-17 02:48:01616 const GURL kPrerenderingUrl = GURL("https://example.com/empty.html");
Avi Drissmandcc8e682024-09-04 14:14:48617 const FrameTreeNodeId prerender_frame_tree_node_id =
618 registry().CreateAndStartHost(
619 GeneratePrerenderAttributes(kPrerenderingUrl));
Robert Lin1ae40562021-09-17 02:48:01620 PrerenderHost* prerender_host =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14621 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
Robert Lin1ae40562021-09-17 02:48:01622 ASSERT_NE(prerender_host, nullptr);
623 CommitPrerenderNavigation(*prerender_host);
624
Asami Doid1baa4422022-10-04 09:04:07625 // Changing the visibility state to OCCLUDED will not stop prerendering.
Hiroki Nakagawae72cf55c2022-11-30 05:58:14626 contents()->WasOccluded();
627 contents()->ActivatePrerenderedPage(kPrerenderingUrl);
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31628 ExpectFinalStatus(PrerenderFinalStatus::kActivated);
Robert Lin1ae40562021-09-17 02:48:01629}
630#endif
631
Lingqi Chi21d9feb2022-02-02 09:42:18632TEST_F(PrerenderHostTest, UrlMatchPredicate) {
Lingqi Chi21d9feb2022-02-02 09:42:18633 const GURL kPrerenderingUrl = GURL("https://example.com/empty.html");
Liviu Tintacf066d52024-07-24 16:00:55634 base::RepeatingCallback callback = base::BindRepeating(
635 [](const GURL&, const std::optional<content::UrlMatchType>&) {
636 return true;
637 });
Avi Drissmandcc8e682024-09-04 14:14:48638 const FrameTreeNodeId prerender_frame_tree_node_id =
639 registry().CreateAndStartHost(
640 GeneratePrerenderAttributesWithPredicate(kPrerenderingUrl, callback));
Lingqi Chi21d9feb2022-02-02 09:42:18641 PrerenderHost* prerender_host =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14642 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
Lingqi Chi21d9feb2022-02-02 09:42:18643 ASSERT_NE(prerender_host, nullptr);
644 const GURL kActivatedUrl = GURL("https://example.com/empty.html?activate");
645 ASSERT_NE(kActivatedUrl, kPrerenderingUrl);
646 EXPECT_TRUE(prerender_host->IsUrlMatch(kActivatedUrl));
647 // Even if the predicate always returns true, a cross-origin url shouldn't be
648 // able to activate a prerendered page.
649 EXPECT_FALSE(
650 prerender_host->IsUrlMatch(GURL("https://example2.com/empty.html")));
651}
652
Lingqi Chi8098c5e92022-09-21 13:24:50653// Regression test for https://crbug.com/1366046: This test will crash if
654// PrerenderHost is set to "ready_for_activation" after getting canceled.
655TEST_F(PrerenderHostTest, CanceledPrerenderCannotBeReadyForActivation) {
Lingqi Chi8098c5e92022-09-21 13:24:50656 const GURL kPrerenderingUrl = GURL("https://example.com/empty.html");
Lingqi Chi8098c5e92022-09-21 13:24:50657
Hiroki Nakagawae72cf55c2022-11-30 05:58:14658 auto* preloading_data = PreloadingData::GetOrCreateForWebContents(contents());
Lingqi Chi8098c5e92022-09-21 13:24:50659
660 // Create new PreloadingAttempt and pass all the values corresponding to
661 // this prerendering attempt.
662 PreloadingURLMatchCallback same_url_matcher =
663 PreloadingData::GetSameURLMatcher(kPrerenderingUrl);
664 PreloadingAttempt* preloading_attempt = preloading_data->AddPreloadingAttempt(
William Liu238a9e52023-01-23 20:32:40665 content_preloading_predictor::kSpeculationRules,
Hiroki Nakagawaf93776b2023-09-12 22:04:27666 PreloadingType::kPrerender, std::move(same_url_matcher),
667 contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
Lingqi Chi8098c5e92022-09-21 13:24:50668
Avi Drissmandcc8e682024-09-04 14:14:48669 const FrameTreeNodeId prerender_frame_tree_node_id =
670 registry().CreateAndStartHost(
671 GeneratePrerenderAttributes(kPrerenderingUrl), preloading_attempt);
Lingqi Chi8098c5e92022-09-21 13:24:50672 PrerenderHost* prerender_host =
Hiroki Nakagawae72cf55c2022-11-30 05:58:14673 registry().FindNonReservedHostById(prerender_frame_tree_node_id);
Lingqi Chi8098c5e92022-09-21 13:24:50674 ASSERT_NE(prerender_host, nullptr);
675
Lingqi Chi8e192ba2022-11-22 04:34:25676 // Registry keeps alive through this test, so it is safe to capture the
677 // reference to `registry`.
Sean Maher52fa5a72022-11-14 15:53:25678 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
Lingqi Chi8e192ba2022-11-22 04:34:25679 FROM_HERE, base::BindOnce(base::BindLambdaForTesting([&]() {
Hiroki Nakagawae72cf55c2022-11-30 05:58:14680 registry().CancelHost(prerender_frame_tree_node_id,
681 PrerenderFinalStatus::kTriggerDestroyed);
Lingqi Chi8e192ba2022-11-22 04:34:25682 })));
Lingqi Chi8098c5e92022-09-21 13:24:50683
684 // For some reasons triggers want to set the failure reason by themselves,
685 // this would happen together with cancelling prerender.
Sean Maher52fa5a72022-11-14 15:53:25686 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
Lingqi Chi8098c5e92022-09-21 13:24:50687 FROM_HERE,
688 base::BindOnce(
689 &PreloadingAttempt::SetFailureReason,
690 base::Unretained(preloading_attempt),
691 static_cast<PreloadingFailureReason>(
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31692 static_cast<int>(PrerenderFinalStatus::kTriggerDestroyed) +
Lingqi Chi8098c5e92022-09-21 13:24:50693 static_cast<int>(PreloadingFailureReason::
694 kPreloadingFailureReasonCommonEnd))));
695
696 base::RunLoop run_loop;
Sean Maher52fa5a72022-11-14 15:53:25697 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
Lingqi Chi8098c5e92022-09-21 13:24:50698 FROM_HERE, base::BindLambdaForTesting([&]() {
699 CommitPrerenderNavigation(*prerender_host,
700 ExpectedReadyForActivationState(false));
701 run_loop.Quit();
702 }));
703
704 // Wait for the completion of CommitPrerenderNavigation() above.
705 run_loop.Run();
706
Sreeja Kamishetty26e40d22022-10-17 17:54:35707 EXPECT_EQ(test::PreloadingAttemptAccessor(preloading_attempt)
708 .GetTriggeringOutcome(),
Lingqi Chi8098c5e92022-09-21 13:24:50709 PreloadingTriggeringOutcome::kFailure);
710}
711
Miina Koyama6f3bbc72023-09-08 08:13:02712TEST(AreHttpRequestHeadersCompatible, IgnoreRTT) {
Miina Koyamad0e41f32023-11-13 06:50:09713 PrerenderCancellationReason reason = PrerenderCancellationReason(
714 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
Miina Koyama6f3bbc72023-09-08 08:13:02715 const std::string prerender_headers = "rtt: 1 \r\n downlink: 3";
716 const std::string potential_activation_headers = "rtt: 2 \r\n downlink: 4";
Doug Turner7ce58cf2025-08-04 22:03:25717#if BUILDFLAG(IS_ANDROID)
718 net::HttpRequestHeaders potential_activation_additional_headers;
719#endif
720
Miina Koyama6f3bbc72023-09-08 08:13:02721 EXPECT_TRUE(PrerenderHost::AreHttpRequestHeadersCompatible(
Hiroki Nakagawa8348bc42024-12-13 16:24:42722 potential_activation_headers,
723#if BUILDFLAG(IS_ANDROID)
Doug Turner7ce58cf2025-08-04 22:03:25724 potential_activation_additional_headers,
Hiroki Nakagawa8348bc42024-12-13 16:24:42725#endif // BUILDFLAG(IS_ANDROID)
726 prerender_headers, PreloadingTriggerType::kSpeculationRule,
Hiroki Nakagawaaadc3292025-04-03 00:46:32727 /*embedder_histogram_suffix=*/"", /*allow_x_header_mismatch=*/false,
728 reason));
729}
730
731TEST(AreHttpRequestHeadersCompatible, XHeaders) {
732 PrerenderCancellationReason reason = PrerenderCancellationReason(
733 PrerenderFinalStatus::kActivationNavigationParameterMismatch);
734 const std::string prerender_headers = "x-hello: 1";
735 const std::string potential_activation_headers = "X-world: 2";
Doug Turner7ce58cf2025-08-04 22:03:25736#if BUILDFLAG(IS_ANDROID)
737 net::HttpRequestHeaders potential_activation_additional_headers;
738#endif
Hiroki Nakagawaaadc3292025-04-03 00:46:32739
740 EXPECT_FALSE(PrerenderHost::AreHttpRequestHeadersCompatible(
741 potential_activation_headers,
742#if BUILDFLAG(IS_ANDROID)
Doug Turner7ce58cf2025-08-04 22:03:25743 potential_activation_additional_headers,
Hiroki Nakagawaaadc3292025-04-03 00:46:32744#endif // BUILDFLAG(IS_ANDROID)
745 prerender_headers, PreloadingTriggerType::kSpeculationRule,
746 /*embedder_histogram_suffix=*/"", /*allow_x_header_mismatch=*/false,
747 reason));
748
749 EXPECT_TRUE(PrerenderHost::AreHttpRequestHeadersCompatible(
750 potential_activation_headers,
751#if BUILDFLAG(IS_ANDROID)
Doug Turner7ce58cf2025-08-04 22:03:25752 potential_activation_additional_headers,
Hiroki Nakagawaaadc3292025-04-03 00:46:32753#endif // BUILDFLAG(IS_ANDROID)
754 prerender_headers, PreloadingTriggerType::kSpeculationRule,
755 /*embedder_histogram_suffix=*/"", /*allow_x_header_mismatch=*/true,
756 reason));
Miina Koyama6f3bbc72023-09-08 08:13:02757}
758
Hiroki Nakagawa968139e22020-10-22 15:03:56759} // namespace
760} // namespace content