blob: 39a8cabd11c25e72178efa4c62a8c634c0bc716e [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2022 The Chromium Authors
Max Curran6c2835ea2022-03-07 19:52:382// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Sreeja Kamishettyf66553a2022-07-14 17:41:275#include "content/browser/preloading/prefetch/prefetch_service.h"
Max Curran146bf442022-03-28 23:22:146
Peter Kasting1557e5f2025-01-28 01:14:087#include <algorithm>
Wayne Jackson Jr.88f3db62025-01-07 12:53:348#include <memory>
9#include <optional>
Md Hasibul Hasana963a9342024-04-03 10:15:1410#include <string_view>
Andrew Rayskiydae52e92024-03-05 17:53:3611#include <vector>
12
Taiyo Mizuhashi08c47d12025-03-05 23:46:0713#include "base/functional/callback_helpers.h"
Wayne Jackson Jr.88f3db62025-01-07 12:53:3414#include "base/memory/weak_ptr.h"
Max Curranaefeb772023-08-01 18:03:1015#include "base/notreached.h"
Max Curran146bf442022-03-28 23:22:1416#include "base/run_loop.h"
Lei Zhang0a85e65a2025-05-23 19:22:0617#include "base/strings/string_number_conversions.h"
Liviu Tintaf7d62a72023-08-29 21:48:3918#include "base/strings/string_util.h"
Lei Zhangab09dd842025-05-17 08:06:4819#include "base/strings/stringprintf.h"
Liviu Tinta1fe1a6d2023-09-20 19:44:0420#include "base/test/bind.h"
Adithya Srinivasand1c68ba2023-05-30 19:54:2521#include "base/test/mock_callback.h"
Max Curran6c2835ea2022-03-07 19:52:3822#include "base/test/scoped_feature_list.h"
Hiroshige Hayashizaki6e9a1892023-04-17 06:47:3823#include "base/test/test_future.h"
William Liu60e005f2023-01-18 16:17:2224#include "base/time/time.h"
25#include "base/timer/elapsed_timer.h"
Liviu Tinta2e1ffe22024-06-21 21:01:3326#include "components/variations/scoped_variations_ids_provider.h"
Wayne Jackson Jr.88f3db62025-01-07 12:53:3427#include "content/browser/browser_context_impl.h"
Jeremy Romane561b412024-02-15 18:31:3428#include "content/browser/preloading/prefetch/mock_prefetch_service_delegate.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2729#include "content/browser/preloading/prefetch/prefetch_container.h"
30#include "content/browser/preloading/prefetch/prefetch_document_manager.h"
31#include "content/browser/preloading/prefetch/prefetch_features.h"
Liviu Tinta4eaa53c22023-08-02 21:01:0632#include "content/browser/preloading/prefetch/prefetch_match_resolver.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2733#include "content/browser/preloading/prefetch/prefetch_params.h"
kenoss1b8ebc22025-04-04 00:11:3534#include "content/browser/preloading/prefetch/prefetch_scheduler.h"
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:0835#include "content/browser/preloading/prefetch/prefetch_servable_state.h"
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:5736#include "content/browser/preloading/prefetch/prefetch_serving_handle.h"
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:1137#include "content/browser/preloading/prefetch/prefetch_serving_page_metrics_container.h"
Taiyo Mizuhashi08c47d12025-03-05 23:46:0738#include "content/browser/preloading/prefetch/prefetch_streaming_url_loader.h"
Hiroshige Hayashizakib2a4d782025-01-15 11:25:1539#include "content/browser/preloading/prefetch/prefetch_test_util_internal.h"
Taiyo Mizuhashi6b0a4952024-03-18 22:40:3740#include "content/browser/preloading/prefetch/prefetch_type.h"
William Liu77089052022-12-15 18:53:3541#include "content/browser/preloading/preloading.h"
Liviu Tinta91202302023-09-26 17:49:1142#include "content/browser/preloading/preloading_attempt_impl.h"
Dave Tapuskaab31ccf2023-04-12 19:27:2743#include "content/browser/preloading/preloading_config.h"
William Liu77089052022-12-15 18:53:3544#include "content/browser/preloading/preloading_data_impl.h"
kenossb68c9e82024-10-10 10:11:1545#include "content/browser/preloading/prerender/prerender_features.h"
HuanPo Lin740620b2025-03-21 12:37:2646#include "content/browser/preloading/speculation_rules/speculation_rules_tags.h"
kenossf6fbd5652024-09-04 00:40:2847#include "content/browser/renderer_host/frame_tree_node.h"
Hiroshige Hayashizaki2df45292023-10-10 22:59:0348#include "content/browser/renderer_host/render_frame_host_impl.h"
Arthur Sonzognibdeca8e2023-09-11 08:32:1249#include "content/common/features.h"
Max Curran146bf442022-03-28 23:22:1450#include "content/public/browser/browser_context.h"
Max Curran3df677bf2022-09-12 19:47:0751#include "content/public/browser/frame_accept_header.h"
Wayne Jackson Jr.88f3db62025-01-07 12:53:3452#include "content/public/browser/prefetch_request_status_listener.h"
Max Curranead64a62022-06-22 01:10:5253#include "content/public/browser/prefetch_service_delegate.h"
kenossc3f9a2c2025-03-06 15:35:5154#include "content/public/browser/preload_pipeline_info.h"
William Liu77089052022-12-15 18:53:3555#include "content/public/browser/preloading.h"
Max Curran146bf442022-03-28 23:22:1456#include "content/public/browser/storage_partition.h"
Max Curranead64a62022-06-22 01:10:5257#include "content/public/common/content_client.h"
Simon Pelchatefb7e7f2023-04-04 01:20:4858#include "content/public/common/content_features.h"
Max Curran146bf442022-03-28 23:22:1459#include "content/public/test/fake_service_worker_context.h"
Jeremy Roman586b5ec2024-02-13 15:14:4060#include "content/public/test/mock_client_hints_controller_delegate.h"
Max Curranc4445fc2022-06-02 18:43:4361#include "content/public/test/mock_navigation_handle.h"
Kevin McNee06824c72024-02-06 18:59:5262#include "content/public/test/navigation_simulator.h"
William Liu77089052022-12-15 18:53:3563#include "content/public/test/preloading_test_util.h"
64#include "content/public/test/test_browser_context.h"
Jeremy Romanbae6a422022-05-17 22:41:1765#include "content/test/test_content_browser_client.h"
Dmitrii Kuragin8045a832022-07-18 17:44:3266#include "net/base/load_flags.h"
Andrew Williams8a9e2422023-11-06 20:32:5767#include "net/base/proxy_chain.h"
Max Curran6b93426f2022-05-03 00:55:5268#include "net/base/proxy_server.h"
Jeremy Roman4c9524212023-07-27 22:01:0569#include "net/base/request_priority.h"
Wayne Jackson Jr.88f3db62025-01-07 12:53:3470#include "net/http/http_no_vary_search_data.h"
71#include "net/http/http_request_headers.h"
Liviu Tinta91202302023-09-26 17:49:1172#include "services/metrics/public/cpp/metrics_utils.h"
Liviu Tintad97a6a32022-12-08 23:28:4073#include "services/network/public/cpp/parsed_headers.h"
Max Curran18a6f2b2022-05-02 23:13:2474#include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
Max Curran146bf442022-03-28 23:22:1475#include "services/network/public/mojom/cookie_manager.mojom.h"
76#include "services/network/public/mojom/network_context.mojom.h"
Max Currancc1ab0c2022-09-12 22:03:1177#include "services/network/public/mojom/proxy_lookup_client.mojom.h"
78#include "services/network/test/test_network_context.h"
Max Curran18a6f2b2022-05-02 23:13:2479#include "services/network/test/test_url_loader_factory.h"
80#include "services/network/test/test_utils.h"
Max Curranead64a62022-06-22 01:10:5281#include "testing/gmock/include/gmock/gmock.h"
Max Curran6c2835ea2022-03-07 19:52:3882#include "testing/gtest/include/gtest/gtest.h"
David Risneya1dd2bf2025-03-06 03:40:4283#include "third_party/blink/public/common/navigation/preloading_headers.h"
Jeremy Romanbae6a422022-05-17 22:41:1784#include "third_party/blink/public/common/web_preferences/web_preferences.h"
Max Curranc4445fc2022-06-02 18:43:4385#include "url/gurl.h"
Max Curran6c2835ea2022-03-07 19:52:3886
87namespace content {
88namespace {
89
kenossda6656b2024-07-23 02:18:4890#if BUILDFLAG(IS_CHROMEOS)
91#define DISABLED_CHROMEOS(x) DISABLED_##x
92#else
93#define DISABLED_CHROMEOS(x) x
94#endif
95
96#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_CASTOS)
97#define DISABLED_CHROMEOS_AND_CASTOS(x) DISABLED_##x
98#else
99#define DISABLED_CHROMEOS_AND_CASTOS(x) x
100#endif
101
Taiyo Mizuhashi98ecb382025-06-03 15:24:55102// Represents the duration between prefetch is added and its URLRequest is
103// started (`URLResponseHead.LoadTimingInfo.request_start`).
104constexpr int kAddedToURLRequestStartLatency = 123;
105// Represents the duration between the URLRequest is started
106// (`URLResponseHead.LoadTimingInfo.request_start`) and the header is received
107// (`URLResponseHead.LoadTimingInfo.receive_headers_end`).
108constexpr int kHeaderLatency = 456;
Max Curran210cffa2022-09-06 22:24:31109
Taiyo Mizuhashi98ecb382025-06-03 15:24:55110// TODO(taiyo): Convert const to constexper.
Max Curran18a6f2b2022-05-02 23:13:24111const char kHTMLMimeType[] = "text/html";
112
113const char kHTMLBody[] = R"(
114 <!DOCTYPE HTML>
115 <html>
116 <head></head>
117 <body></body>
118 </html>)";
119
Wayne Jackson Jr.88f3db62025-01-07 12:53:34120const char kHTMLBodyServerError[] = R"(
121 <!DOCTYPE HTML>
122<html lang="en">
123<head>
124 <title>500 Internal Server Error</title>
125</head>
126<body>
127</body>
128</html>
129)";
130
kenossbd9a3fc2025-03-28 05:15:57131// Param for parametrized tests for rearchitecturing/refactoring of
132// `PrefetchService`.
133//
134// Do not remove and keep it even if there is no param to make it easy to add
135// another param in the future.
136struct PrefetchServiceRearchParam {
137 public:
138 using Arg = int;
139
140 static std::vector<PrefetchServiceRearchParam::Arg> Params();
141 static PrefetchServiceRearchParam CreateFromIndex(int index);
kenoss1b8ebc22025-04-04 00:11:35142
143 bool prefetch_scheduler;
kenoss0a567362025-05-16 11:18:55144 bool prefetch_scheduler_progress_sync_best_effort;
kenossbd9a3fc2025-03-28 05:15:57145};
146
147// static
148std::vector<int> PrefetchServiceRearchParam::Params() {
kenoss0a567362025-05-16 11:18:55149 return {0, 1, 2};
kenossbd9a3fc2025-03-28 05:15:57150}
151
152// static
153PrefetchServiceRearchParam PrefetchServiceRearchParam::CreateFromIndex(
154 int index) {
155 std::vector<PrefetchServiceRearchParam> params = {
kenoss1b8ebc22025-04-04 00:11:35156 PrefetchServiceRearchParam{
157 .prefetch_scheduler = false,
kenoss0a567362025-05-16 11:18:55158 .prefetch_scheduler_progress_sync_best_effort = false,
kenoss1b8ebc22025-04-04 00:11:35159 },
160 PrefetchServiceRearchParam{
161 .prefetch_scheduler = true,
kenoss0a567362025-05-16 11:18:55162 .prefetch_scheduler_progress_sync_best_effort = false,
163 },
164 PrefetchServiceRearchParam{
165 .prefetch_scheduler = true,
166 .prefetch_scheduler_progress_sync_best_effort = true,
kenoss1b8ebc22025-04-04 00:11:35167 },
kenossbd9a3fc2025-03-28 05:15:57168 };
169 return params[index];
170}
171
172class WithPrefetchServiceRearchParam {
173 public:
174 explicit WithPrefetchServiceRearchParam(int index)
175 : param_(PrefetchServiceRearchParam::CreateFromIndex(index)) {}
176 virtual ~WithPrefetchServiceRearchParam() = default;
177
178 void InitRearchFeatures();
179
180 const PrefetchServiceRearchParam& rearch_param() { return param_; }
181
182 private:
183 PrefetchServiceRearchParam param_;
kenoss1b8ebc22025-04-04 00:11:35184 base::test::ScopedFeatureList feature_list_prefetch_scheduler_;
kenossbd9a3fc2025-03-28 05:15:57185};
186
kenoss1b8ebc22025-04-04 00:11:35187void WithPrefetchServiceRearchParam::InitRearchFeatures() {
188 if (param_.prefetch_scheduler) {
189 feature_list_prefetch_scheduler_.InitWithFeaturesAndParameters(
kenoss0a567362025-05-16 11:18:55190 {{
191 features::kPrefetchScheduler,
192 {
193 {"kPrefetchSchedulerProgressSyncBestEffort",
194 param_.prefetch_scheduler_progress_sync_best_effort ? "true"
195 : "false"},
196 },
197 }},
198 {});
kenoss1b8ebc22025-04-04 00:11:35199 }
200}
kenossbd9a3fc2025-03-28 05:15:57201
Max Curranead64a62022-06-22 01:10:52202class ScopedPrefetchServiceContentBrowserClient
203 : public TestContentBrowserClient {
204 public:
205 explicit ScopedPrefetchServiceContentBrowserClient(
206 std::unique_ptr<MockPrefetchServiceDelegate>
207 mock_prefetch_service_delegate)
208 : mock_prefetch_service_delegate_(
209 std::move(mock_prefetch_service_delegate)) {
210 old_browser_client_ = SetBrowserClientForTesting(this);
William Liu77089052022-12-15 18:53:35211 off_the_record_context_ = std::make_unique<TestBrowserContext>();
212 off_the_record_context_->set_is_off_the_record(true);
Max Curranead64a62022-06-22 01:10:52213 }
214
215 ~ScopedPrefetchServiceContentBrowserClient() override {
216 EXPECT_EQ(this, SetBrowserClientForTesting(old_browser_client_));
217 }
218
Max Curranead64a62022-06-22 01:10:52219 // ContentBrowserClient.
220 std::unique_ptr<PrefetchServiceDelegate> CreatePrefetchServiceDelegate(
Johanna9fe85f2023-01-17 10:15:43221 BrowserContext*) override {
Max Curranead64a62022-06-22 01:10:52222 return std::move(mock_prefetch_service_delegate_);
223 }
224
William Liu77089052022-12-15 18:53:35225 void UseOffTheRecordContextForStoragePartition(bool use) {
226 use_off_the_record_context_for_storage_paritition_ = use;
227 }
228 // `BrowserContext::GetStoragePartitionForUrl` eventually calls this method
229 // on the browser client to get the config. Overwrite it so the prefetch can
230 // be rejected due to a non-default storage partition.
231 StoragePartitionConfig GetStoragePartitionConfigForSite(
232 BrowserContext* browser_context,
233 const GURL& site) override {
234 if (use_off_the_record_context_for_storage_paritition_) {
235 return StoragePartitionConfig::CreateDefault(
236 off_the_record_context_.get());
237 }
238 return TestContentBrowserClient::GetStoragePartitionConfigForSite(
239 browser_context, site);
240 }
241
Max Curranead64a62022-06-22 01:10:52242 private:
243 raw_ptr<ContentBrowserClient> old_browser_client_;
244 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate_;
William Liu77089052022-12-15 18:53:35245 // This browser context is used to generate a different storage partition if
246 // `use_off_the_record_context_for_storage_paritition_` is set to true.
247 std::unique_ptr<TestBrowserContext> off_the_record_context_;
248 bool use_off_the_record_context_for_storage_paritition_{false};
Max Curranead64a62022-06-22 01:10:52249};
250
Max Currancc1ab0c2022-09-12 22:03:11251// This is only used to test the proxy lookup.
252class TestNetworkContext : public network::TestNetworkContext {
253 public:
Arthur Sonzognic686e8f2024-01-11 08:36:37254 explicit TestNetworkContext(std::optional<net::ProxyInfo> proxy_info)
Max Currancc1ab0c2022-09-12 22:03:11255 : proxy_info_(proxy_info) {}
256
Brianna Goldsteind22b0642022-10-11 16:30:50257 void LookUpProxyForURL(
258 const GURL& url,
Brianna Goldstein581e2af82022-10-20 13:56:30259 const net::NetworkAnonymizationKey& network_anonymization_key,
Brianna Goldsteind22b0642022-10-11 16:30:50260 mojo::PendingRemote<network::mojom::ProxyLookupClient>
261 pending_proxy_lookup_client) override {
Max Currancc1ab0c2022-09-12 22:03:11262 mojo::Remote<network::mojom::ProxyLookupClient> proxy_lookup_client(
263 std::move(pending_proxy_lookup_client));
264 proxy_lookup_client->OnProxyLookupComplete(net::OK, proxy_info_);
265 }
266
267 private:
Arthur Sonzognic686e8f2024-01-11 08:36:37268 std::optional<net::ProxyInfo> proxy_info_;
Max Currancc1ab0c2022-09-12 22:03:11269};
270
Jeremy Roman4c9524212023-07-27 22:01:05271net::RequestPriority ExpectedPriorityForEagerness(
272 blink::mojom::SpeculationEagerness eagerness) {
273 switch (eagerness) {
274 case blink::mojom::SpeculationEagerness::kConservative:
275 return net::RequestPriority::MEDIUM;
276 case blink::mojom::SpeculationEagerness::kModerate:
277 return net::RequestPriority::LOW;
278 default:
279 return net::RequestPriority::IDLE;
280 }
281}
282
Liviu Tintaf7d62a72023-08-29 21:48:39283class PrefetchFakeServiceWorkerContext : public FakeServiceWorkerContext {
284 public:
Liviu Tinta91202302023-09-26 17:49:11285 explicit PrefetchFakeServiceWorkerContext(
286 BrowserTaskEnvironment& task_environment)
287 : task_environment_(task_environment) {}
Liviu Tintaf7d62a72023-08-29 21:48:39288
289 PrefetchFakeServiceWorkerContext(const PrefetchFakeServiceWorkerContext&) =
290 delete;
291 PrefetchFakeServiceWorkerContext& operator=(
292 const PrefetchFakeServiceWorkerContext&) = delete;
293
294 ~PrefetchFakeServiceWorkerContext() override = default;
295
296 void CheckHasServiceWorker(const GURL& url,
297 const blink::StorageKey& key,
298 CheckHasServiceWorkerCallback callback) override {
Liviu Tinta91202302023-09-26 17:49:11299 if (long_service_worker_check_duration_.is_positive()) {
300 task_environment()->FastForwardBy(long_service_worker_check_duration_);
301 }
Liviu Tintaf7d62a72023-08-29 21:48:39302 if (!MaybeHasRegistrationForStorageKey(key)) {
303 std::move(callback).Run(ServiceWorkerCapability::NO_SERVICE_WORKER);
304 return;
305 }
Peter Kasting1557e5f2025-01-28 01:14:08306 auto service_worker_info = std::ranges::find_if(
Liviu Tintaf7d62a72023-08-29 21:48:39307 service_worker_scopes_,
308 [url](const std::pair<GURL, ServiceWorkerCapability>&
309 service_worker_info) {
310 return base::StartsWith(url.spec(), service_worker_info.first.spec());
311 });
312 if (service_worker_info != service_worker_scopes_.end()) {
313 std::move(callback).Run(service_worker_info->second);
314 return;
315 }
316 std::move(callback).Run(ServiceWorkerCapability::NO_SERVICE_WORKER);
317 }
318
319 void AddServiceWorkerScope(const GURL& scope,
320 ServiceWorkerCapability capability) {
Liviu Tinta1fe1a6d2023-09-20 19:44:04321 ASSERT_NE(capability, ServiceWorkerCapability::NO_SERVICE_WORKER);
Liviu Tintaf7d62a72023-08-29 21:48:39322 service_worker_scopes_[scope] = capability;
323 }
324
Liviu Tinta91202302023-09-26 17:49:11325 void SetServiceWorkerCheckDuration(base::TimeDelta duration) {
326 long_service_worker_check_duration_ = duration;
327 }
328
Liviu Tintaf7d62a72023-08-29 21:48:39329 private:
Liviu Tinta91202302023-09-26 17:49:11330 BrowserTaskEnvironment* task_environment() {
331 return &task_environment_.get();
332 }
333
334 base::TimeDelta long_service_worker_check_duration_;
Liviu Tintaf7d62a72023-08-29 21:48:39335 std::map<GURL, ServiceWorkerCapability> service_worker_scopes_;
Liviu Tinta91202302023-09-26 17:49:11336 const base::raw_ref<BrowserTaskEnvironment> task_environment_;
Liviu Tintaf7d62a72023-08-29 21:48:39337};
338
kenossf6fbd5652024-09-04 00:40:28339struct NavigationResult {
340 std::unique_ptr<testing::NiceMock<MockNavigationHandle>> navigation_handle;
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57341 base::test::TestFuture<PrefetchServingHandle> serving_handle_future;
kenossf6fbd5652024-09-04 00:40:28342};
343
Wayne Jackson Jr.88f3db62025-01-07 12:53:34344// A `content::PrefetchRequestStatusListener` that tracks when the callbacks
345// have been called.
346class ProbePrefetchRequestStatusListener
347 : public content::PrefetchRequestStatusListener {
348 public:
349 ~ProbePrefetchRequestStatusListener() override = default;
350
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46351 void OnPrefetchStartFailedGeneric() override {
Wayne Jackson Jr.88f3db62025-01-07 12:53:34352 prefetch_start_failed_called_ = true;
353 }
354
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46355 void OnPrefetchStartFailedDuplicate() override {
356 prefetch_start_failed_duplicate_called_ = true;
357 }
358
Wayne Jackson Jr.88f3db62025-01-07 12:53:34359 void OnPrefetchResponseCompleted() override {
360 prefetch_response_completed_called_ = true;
361 }
362
363 void OnPrefetchResponseError() override {
364 prefetch_response_error_called_ = true;
365 }
366
367 void OnPrefetchResponseServerError(int response_code) override {
368 prefetch_response_server_error_called_ = true;
369 server_response_code_ = response_code;
370 }
371
372 bool GetPrefetchStartFailedCalled() { return prefetch_start_failed_called_; }
373
374 bool GetPrefetchResponseCompletedCalled() {
375 return prefetch_response_completed_called_;
376 }
377
378 bool GetPrefetchResponseErrorCalled() {
379 return prefetch_response_error_called_;
380 }
381
382 bool GetPrefetchResponseServerErrorCalled() {
383 return prefetch_response_server_error_called_;
384 }
385
386 base::WeakPtr<ProbePrefetchRequestStatusListener> GetWeakPtr() {
387 return weak_factory_.GetWeakPtr();
388 }
389
390 private:
391 bool prefetch_start_failed_called_ = false;
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46392 bool prefetch_start_failed_duplicate_called_ = false;
Wayne Jackson Jr.88f3db62025-01-07 12:53:34393 bool prefetch_response_completed_called_ = false;
394 bool prefetch_response_error_called_ = false;
395 bool prefetch_response_server_error_called_ = false;
396
397 std::optional<int> server_response_code_;
398
399 base::WeakPtrFactory<ProbePrefetchRequestStatusListener> weak_factory_{this};
400};
401
402// A |content::PrefetchRequestStatusListener| that forwards the callback
403// resolution to a `ProbePrefetchRequestStatusListener` for testability.
404//
405// The `ProbePrefetchRequestStatusListener` is needed because the
406// `TestablePrefetchRequestStatusListener` is eventually owned by the browser
407// context, whereas the `ProbePrefetchRequestStatusListener` is owned by the
408// test.
409class TestablePrefetchRequestStatusListener
410 : public content::PrefetchRequestStatusListener {
411 public:
412 explicit TestablePrefetchRequestStatusListener(
413 base::WeakPtr<ProbePrefetchRequestStatusListener> probe_listener)
414 : probe_listener_(probe_listener) {}
415
416 ~TestablePrefetchRequestStatusListener() override = default;
417
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46418 void OnPrefetchStartFailedGeneric() override {
419 probe_listener_->OnPrefetchStartFailedGeneric();
420 }
421
422 void OnPrefetchStartFailedDuplicate() override {
423 probe_listener_->OnPrefetchStartFailedDuplicate();
Wayne Jackson Jr.88f3db62025-01-07 12:53:34424 }
425
426 void OnPrefetchResponseCompleted() override {
427 probe_listener_->OnPrefetchResponseCompleted();
428 }
429
430 void OnPrefetchResponseError() override {
431 probe_listener_->OnPrefetchResponseError();
432 }
433
434 void OnPrefetchResponseServerError(int response_code) override {
435 probe_listener_->OnPrefetchResponseServerError(response_code);
436 }
437
438 private:
439 base::WeakPtr<ProbePrefetchRequestStatusListener> probe_listener_ = nullptr;
440};
441
Hiroshige Hayashizakib2a4d782025-01-15 11:25:15442class PrefetchServiceTestBase : public PrefetchingMetricsTestBase {
Max Curran146bf442022-03-28 23:22:14443 public:
Liviu Tinta91202302023-09-26 17:49:11444 const int kServiceWorkerCheckDuration = 1000;
kenoss972e8682024-09-05 00:20:14445 PrefetchServiceTestBase()
Hiroshige Hayashizakib2a4d782025-01-15 11:25:15446 : test_url_loader_factory_(/*observe_loader_requests=*/true),
Max Curran5d4da4b42023-03-10 23:41:46447 test_shared_url_loader_factory_(
Max Curran18a6f2b2022-05-02 23:13:24448 base::MakeRefCounted<network::WeakWrapperSharedURLLoaderFactory>(
Liviu Tinta91202302023-09-26 17:49:11449 &test_url_loader_factory_)) {
450 service_worker_context_ =
451 std::make_unique<PrefetchFakeServiceWorkerContext>(*task_environment());
452 }
Max Curran18a6f2b2022-05-02 23:13:24453
Max Curran146bf442022-03-28 23:22:14454 void SetUp() override {
Hiroshige Hayashizakib2a4d782025-01-15 11:25:15455 PrefetchingMetricsTestBase::SetUp();
Max Curran146bf442022-03-28 23:22:14456
457 browser_context()
458 ->GetDefaultStoragePartition()
459 ->GetNetworkContext()
460 ->GetCookieManager(cookie_manager_.BindNewPipeAndPassReceiver());
461
Max Curran6b93426f2022-05-03 00:55:52462 InitScopedFeatureList();
Max Curran18a6f2b2022-05-02 23:13:24463
464 PrefetchService::SetURLLoaderFactoryForTesting(
465 test_shared_url_loader_factory_.get());
Max Curran146bf442022-03-28 23:22:14466
467 PrefetchService::SetHostNonUniqueFilterForTesting(
Md Hasibul Hasana963a9342024-04-03 10:15:14468 [](std::string_view) { return false; });
Max Curran146bf442022-03-28 23:22:14469 PrefetchService::SetServiceWorkerContextForTesting(
Liviu Tinta91202302023-09-26 17:49:11470 service_worker_context_.get());
Max Curran146bf442022-03-28 23:22:14471 }
472
473 void TearDown() override {
Max Currane0444942022-09-06 23:55:23474 if (PrefetchDocumentManager::GetForCurrentDocument(main_rfh()))
475 PrefetchDocumentManager::DeleteForCurrentDocument(main_rfh());
476 PrefetchDocumentManager::SetPrefetchServiceForTesting(nullptr);
Max Curran210cffa2022-09-06 22:24:31477 mock_navigation_handle_.reset();
Max Curran18a6f2b2022-05-02 23:13:24478 PrefetchService::SetURLLoaderFactoryForTesting(nullptr);
Max Curran146bf442022-03-28 23:22:14479 PrefetchService::SetHostNonUniqueFilterForTesting(nullptr);
480 PrefetchService::SetServiceWorkerContextForTesting(nullptr);
Max Curran18a6f2b2022-05-02 23:13:24481 PrefetchService::SetURLLoaderFactoryForTesting(nullptr);
Max Curranead64a62022-06-22 01:10:52482 test_content_browser_client_.reset();
Hiroshige Hayashizakia580b332023-09-06 00:16:54483 request_handler_keep_alive_.clear();
Liviu Tinta91202302023-09-26 17:49:11484 service_worker_context_.reset();
Hiroshige Hayashizakib2a4d782025-01-15 11:25:15485 PrefetchingMetricsTestBase::TearDown();
Max Curran146bf442022-03-28 23:22:14486 }
487
kenoss557d4092025-04-01 05:01:15488 virtual void InitScopedFeatureList() = 0;
489
490 void InitBaseParams() {
491 scoped_feature_list_base_params_.InitWithFeaturesAndParameters(
Johanna9fe85f2023-01-17 10:15:43492 {{features::kPrefetchUseContentRefactor,
Liviu Tinta91455162022-12-14 22:06:23493 {{"ineligible_decoy_request_probability", "0"},
494 {"prefetch_container_lifetime_s", "-1"}}}},
Steven Wei012a48d2025-08-07 17:21:20495 {blink::features::kRemovePurposeHeaderForPrefetch});
Max Curran6b93426f2022-05-03 00:55:52496 }
497
Max Curranead64a62022-06-22 01:10:52498 void MakePrefetchService(std::unique_ptr<MockPrefetchServiceDelegate>
499 mock_prefetch_service_delegate) {
500 test_content_browser_client_ =
501 std::make_unique<ScopedPrefetchServiceContentBrowserClient>(
502 std::move(mock_prefetch_service_delegate));
503
Wayne Jackson Jr.88f3db62025-01-07 12:53:34504 std::unique_ptr<PrefetchService> prefetch_service =
505 std::make_unique<PrefetchService>(browser_context());
506 BrowserContextImpl::From(browser_context())
507 ->SetPrefetchServiceForTesting(std::move(prefetch_service));
Max Curranead64a62022-06-22 01:10:52508 PrefetchDocumentManager::SetPrefetchServiceForTesting(
Wayne Jackson Jr.88f3db62025-01-07 12:53:34509 BrowserContextImpl::From(browser_context())->GetPrefetchService());
Max Curranead64a62022-06-22 01:10:52510 }
511
Max Curran18a6f2b2022-05-02 23:13:24512 // Creates a prefetch request for |url| on the current main frame.
Max Curranc0137502023-05-02 18:06:22513 void MakePrefetchOnMainFrame(
514 const GURL& prefetch_url,
515 const PrefetchType& prefetch_type,
Devlin Cronin7f318c12023-06-09 00:57:01516 const blink::mojom::Referrer& referrer = blink::mojom::Referrer(),
Liviu Tintad608e012023-05-10 19:16:41517 network::mojom::NoVarySearchPtr&& no_vary_search_hint =
kenossb68c9e82024-10-10 10:11:15518 network::mojom::NoVarySearchPtr(),
519 PreloadingType planned_max_preloading_type = PreloadingType::kPrefetch) {
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37520 CHECK(prefetch_type.IsRendererInitiated());
Max Curran18a6f2b2022-05-02 23:13:24521 PrefetchDocumentManager* prefetch_document_manager =
522 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
Takashi Toyoshima9e3ca212023-04-28 05:57:59523 prefetch_document_manager->PrefetchUrl(
Kevin McNee98e068a2024-04-09 20:12:34524 prefetch_url, prefetch_type,
525 GetPredictorForPreloadingTriggerType(prefetch_type.trigger_type()),
HuanPo Lin740620b2025-03-21 12:37:26526 referrer, SpeculationRulesTags(), no_vary_search_hint,
kenossc3f9a2c2025-03-06 15:35:51527 PreloadPipelineInfo::Create(planned_max_preloading_type));
Max Curran18a6f2b2022-05-02 23:13:24528 }
529
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:00530 [[nodiscard]] std::unique_ptr<PrefetchHandle> MakePrefetchFromEmbedder(
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37531 const GURL& prefetch_url,
532 const PrefetchType& prefetch_type,
533 const blink::mojom::Referrer& referrer = blink::mojom::Referrer(),
534 const std::optional<url::Origin> referring_origin = std::nullopt) {
535 CHECK(!prefetch_type.IsRendererInitiated());
536
537 auto prefetch_container = std::make_unique<PrefetchContainer>(
Taiyo Mizuhashi49959d02025-04-22 16:07:54538 *web_contents(), prefetch_url, prefetch_type,
539 test::kPreloadingEmbedderHistgramSuffixForTesting, referrer,
540 std::move(referring_origin),
541 /*no_vary_search_hint=*/std::nullopt,
Taiyo Mizuhashid7c856992025-06-23 08:56:02542 /*priority=*/std::nullopt,
kenossa1af66f12025-03-07 06:10:55543 PreloadPipelineInfo::Create(
544 /*planned_max_preloading_type=*/PreloadingType::kPrefetch),
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37545 /*attempt=*/nullptr);
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:00546 return BrowserContextImpl::From(browser_context())
Wayne Jackson Jr.88f3db62025-01-07 12:53:34547 ->GetPrefetchService()
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:00548 ->AddPrefetchContainerWithHandle(std::move(prefetch_container));
Wayne Jackson Jr.88f3db62025-01-07 12:53:34549 }
550
Taiyo Mizuhashic79b5352025-06-20 16:15:17551 [[nodiscard]] std::unique_ptr<content::PrefetchHandle>
552 MakePrefetchFromBrowserContext(
Wayne Jackson Jr.88f3db62025-01-07 12:53:34553 const GURL& url,
554 std::optional<net::HttpNoVarySearchData> no_vary_search_data,
555 const net::HttpRequestHeaders& additional_headers,
elabadysayedf7d6b00f2025-02-05 11:27:00556 std::unique_ptr<PrefetchRequestStatusListener> request_status_listener,
Taiyo Mizuhashicd08a8f2025-06-10 17:27:32557 base::TimeDelta ttl = base::Seconds(/* 10 minutes */ 60 * 10),
558 bool should_disable_block_until_head_timeout = false) {
elabadysayedab1b97c02025-02-13 11:06:26559 return browser_context()->StartBrowserPrefetchRequest(
Taiyo Mizuhashi49959d02025-04-22 16:07:54560 url, test::kPreloadingEmbedderHistgramSuffixForTesting, true,
Taiyo Mizuhashid13f8ca2025-06-23 13:29:02561 no_vary_search_data, PrefetchPriority::kHighest, additional_headers,
kenoss70bfbfe2025-06-10 08:04:42562 std::move(request_status_listener), ttl,
Taiyo Mizuhashicd08a8f2025-06-10 17:27:32563 /*should_append_variations_header=*/true,
564 should_disable_block_until_head_timeout);
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37565 }
566
Max Curran18a6f2b2022-05-02 23:13:24567 int RequestCount() { return test_url_loader_factory_.NumPending(); }
568
Max Currana84311e2023-05-16 20:40:25569 void ClearCompletedRequests() {
570 std::vector<network::TestURLLoaderFactory::PendingRequest>* requests =
571 test_url_loader_factory_.pending_requests();
572
Andrew Rayskiydae52e92024-03-05 17:53:36573 std::erase_if(
Max Currana84311e2023-05-16 20:40:25574 *requests,
575 [](const network::TestURLLoaderFactory::PendingRequest& request) {
576 return !request.client.is_connected();
577 });
578 }
579
Jeremy Roman4c9524212023-07-27 22:01:05580 struct VerifyCommonRequestStateOptions {
581 bool use_prefetch_proxy = false;
582 net::RequestPriority expected_priority = net::RequestPriority::IDLE;
kenossb68c9e82024-10-10 10:11:15583 std::optional<std::string> sec_purpose_header_value = std::nullopt;
Wayne Jackson Jr.88f3db62025-01-07 12:53:34584 net::HttpRequestHeaders additional_headers;
Jeremy Roman4c9524212023-07-27 22:01:05585 };
586
587 void VerifyCommonRequestState(const GURL& url) {
588 VerifyCommonRequestState(url, {});
589 }
590
Taiyo Mizuhashid13f8ca2025-06-23 13:29:02591 void VerifyCommonRequestStateForWebContentsPrefetch(
Taiyo Mizuhashi2bef6682025-03-07 08:58:45592 const GURL& url,
593 const VerifyCommonRequestStateOptions& options) {
Taiyo Mizuhashid13f8ca2025-06-23 13:29:02594 VerifyCommonRequestStateOptions options_override = options;
595 options_override.expected_priority =
596 base::FeatureList::IsEnabled(
597 features::kPrefetchNetworkPriorityForEmbedders)
598 ? net::RequestPriority::MEDIUM
599 : net::RequestPriority::IDLE;
600 VerifyCommonRequestState(url, options_override);
601 }
602
603 void VerifyCommonRequestStateForBrowserContextPrefetch(
604 const GURL& url,
605 const VerifyCommonRequestStateOptions& options) {
606 VerifyCommonRequestStateOptions options_override = options;
607 options_override.expected_priority = net::RequestPriority::HIGHEST;
608 VerifyCommonRequestState(url, options_override);
Taiyo Mizuhashi2bef6682025-03-07 08:58:45609 }
610
Jeremy Roman4c9524212023-07-27 22:01:05611 void VerifyCommonRequestState(
612 const GURL& url,
613 const VerifyCommonRequestStateOptions& options) {
Max Curran18a6f2b2022-05-02 23:13:24614 SCOPED_TRACE(url.spec());
615 EXPECT_EQ(RequestCount(), 1);
616
617 network::TestURLLoaderFactory::PendingRequest* request =
618 test_url_loader_factory_.GetPendingRequest(0);
619
Liviu Tinta1fe1a6d2023-09-20 19:44:04620 VerifyCommonRequestState(url, options, request);
621 }
Max Curran18a6f2b2022-05-02 23:13:24622
Liviu Tinta1fe1a6d2023-09-20 19:44:04623 void VerifyCommonRequestStateByUrl(const GURL& url) {
624 VerifyCommonRequestStateByUrl(url, {});
625 }
Max Curran18a6f2b2022-05-02 23:13:24626
Liviu Tinta1fe1a6d2023-09-20 19:44:04627 void VerifyCommonRequestStateByUrl(
628 const GURL& url,
629 const VerifyCommonRequestStateOptions& options) {
630 SCOPED_TRACE(url.spec());
631 auto* pending_request = GetPendingRequestByUrl(url);
632 ASSERT_TRUE(pending_request);
Max Curran18a6f2b2022-05-02 23:13:24633
Liviu Tinta1fe1a6d2023-09-20 19:44:04634 VerifyCommonRequestState(url, options, pending_request);
Max Curran18a6f2b2022-05-02 23:13:24635 }
636
Lingqi Chi5b01e6a2024-08-26 04:53:01637 // Verify a prefetch attempt is pending (eligible but not started yet, to
638 // ensure prefetches are sequential);
639 void VerifyPrefetchAttemptIsPending(const GURL& url) {
640 PrefetchContainer::Key prefetch_key(MainDocumentToken(), url);
641 base::WeakPtr<PrefetchContainer> prefetch_container =
Wayne Jackson Jr.88f3db62025-01-07 12:53:34642 BrowserContextImpl::From(browser_context())
643 ->GetPrefetchService()
644 ->MatchUrl(prefetch_key);
Lingqi Chi5b01e6a2024-08-26 04:53:01645 ASSERT_TRUE(prefetch_container);
646 ASSERT_FALSE(prefetch_container->GetResourceRequest());
647 ASSERT_EQ(prefetch_container->GetLoadState(),
648 PrefetchContainer::LoadState::kEligible);
649 }
650
Max Curran18a6f2b2022-05-02 23:13:24651 void VerifyIsolationInfo(const net::IsolationInfo& isolation_info) {
652 EXPECT_FALSE(isolation_info.IsEmpty());
653 EXPECT_TRUE(isolation_info.network_isolation_key().IsFullyPopulated());
654 EXPECT_FALSE(isolation_info.network_isolation_key().IsTransient());
655 EXPECT_FALSE(isolation_info.site_for_cookies().IsNull());
656 }
657
Max Curran892ca5422022-12-12 20:55:34658 network::mojom::URLResponseHeadPtr CreateURLResponseHeadForPrefetch(
Max Curran18a6f2b2022-05-02 23:13:24659 net::HttpStatusCode http_status,
Max Curran18a6f2b2022-05-02 23:13:24660 const std::string mime_type,
Max Curran6b93426f2022-05-03 00:55:52661 bool use_prefetch_proxy,
Max Curran892ca5422022-12-12 20:55:34662 const std::vector<std::pair<std::string, std::string>>& headers,
663 const GURL& request_url) {
Max Curran18a6f2b2022-05-02 23:13:24664 auto head = network::CreateURLResponseHead(http_status);
665
666 head->response_time = base::Time::Now();
667 head->request_time =
668 head->response_time - base::Milliseconds(kTotalTimeDuration);
669
670 head->load_timing.connect_timing.connect_end =
671 base::TimeTicks::Now() - base::Minutes(2);
672 head->load_timing.connect_timing.connect_start =
673 head->load_timing.connect_timing.connect_end -
674 base::Milliseconds(kConnectTimeDuration);
675
Max Curran210cffa2022-09-06 22:24:31676 head->load_timing.receive_headers_end = base::TimeTicks::Now();
677 head->load_timing.request_start = head->load_timing.receive_headers_end -
678 base::Milliseconds(kHeaderLatency);
679
Andrew Williams8a9e2422023-11-06 20:32:57680 head->proxy_chain =
Max Curranead64a62022-06-22 01:10:52681 use_prefetch_proxy
Andrew Williams8a9e2422023-11-06 20:32:57682 ? net::ProxyChain::FromSchemeHostAndPort(
Max Curranead64a62022-06-22 01:10:52683 net::ProxyServer::Scheme::SCHEME_HTTPS,
Jeremy Romane561b412024-02-15 18:31:34684 PrefetchProxyHost(
685 GURL(MockPrefetchServiceDelegate::kPrefetchProxyAddress))
686 .spec(),
Arthur Sonzognic686e8f2024-01-11 08:36:37687 std::nullopt)
Andrew Williams8a9e2422023-11-06 20:32:57688 : net::ProxyChain::Direct();
Max Curran6b93426f2022-05-03 00:55:52689
Max Curran18a6f2b2022-05-02 23:13:24690 head->mime_type = mime_type;
691 for (const auto& header : headers) {
692 head->headers->AddHeader(header.first, header.second);
693 }
Liviu Tintad97a6a32022-12-08 23:28:40694 if (!head->parsed_headers) {
Max Curran892ca5422022-12-12 20:55:34695 head->parsed_headers =
696 network::PopulateParsedHeaders(head->headers.get(), request_url);
Liviu Tintad97a6a32022-12-08 23:28:40697 }
698
Max Curran892ca5422022-12-12 20:55:34699 return head;
700 }
701
Max Curran5d4da4b42023-03-10 23:41:46702 void MakeSingleRedirectAndWait(
703 const net::RedirectInfo& redirect_info,
704 network::mojom::URLResponseHeadPtr redirect_head) {
705 network::TestURLLoaderFactory::PendingRequest* request =
706 test_url_loader_factory_.GetPendingRequest(0);
707 ASSERT_TRUE(request);
708 ASSERT_TRUE(request->client);
709
710 request->client->OnReceiveRedirect(redirect_info, redirect_head.Clone());
711 task_environment()->RunUntilIdle();
712 }
713
714 void VerifyFollowRedirectParams(size_t expected_follow_redirect_params_size) {
715 network::TestURLLoaderFactory::PendingRequest* request =
716 test_url_loader_factory_.GetPendingRequest(0);
717 ASSERT_TRUE(request);
718 ASSERT_TRUE(request->test_url_loader);
719
720 const auto& follow_redirect_params =
721 request->test_url_loader->follow_redirect_params();
722 EXPECT_EQ(follow_redirect_params.size(),
723 expected_follow_redirect_params_size);
724
725 for (const auto& follow_redirect_param : follow_redirect_params) {
726 EXPECT_EQ(follow_redirect_param.removed_headers.size(), 0U);
727 EXPECT_TRUE(follow_redirect_param.modified_headers.IsEmpty());
728 EXPECT_TRUE(follow_redirect_param.modified_cors_exempt_headers.IsEmpty());
729 EXPECT_FALSE(follow_redirect_param.new_url);
730 }
731 }
732
Max Curran892ca5422022-12-12 20:55:34733 void MakeResponseAndWait(
734 net::HttpStatusCode http_status,
735 net::Error net_error,
736 const std::string mime_type,
737 bool use_prefetch_proxy,
738 std::vector<std::pair<std::string, std::string>> headers,
Max Curran5d4da4b42023-03-10 23:41:46739 const std::string& body) {
Max Curran892ca5422022-12-12 20:55:34740 network::TestURLLoaderFactory::PendingRequest* request =
741 test_url_loader_factory_.GetPendingRequest(0);
742 ASSERT_TRUE(request);
743
744 auto head = CreateURLResponseHeadForPrefetch(http_status, mime_type,
745 use_prefetch_proxy, headers,
746 request->request.url);
Max Curran18a6f2b2022-05-02 23:13:24747 network::URLLoaderCompletionStatus status(net_error);
748 test_url_loader_factory_.AddResponse(request->request.url, std::move(head),
Max Curran5d4da4b42023-03-10 23:41:46749 body, status);
Max Curran18a6f2b2022-05-02 23:13:24750 task_environment()->RunUntilIdle();
751 // Clear responses in the network service so we can inspect the next request
752 // that comes in before it is responded to.
753 test_url_loader_factory_.ClearResponses();
754 }
755
Max Curran892ca5422022-12-12 20:55:34756 void SendHeadOfResponseAndWait(
757 net::HttpStatusCode http_status,
758 const std::string mime_type,
759 bool use_prefetch_proxy,
760 std::vector<std::pair<std::string, std::string>> headers,
761 uint32_t expected_total_body_size) {
Max Curran892ca5422022-12-12 20:55:34762 network::TestURLLoaderFactory::PendingRequest* request =
763 test_url_loader_factory_.GetPendingRequest(0);
Liviu Tinta1fe1a6d2023-09-20 19:44:04764 ASSERT_FALSE(producer_handle_for_gurl_.count(request->request.url));
765 ASSERT_TRUE(request);
766 SendHeadOfResponseAndWait(http_status, mime_type, use_prefetch_proxy,
767 headers, expected_total_body_size, request);
768 ASSERT_TRUE(producer_handle_for_gurl_.count(request->request.url));
769 }
770
771 void SendHeadOfResponseForUrlAndWait(
772 const GURL& request_url,
773 net::HttpStatusCode http_status,
774 const std::string mime_type,
775 bool use_prefetch_proxy,
776 std::vector<std::pair<std::string, std::string>> headers,
777 uint32_t expected_total_body_size) {
778 auto* request = GetPendingRequestByUrl(request_url);
779
Max Curran892ca5422022-12-12 20:55:34780 ASSERT_TRUE(request);
781 ASSERT_TRUE(request->client);
Liviu Tinta1fe1a6d2023-09-20 19:44:04782 ASSERT_FALSE(producer_handle_for_gurl_.count(request->request.url));
783 SendHeadOfResponseAndWait(http_status, mime_type, use_prefetch_proxy,
784 headers, expected_total_body_size, request);
785 ASSERT_TRUE(producer_handle_for_gurl_.count(request->request.url));
Max Curran892ca5422022-12-12 20:55:34786 }
787
788 void SendBodyContentOfResponseAndWait(const std::string& body) {
Liviu Tinta1fe1a6d2023-09-20 19:44:04789 network::TestURLLoaderFactory::PendingRequest* request =
790 test_url_loader_factory_.GetPendingRequest(0);
791 ASSERT_TRUE(request);
792 SendBodyContentOfResponseAndWait(body, request);
793 }
Max Curran892ca5422022-12-12 20:55:34794
Liviu Tinta1fe1a6d2023-09-20 19:44:04795 void SendBodyContentOfResponseForUrlAndWait(const GURL& url,
796 const std::string& body) {
797 auto* request = GetPendingRequestByUrl(url);
798 ASSERT_TRUE(request);
799 SendBodyContentOfResponseAndWait(body, request);
Max Curran892ca5422022-12-12 20:55:34800 }
801
802 void CompleteResponseAndWait(net::Error net_error,
803 uint32_t expected_total_body_size) {
Max Curran892ca5422022-12-12 20:55:34804 network::TestURLLoaderFactory::PendingRequest* request =
805 test_url_loader_factory_.GetPendingRequest(0);
806 ASSERT_TRUE(request);
Liviu Tinta1fe1a6d2023-09-20 19:44:04807 CompleteResponseAndWait(net_error, expected_total_body_size, request);
808 }
Max Curran892ca5422022-12-12 20:55:34809
Liviu Tinta1fe1a6d2023-09-20 19:44:04810 void CompleteResponseForUrlAndWait(const GURL& url,
811 net::Error net_error,
812 uint32_t expected_total_body_size) {
813 auto* request = GetPendingRequestByUrl(url);
814 ASSERT_TRUE(request);
815 CompleteResponseAndWait(net_error, expected_total_body_size, request);
Max Curran892ca5422022-12-12 20:55:34816 }
817
Max Curran146bf442022-03-28 23:22:14818 bool SetCookie(const GURL& url, const std::string& value) {
Ari Chivukula92851c32024-04-09 12:37:44819 std::unique_ptr<net::CanonicalCookie> cookie(
820 net::CanonicalCookie::CreateForTesting(url, value, base::Time::Now()));
Max Curran146bf442022-03-28 23:22:14821
822 EXPECT_TRUE(cookie.get());
823
824 bool result = false;
825 base::RunLoop run_loop;
826
827 net::CookieOptions options;
828 options.set_include_httponly();
829 options.set_same_site_cookie_context(
830 net::CookieOptions::SameSiteCookieContext::MakeInclusive());
831
832 cookie_manager_->SetCanonicalCookie(
833 *cookie.get(), url, options,
834 base::BindOnce(
835 [](bool* result, base::RunLoop* run_loop,
836 net::CookieAccessResult set_cookie_access_result) {
837 *result = set_cookie_access_result.status.IsInclude();
838 run_loop->Quit();
839 },
840 &result, &run_loop));
841 run_loop.Run();
842 return result;
843 }
844
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:11845 void Navigate(
846 const GURL& url,
847 int initiator_process_id,
Arthur Sonzognic686e8f2024-01-11 08:36:37848 const std::optional<blink::LocalFrameToken>& initiator_local_frame_token,
849 const std::optional<blink::DocumentToken>& initiator_document_token) {
Max Curran210cffa2022-09-06 22:24:31850 mock_navigation_handle_ =
851 std::make_unique<testing::NiceMock<MockNavigationHandle>>(
852 web_contents());
853 mock_navigation_handle_->set_url(url);
Hiroshige Hayashizaki0de58d32023-10-23 08:58:43854 mock_navigation_handle_->set_initiator_process_id(initiator_process_id);
Hiroshige Hayashizaki96f3d63132023-05-23 23:14:52855 mock_navigation_handle_->set_initiator_frame_token(
856 base::OptionalToPtr(initiator_local_frame_token));
Max Curranc4445fc2022-06-02 18:43:43857
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:11858 // Simulate how `NavigationRequest` calls
859 // `PrefetchServingPageMetricsContainer::GetOrCreateForNavigationHandle()`.
860 if (initiator_document_token &&
861 PrefetchDocumentManager::FromDocumentToken(initiator_process_id,
862 *initiator_document_token)) {
863 PrefetchServingPageMetricsContainer::GetOrCreateForNavigationHandle(
864 *mock_navigation_handle_);
865 }
Max Curran210cffa2022-09-06 22:24:31866 }
867
Taiyo Mizuhashi01d86af22024-03-27 14:27:42868 void NavigateInitiatedByRenderer(const GURL& url) {
Emily Andrewsd15fd762024-12-10 20:41:54869 Navigate(url, main_rfh()->GetProcess()->GetDeprecatedID(),
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:11870 main_rfh()->GetFrameToken(), MainDocumentToken());
Hiroshige Hayashizaki0de58d32023-10-23 08:58:43871 }
872
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37873 void NavigateInitiatedByBrowser(const GURL& url) {
874 Navigate(url, ChildProcessHost::kInvalidUniqueID, std::nullopt,
875 std::nullopt);
876 }
877
Arthur Sonzognic686e8f2024-01-11 08:36:37878 std::optional<PrefetchServingPageMetrics>
Max Curran210cffa2022-09-06 22:24:31879 GetMetricsForMostRecentNavigation() {
880 if (!mock_navigation_handle_)
Arthur Sonzognic686e8f2024-01-11 08:36:37881 return std::nullopt;
Max Curran210cffa2022-09-06 22:24:31882
883 return PrefetchServingPageMetrics::GetForNavigationHandle(
884 *mock_navigation_handle_);
Max Curranc4445fc2022-06-02 18:43:43885 }
886
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:11887 base::WeakPtr<PrefetchServingPageMetricsContainer>
888 GetServingPageMetricsContainerForMostRecentNavigation() {
889 if (!mock_navigation_handle_) {
890 return nullptr;
891 }
892
893 auto* serving_page_metrics_container =
894 PrefetchServingPageMetricsContainer::GetForNavigationHandle(
895 *mock_navigation_handle_);
896 if (!serving_page_metrics_container) {
897 return nullptr;
898 }
899 return serving_page_metrics_container->GetWeakPtr();
900 }
901
Hiroshige Hayashizaki2df45292023-10-10 22:59:03902 blink::DocumentToken MainDocumentToken() {
903 return static_cast<RenderFrameHostImpl*>(main_rfh())->GetDocumentToken();
904 }
905
Hiroshige Hayashizakibc780622023-09-05 19:07:33906 void GetPrefetchToServe(
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57907 base::test::TestFuture<PrefetchServingHandle>& future,
Hiroshige Hayashizaki6e9a1892023-04-17 06:47:38908 const GURL& url,
Taiyo Mizuhashi01d86af22024-03-27 14:27:42909 std::optional<blink::DocumentToken> initiator_document_token) {
kenossf6fbd5652024-09-04 00:40:28910 auto callback = base::BindOnce(
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57911 [](base::test::TestFuture<PrefetchServingHandle>* future,
Hiroshige Hayashizakic853c0302023-09-13 08:51:07912 std::vector<PrefetchRequestHandler>* request_handler_keep_alive,
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57913 PrefetchServingHandle prefetch_to_serve) {
Hiroshige Hayashizakia580b332023-09-06 00:16:54914 if (prefetch_to_serve) {
915 // When GetPrefetchToServe() is successful, also call
916 // `CreateRequestHandler()` to simulate
917 // `PrefetchURLLoaderInterceptor::OnGetPrefetchComplete()` behavior,
918 // which is the primary non-test `GetPrefetchToServe()` code path.
Hiroshige Hayashizaki055871f22025-03-14 03:27:38919 auto request_handler =
920 prefetch_to_serve.CreateRequestHandler().first;
Hiroshige Hayashizakia580b332023-09-06 00:16:54921 CHECK(request_handler);
Hiroshige Hayashizakic853c0302023-09-13 08:51:07922 // Keep-alive the PrefetchRequestHandler, because destructing
Hiroshige Hayashizakia580b332023-09-06 00:16:54923 // `request_handler` here can signal that the serving is finished,
924 // and e.g. close body mojo pipe.
925 request_handler_keep_alive->push_back(std::move(request_handler));
926 }
927 future->SetValue(std::move(prefetch_to_serve));
928 },
929 base::Unretained(&future),
kenossf6fbd5652024-09-04 00:40:28930 base::Unretained(&request_handler_keep_alive_));
Wayne Jackson Jr.88f3db62025-01-07 12:53:34931 PrefetchService* prefetch_service =
932 BrowserContextImpl::From(browser_context())->GetPrefetchService();
kenossf6fbd5652024-09-04 00:40:28933 auto key = PrefetchContainer::Key(initiator_document_token, url);
kenoss3dfd90792025-03-11 01:13:46934 PrefetchMatchResolver::FindPrefetch(
Hiroshige Hayashizakib4af4c2d2025-03-17 18:59:10935 std::move(key), PrefetchServiceWorkerState::kDisallowed,
936 /*is_nav_prerender=*/false, *prefetch_service,
kenossf6fbd5652024-09-04 00:40:28937 GetServingPageMetricsContainerForMostRecentNavigation(),
938 std::move(callback));
Hiroshige Hayashizakibc780622023-09-05 19:07:33939 }
940
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57941 PrefetchServingHandle GetPrefetchToServe(
Hiroshige Hayashizakibc780622023-09-05 19:07:33942 const GURL& url,
Taiyo Mizuhashi01d86af22024-03-27 14:27:42943 std::optional<blink::DocumentToken> initiator_document_token) {
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57944 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizaki2df45292023-10-10 22:59:03945 GetPrefetchToServe(future, url, std::move(initiator_document_token));
Hiroshige Hayashizaki00b3f002023-07-15 00:32:48946 return future.Take();
Max Curran2e83b5d2022-12-29 18:53:38947 }
948
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57949 PrefetchServingHandle GetPrefetchToServe(const GURL& url) {
Taiyo Mizuhashi01d86af22024-03-27 14:27:42950 return GetPrefetchToServe(url, MainDocumentToken());
951 }
952
kenossf6fbd5652024-09-04 00:40:28953 // Simulates part of navigation.
954 //
955 // - Creates MockNavigationHandle.
kenoss3dfd90792025-03-11 01:13:46956 // - Starts matching of prefetch `PrefetchMatchResolver::FindPrefetch()`.
kenossf6fbd5652024-09-04 00:40:28957 //
958 // Note that these are very small part of navigation. For example,
959 // post-matching process `OnGotPrefetchToServe()` and redirects are not
960 // handled.
961 std::unique_ptr<NavigationResult> SimulatePartOfNavigation(
962 const GURL& url,
kenoss804a72dc2024-10-11 12:43:49963 bool is_renderer_initiated,
964 bool is_nav_prerender) {
kenossf6fbd5652024-09-04 00:40:28965 return is_renderer_initiated
966 ? SimulatePartOfNavigation(
Emily Andrewsd15fd762024-12-10 20:41:54967 url, is_nav_prerender,
968 main_rfh()->GetProcess()->GetDeprecatedID(),
kenossf6fbd5652024-09-04 00:40:28969 main_rfh()->GetFrameToken(), MainDocumentToken())
kenoss804a72dc2024-10-11 12:43:49970 : SimulatePartOfNavigation(url, is_nav_prerender,
kenossf6fbd5652024-09-04 00:40:28971 ChildProcessHost::kInvalidUniqueID,
972 std::nullopt, std::nullopt);
973 }
974
975 std::unique_ptr<NavigationResult> SimulatePartOfNavigation(
976 const GURL& url,
kenoss804a72dc2024-10-11 12:43:49977 bool is_nav_prerender,
kenossf6fbd5652024-09-04 00:40:28978 int initiator_process_id,
979 const std::optional<blink::LocalFrameToken>& initiator_local_frame_token,
980 const std::optional<blink::DocumentToken>& initiator_document_token) {
kenossf6fbd5652024-09-04 00:40:28981 // Use std::unique_ptr as the below uses raw pointers and references.
982 auto res = std::make_unique<NavigationResult>();
983
984 res->navigation_handle =
985 std::make_unique<testing::NiceMock<MockNavigationHandle>>(
986 web_contents());
987 res->navigation_handle->set_url(url);
988 res->navigation_handle->set_initiator_process_id(initiator_process_id);
989 res->navigation_handle->set_initiator_frame_token(
990 base::OptionalToPtr(initiator_local_frame_token));
991
992 // Simulate how `NavigationRequest` calls
993 // `PrefetchServingPageMetricsContainer::GetOrCreateForNavigationHandle()`.
994 if (initiator_document_token &&
995 PrefetchDocumentManager::FromDocumentToken(initiator_process_id,
996 *initiator_document_token)) {
997 PrefetchServingPageMetricsContainer::GetOrCreateForNavigationHandle(
998 *res->navigation_handle);
999 }
1000
1001 auto callback = base::BindOnce(
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571002 [](base::test::TestFuture<PrefetchServingHandle>& serving_handle_future,
1003 PrefetchServingHandle serving_handle) {
1004 serving_handle_future.SetValue(std::move(serving_handle));
kenossf6fbd5652024-09-04 00:40:281005 },
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571006 std::ref(res->serving_handle_future));
kenossf6fbd5652024-09-04 00:40:281007 auto serving_page_metrics_container =
1008 [&res]() -> base::WeakPtr<PrefetchServingPageMetricsContainer> {
1009 auto* serving_page_metrics_container =
1010 PrefetchServingPageMetricsContainer::GetForNavigationHandle(
1011 *res->navigation_handle);
1012 if (!serving_page_metrics_container) {
1013 return nullptr;
1014 }
1015
1016 return serving_page_metrics_container->GetWeakPtr();
1017 }();
Wayne Jackson Jr.88f3db62025-01-07 12:53:341018 PrefetchService* prefetch_service =
1019 BrowserContextImpl::From(browser_context())->GetPrefetchService();
kenossf6fbd5652024-09-04 00:40:281020 auto key = PrefetchContainer::Key(initiator_document_token, url);
kenoss3dfd90792025-03-11 01:13:461021 PrefetchMatchResolver::FindPrefetch(
Hiroshige Hayashizakib4af4c2d2025-03-17 18:59:101022 std::move(key), PrefetchServiceWorkerState::kDisallowed,
1023 is_nav_prerender, *prefetch_service,
kenossf6fbd5652024-09-04 00:40:281024 std::move(serving_page_metrics_container), std::move(callback));
1025
1026 return res;
1027 }
1028
Max Curranead64a62022-06-22 01:10:521029 ScopedPrefetchServiceContentBrowserClient* test_content_browser_client() {
1030 return test_content_browser_client_.get();
1031 }
1032
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461033 // ##### Helpers for serving-related metrics #####
kenossf6fbd5652024-09-04 00:40:281034 static void ExpectServingMetrics(
1035 const base::Location& location,
1036 std::optional<PrefetchServingPageMetrics> serving_page_metrics,
1037 PrefetchStatus expected_prefetch_status,
1038 std::optional<base::TimeDelta> prefetch_header_latency,
1039 bool required_private_prefetch_proxy) {
1040 SCOPED_TRACE(::testing::Message() << "callsite: " << location.ToString());
1041
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461042 ASSERT_TRUE(serving_page_metrics);
1043 ASSERT_TRUE(serving_page_metrics->prefetch_status);
1044 EXPECT_EQ(serving_page_metrics->prefetch_status.value(),
1045 static_cast<int>(expected_prefetch_status));
kenossf6fbd5652024-09-04 00:40:281046 EXPECT_EQ(serving_page_metrics->prefetch_header_latency,
1047 prefetch_header_latency);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461048 EXPECT_EQ(serving_page_metrics->required_private_prefetch_proxy,
1049 required_private_prefetch_proxy);
1050 EXPECT_TRUE(serving_page_metrics->same_tab_as_prefetching_tab);
kenossf6fbd5652024-09-04 00:40:281051 }
1052
1053 void ExpectServingMetrics(PrefetchStatus expected_prefetch_status,
1054 bool prefetch_header_latency = false,
1055 bool required_private_prefetch_proxy = true) {
1056 // std::optional<base::TimeDelta> prefetch_header_latency_value =
1057 // prefetch_header_latency ? base::Milliseconds(kHeaderLatency) :
1058 // std::nullopt;
1059 std::optional<base::TimeDelta> prefetch_header_latency_value;
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461060 if (prefetch_header_latency) {
kenossf6fbd5652024-09-04 00:40:281061 prefetch_header_latency_value = base::Milliseconds(kHeaderLatency);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461062 }
kenossf6fbd5652024-09-04 00:40:281063 ExpectServingMetrics(FROM_HERE, GetMetricsForMostRecentNavigation(),
1064 expected_prefetch_status,
1065 std::move(prefetch_header_latency_value),
1066 required_private_prefetch_proxy);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461067 }
1068
1069 void ExpectServingMetricsSuccess(
1070 bool required_private_prefetch_proxy = true) {
1071 ExpectServingMetrics(PrefetchStatus::kPrefetchSuccessful,
1072 /*prefetch_header_latency=*/true,
1073 required_private_prefetch_proxy);
1074 }
1075
kenossf6fbd5652024-09-04 00:40:281076 struct ExpectServingMetricsArgs {
1077 PrefetchStatus prefetch_status;
1078 std::optional<base::TimeDelta> prefetch_header_latency;
1079 bool required_private_prefetch_proxy;
1080 };
1081 static void ExpectServingMetrics(
1082 const base::Location& location,
1083 const std::unique_ptr<NavigationResult>& nav_res,
1084 ExpectServingMetricsArgs args) {
1085 ExpectServingMetrics(location,
1086 PrefetchServingPageMetrics::GetForNavigationHandle(
1087 *nav_res->navigation_handle),
1088 args.prefetch_status, args.prefetch_header_latency,
1089 args.required_private_prefetch_proxy);
1090 }
1091
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461092 static void ExpectServingReaderSuccess(
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571093 const PrefetchServingHandle& serving_handle) {
1094 ExpectServingReaderSuccess(FROM_HERE, serving_handle);
kenossf6fbd5652024-09-04 00:40:281095 }
1096
1097 static void ExpectServingReaderSuccess(
1098 const base::Location& location,
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571099 const PrefetchServingHandle& serving_handle) {
kenossf6fbd5652024-09-04 00:40:281100 SCOPED_TRACE(::testing::Message() << "callsite: " << location.ToString());
1101
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571102 ASSERT_TRUE(serving_handle);
1103 EXPECT_TRUE(serving_handle.HasPrefetchStatus());
1104 EXPECT_EQ(serving_handle.GetPrefetchStatus(),
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461105 PrefetchStatus::kPrefetchSuccessful);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571106 EXPECT_EQ(serving_handle.GetServableState(base::TimeDelta::Max()),
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:081107 PrefetchServableState::kServable);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571108 ASSERT_TRUE(serving_handle.GetPrefetchContainer()->GetNonRedirectHead());
1109 EXPECT_TRUE(serving_handle.GetPrefetchContainer()
kenossf7b4d60d2024-07-16 15:15:081110 ->GetNonRedirectHead()
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461111 ->was_in_prefetch_cache);
1112 }
1113
Max Curran146bf442022-03-28 23:22:141114 protected:
Liviu Tinta1fe1a6d2023-09-20 19:44:041115 network::TestURLLoaderFactory::PendingRequest* GetPendingRequestByUrl(
1116 const GURL& url) {
1117 auto& pending_requests = *test_url_loader_factory_.pending_requests();
Peter Kasting1557e5f2025-01-28 01:14:081118 auto it = std::ranges::find(pending_requests, url,
1119 [](const auto& pending_request) {
1120 return pending_request.request.url;
1121 });
Liviu Tinta1fe1a6d2023-09-20 19:44:041122 if (it == pending_requests.end()) {
1123 return nullptr;
1124 }
1125 network::TestURLLoaderFactory::PendingRequest* request = &*it;
1126 return request;
1127 }
1128
1129 void VerifyCommonRequestState(
1130 const GURL& url,
1131 const VerifyCommonRequestStateOptions& options,
1132 const network::TestURLLoaderFactory::PendingRequest* request) {
1133 ASSERT_TRUE(request);
1134
1135 EXPECT_EQ(request->request.url, url);
1136 EXPECT_EQ(request->request.method, "GET");
1137 EXPECT_TRUE(request->request.enable_load_timing);
Adithya Srinivasanefc294932023-12-11 17:48:201138 EXPECT_EQ(request->request.load_flags, net::LOAD_PREFETCH);
Liviu Tinta1fe1a6d2023-09-20 19:44:041139 EXPECT_EQ(request->request.credentials_mode,
1140 network::mojom::CredentialsMode::kInclude);
1141
David Risneya1dd2bf2025-03-06 03:40:421142 EXPECT_THAT(
1143 request->request.headers.GetHeader(blink::kPurposeHeaderName),
1144 testing::Optional(std::string(blink::kSecPurposePrefetchHeaderValue)));
Liviu Tinta1fe1a6d2023-09-20 19:44:041145
kenossb68c9e82024-10-10 10:11:151146 std::string sec_purpose_header_value;
1147 if (options.sec_purpose_header_value) {
1148 sec_purpose_header_value = options.sec_purpose_header_value.value();
1149 } else {
David Risneya1dd2bf2025-03-06 03:40:421150 sec_purpose_header_value =
1151 options.use_prefetch_proxy
1152 ? blink::kSecPurposePrefetchAnonymousClientIpHeaderValue
1153 : blink::kSecPurposePrefetchHeaderValue;
kenossb68c9e82024-10-10 10:11:151154 }
David Risneya1dd2bf2025-03-06 03:40:421155 EXPECT_THAT(
1156 request->request.headers.GetHeader(blink::kSecPurposeHeaderName),
1157 testing::Optional(sec_purpose_header_value));
Liviu Tinta1fe1a6d2023-09-20 19:44:041158
Chris Fredricksond923c682024-07-30 18:19:341159 EXPECT_THAT(request->request.headers.GetHeader("Accept"),
1160 testing::Optional(FrameAcceptHeaderValue(
1161 /*allow_sxg_responses=*/true, browser_context())));
Liviu Tinta1fe1a6d2023-09-20 19:44:041162
Chris Fredricksond923c682024-07-30 18:19:341163 EXPECT_THAT(request->request.headers.GetHeader("Upgrade-Insecure-Requests"),
1164 testing::Optional(std::string("1")));
Liviu Tinta1fe1a6d2023-09-20 19:44:041165
1166 ASSERT_TRUE(request->request.trusted_params.has_value());
1167 VerifyIsolationInfo(request->request.trusted_params->isolation_info);
1168
1169 EXPECT_EQ(request->request.priority, options.expected_priority);
Wayne Jackson Jr.88f3db62025-01-07 12:53:341170
1171 net::HttpRequestHeaders::Iterator header_it(options.additional_headers);
1172 while (header_it.GetNext()) {
1173 EXPECT_THAT(request->request.headers.GetHeader(header_it.name()),
1174 testing::Optional(header_it.value()));
1175 }
Liviu Tinta1fe1a6d2023-09-20 19:44:041176 }
1177
1178 void SendHeadOfResponseAndWait(
1179 net::HttpStatusCode http_status,
1180 const std::string mime_type,
1181 bool use_prefetch_proxy,
1182 std::vector<std::pair<std::string, std::string>> headers,
1183 uint32_t expected_total_body_size,
1184 network::TestURLLoaderFactory::PendingRequest* request) {
1185 ASSERT_FALSE(producer_handle_for_gurl_.count(request->request.url));
1186 ASSERT_TRUE(request);
1187 ASSERT_TRUE(request->client);
1188
1189 auto head = CreateURLResponseHeadForPrefetch(http_status, mime_type,
1190 use_prefetch_proxy, headers,
1191 request->request.url);
1192
1193 mojo::ScopedDataPipeConsumerHandle body;
1194 EXPECT_EQ(mojo::CreateDataPipe(
1195 expected_total_body_size,
1196 producer_handle_for_gurl_[request->request.url], body),
1197 MOJO_RESULT_OK);
1198
1199 request->client->OnReceiveResponse(std::move(head), std::move(body),
Arthur Sonzognic686e8f2024-01-11 08:36:371200 std::nullopt);
Liviu Tinta1fe1a6d2023-09-20 19:44:041201 task_environment()->RunUntilIdle();
1202 }
1203
1204 void SendBodyContentOfResponseAndWait(
1205 const std::string& body,
1206 network::TestURLLoaderFactory::PendingRequest* request) {
1207 ASSERT_TRUE(producer_handle_for_gurl_.count(request->request.url));
1208 ASSERT_TRUE(producer_handle_for_gurl_[request->request.url]);
1209
Lukasz Anforowicz0339afd2024-06-24 19:57:191210 EXPECT_EQ(producer_handle_for_gurl_[request->request.url]->WriteAllData(
1211 base::as_byte_span(body)),
Lukasz Anforowicz2a672e32024-06-14 17:27:491212 MOJO_RESULT_OK);
1213 // Ok to ignore `actually_written_bytes` because of `...ALL_OR_NONE`.
Liviu Tinta1fe1a6d2023-09-20 19:44:041214 task_environment()->RunUntilIdle();
1215 }
1216
1217 void CompleteResponseAndWait(
1218 net::Error net_error,
1219 uint32_t expected_total_body_size,
1220 network::TestURLLoaderFactory::PendingRequest* request) {
1221 ASSERT_TRUE(request);
1222 ASSERT_TRUE(request->client);
1223 if (producer_handle_for_gurl_.count(request->request.url)) {
1224 producer_handle_for_gurl_[request->request.url].reset();
1225 producer_handle_for_gurl_.erase(request->request.url);
1226 }
1227
1228 network::URLLoaderCompletionStatus completion_status(net_error);
1229 completion_status.decoded_body_length = expected_total_body_size;
1230 request->client->OnComplete(completion_status);
1231 task_environment()->RunUntilIdle();
1232
1233 test_url_loader_factory_.ClearResponses();
1234 }
1235
Anthony Vallée-Dubois4b2977912024-11-22 16:28:511236 base::ScopedMockElapsedTimersForTest scoped_test_timer_;
1237
Liviu Tinta91202302023-09-26 17:49:111238 std::unique_ptr<PrefetchFakeServiceWorkerContext> service_worker_context_;
Max Curran146bf442022-03-28 23:22:141239 mojo::Remote<network::mojom::CookieManager> cookie_manager_;
1240
Max Curran18a6f2b2022-05-02 23:13:241241 network::TestURLLoaderFactory test_url_loader_factory_;
1242 scoped_refptr<network::SharedURLLoaderFactory>
1243 test_shared_url_loader_factory_;
1244
kenoss557d4092025-04-01 05:01:151245 base::test::ScopedFeatureList scoped_feature_list_base_params_;
Simon Pelchatb2ce6df2023-07-26 21:48:371246 // Disable sampling of UKM preloading logs.
1247 content::test::PreloadingConfigOverride preloading_config_override_;
Max Curranead64a62022-06-22 01:10:521248
Max Curran210cffa2022-09-06 22:24:311249 std::unique_ptr<testing::NiceMock<MockNavigationHandle>>
1250 mock_navigation_handle_;
1251
Max Curranead64a62022-06-22 01:10:521252 std::unique_ptr<ScopedPrefetchServiceContentBrowserClient>
1253 test_content_browser_client_;
Max Curran892ca5422022-12-12 20:55:341254
Liviu Tinta1fe1a6d2023-09-20 19:44:041255 std::map<GURL, mojo::ScopedDataPipeProducerHandle> producer_handle_for_gurl_;
William Liu60e005f2023-01-18 16:17:221256
Hiroshige Hayashizakic853c0302023-09-13 08:51:071257 std::vector<PrefetchRequestHandler> request_handler_keep_alive_;
Liviu Tinta2e1ffe22024-06-21 21:01:331258
1259 variations::ScopedVariationsIdsProvider scoped_variations_ids_provider_{
1260 variations::VariationsIdsProvider::Mode::kIgnoreSignedInState};
Max Curran146bf442022-03-28 23:22:141261};
Max Curran6c2835ea2022-03-07 19:52:381262
kenossbd9a3fc2025-03-28 05:15:571263class PrefetchServiceTest
1264 : public PrefetchServiceTestBase,
1265 public WithPrefetchServiceRearchParam,
1266 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
1267 public:
1268 PrefetchServiceTest() : WithPrefetchServiceRearchParam(GetParam()) {}
kenoss557d4092025-04-01 05:01:151269
1270 void InitScopedFeatureList() override {
1271 InitBaseParams();
1272 InitRearchFeatures();
1273 }
kenoss972e8682024-09-05 00:20:141274};
1275
kenossbd9a3fc2025-03-28 05:15:571276INSTANTIATE_TEST_SUITE_P(
1277 ParametrizedTests,
1278 PrefetchServiceTest,
1279 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
1280
1281TEST_P(PrefetchServiceTest, SuccessCase) {
Max Curran6b93426f2022-05-03 00:55:521282 base::HistogramTester histogram_tester;
1283
Max Curranead64a62022-06-22 01:10:521284 MakePrefetchService(
1285 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
1286
Takashi Nakayama978f0a152025-06-17 08:26:251287 const PrefetchType prefetch_type =
1288 PrefetchType(PreloadingTriggerType::kSpeculationRule,
1289 /*use_prefetch_proxy=*/true,
1290 blink::mojom::SpeculationEagerness::kImmediate);
Taiyo Mizuhashi43066071f2025-04-25 23:40:221291 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:541292 task_environment()->RunUntilIdle();
Max Curran146bf442022-03-28 23:22:141293
Max Curran18a6f2b2022-05-02 23:13:241294 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:051295 {.use_prefetch_proxy = true});
Max Curran18a6f2b2022-05-02 23:13:241296 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
Max Curran6b93426f2022-05-03 00:55:521297 /*use_prefetch_proxy=*/true,
Max Curran18a6f2b2022-05-02 23:13:241298 {{"X-Testing", "Hello World"}}, kHTMLBody);
1299
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461300 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:361301
Taiyo Mizuhashi01d86af22024-03-27 14:27:421302 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Curranc4445fc2022-06-02 18:43:431303
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461304 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:361305 ExpectServingMetricsSuccess();
William Liu77089052022-12-15 18:53:351306
Max Curran7d2578b2023-04-12 19:19:281307 histogram_tester.ExpectUniqueSample(
1308 "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
1309
Taiyo Mizuhashi43066071f2025-04-25 23:40:221310 histogram_tester.ExpectUniqueSample(
1311 base::StringPrintf(
1312 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
1313 GetMetricsSuffixTriggerTypeAndEagerness(
1314 prefetch_type, /*embedder_histogram_suffix=*/std::nullopt)),
1315 false, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:541316
1317 histogram_tester.ExpectUniqueSample(
1318 base::StrCat(
1319 {"Prefetch.PrefetchPotentialCandidateServingResult."
1320 "PerMatchingCandidate.",
1321 GetMetricsSuffixTriggerTypeAndEagerness(
1322 prefetch_type, /*embedder_histogram_suffix=*/std::nullopt)}),
1323 PrefetchPotentialCandidateServingResult::kServed, 1);
kenoss57793e42024-08-16 20:00:271324}
1325
kenossbd9a3fc2025-03-28 05:15:571326TEST_P(PrefetchServiceTest, SuccessCase_Browser) {
Wayne Jackson Jr.88f3db62025-01-07 12:53:341327 base::HistogramTester histogram_tester;
1328 MakePrefetchService(
1329 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1330 /*num_on_prefetch_likely_calls=*/std::nullopt));
1331
1332 net::HttpRequestHeaders request_additional_headers = {};
1333 request_additional_headers.SetHeader("foo", "bar");
1334 request_additional_headers.SetHeader("foo1", "bar1");
1335
1336 std::unique_ptr<ProbePrefetchRequestStatusListener> probe_listener =
1337 std::make_unique<ProbePrefetchRequestStatusListener>();
1338
1339 std::unique_ptr<content::PrefetchRequestStatusListener>
1340 request_status_listener =
1341 std::make_unique<TestablePrefetchRequestStatusListener>(
1342 probe_listener->GetWeakPtr());
1343
elabadysayedab1b97c02025-02-13 11:06:261344 std::unique_ptr<content::PrefetchHandle> handle =
1345 MakePrefetchFromBrowserContext(GURL("https://example.com?b=1"),
1346 std::nullopt, request_additional_headers,
1347 std::move(request_status_listener));
Wayne Jackson Jr.88f3db62025-01-07 12:53:341348 task_environment()->RunUntilIdle();
1349
Taiyo Mizuhashid13f8ca2025-06-23 13:29:021350 VerifyCommonRequestStateForBrowserContextPrefetch(
Taiyo Mizuhashi2bef6682025-03-07 08:58:451351 GURL("https://example.com?b=1"),
1352 {.use_prefetch_proxy = false,
1353 .additional_headers = request_additional_headers});
Wayne Jackson Jr.88f3db62025-01-07 12:53:341354
1355 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1356 EXPECT_FALSE(probe_listener->GetPrefetchResponseCompletedCalled());
1357 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1358 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1359
1360 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
1361 /*use_prefetch_proxy=*/false,
1362 {{"X-Testing", "Hello World"}}, kHTMLBody);
1363
1364 histogram_tester.ExpectUniqueSample(
1365 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
1366 histogram_tester.ExpectUniqueSample(
1367 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
1368 histogram_tester.ExpectUniqueSample(
1369 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
1370 histogram_tester.ExpectUniqueSample(
1371 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
1372 histogram_tester.ExpectUniqueSample(
1373 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
1374 histogram_tester.ExpectUniqueSample(
1375 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
1376
1377 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1378 EXPECT_TRUE(probe_listener->GetPrefetchResponseCompletedCalled());
1379 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1380 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1381
1382 NavigateInitiatedByBrowser(GURL("https://example.com?b=1"));
1383
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571384 PrefetchServingHandle serving_handle =
Wayne Jackson Jr.88f3db62025-01-07 12:53:341385 GetPrefetchToServe(GURL("https://example.com?b=1"), std::nullopt);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571386 ExpectServingReaderSuccess(serving_handle);
1387 EXPECT_EQ(serving_handle.GetPrefetchContainer()->GetURL(),
Wayne Jackson Jr.88f3db62025-01-07 12:53:341388 GURL("https://example.com/?b=1"));
Taiyo Mizuhashi43066071f2025-04-25 23:40:221389
1390 histogram_tester.ExpectUniqueSample(
1391 base::StringPrintf(
1392 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
1393 GetMetricsSuffixTriggerTypeAndEagerness(
1394 PrefetchType(PreloadingTriggerType::kEmbedder,
1395 /*use_prefetch_proxy=*/false),
1396 test::kPreloadingEmbedderHistgramSuffixForTesting)),
1397 false, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:541398
1399 histogram_tester.ExpectUniqueSample(
1400 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
1401 "PerMatchingCandidate.",
1402 GetMetricsSuffixTriggerTypeAndEagerness(
1403 PrefetchType(PreloadingTriggerType::kEmbedder,
1404 /*use_prefetch_proxy=*/false),
1405 test::kPreloadingEmbedderHistgramSuffixForTesting)}),
1406 PrefetchPotentialCandidateServingResult::kServed, 1);
Wayne Jackson Jr.88f3db62025-01-07 12:53:341407}
1408
kenossbd9a3fc2025-03-28 05:15:571409TEST_P(PrefetchServiceTest, SuccessCase_Browser_NoVarySearch) {
Wayne Jackson Jr.88f3db62025-01-07 12:53:341410 base::HistogramTester histogram_tester;
1411 MakePrefetchService(
1412 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1413 /*num_on_prefetch_likely_calls=*/std::nullopt));
1414
1415 net::HttpRequestHeaders request_additional_headers = {};
1416 request_additional_headers.SetHeader("foo", "bar");
1417 request_additional_headers.SetHeader("foo1", "bar1");
1418
1419 std::unique_ptr<ProbePrefetchRequestStatusListener> probe_listener =
1420 std::make_unique<ProbePrefetchRequestStatusListener>();
1421
1422 std::unique_ptr<content::PrefetchRequestStatusListener>
1423 request_status_listener =
1424 std::make_unique<TestablePrefetchRequestStatusListener>(
1425 probe_listener->GetWeakPtr());
1426
1427 net::HttpNoVarySearchData nvs_data =
1428 net::HttpNoVarySearchData::CreateFromNoVaryParams({"a"}, false);
elabadysayedab1b97c02025-02-13 11:06:261429 std::unique_ptr<content::PrefetchHandle> handle =
1430 MakePrefetchFromBrowserContext(GURL("https://example.com?a=1"), nvs_data,
1431 request_additional_headers,
1432 std::move(request_status_listener));
Wayne Jackson Jr.88f3db62025-01-07 12:53:341433 task_environment()->RunUntilIdle();
1434
Taiyo Mizuhashid13f8ca2025-06-23 13:29:021435 VerifyCommonRequestStateForBrowserContextPrefetch(
Taiyo Mizuhashi2bef6682025-03-07 08:58:451436 GURL("https://example.com?a=1"),
1437 {.use_prefetch_proxy = false,
1438 .additional_headers = request_additional_headers});
Wayne Jackson Jr.88f3db62025-01-07 12:53:341439
1440 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1441 EXPECT_FALSE(probe_listener->GetPrefetchResponseCompletedCalled());
1442 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1443 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1444
1445 MakeResponseAndWait(
1446 net::HTTP_OK, net::OK, kHTMLMimeType,
1447 /*use_prefetch_proxy=*/false,
1448 {{"X-Testing", "Hello World"}, {"No-Vary-Search", R"(params=("a"))"}},
1449 kHTMLBody);
1450
1451 histogram_tester.ExpectUniqueSample(
1452 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
1453 histogram_tester.ExpectUniqueSample(
1454 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
1455 histogram_tester.ExpectUniqueSample(
1456 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
1457 histogram_tester.ExpectUniqueSample(
1458 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
1459 histogram_tester.ExpectUniqueSample(
1460 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
1461 histogram_tester.ExpectUniqueSample(
1462 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
1463
1464 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1465 EXPECT_TRUE(probe_listener->GetPrefetchResponseCompletedCalled());
1466 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1467 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1468
1469 NavigateInitiatedByBrowser(GURL("https://example.com"));
1470
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571471 PrefetchServingHandle serving_handle =
Wayne Jackson Jr.88f3db62025-01-07 12:53:341472 GetPrefetchToServe(GURL("https://example.com"), std::nullopt);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571473 ExpectServingReaderSuccess(serving_handle);
1474 EXPECT_EQ(serving_handle.GetPrefetchContainer()->GetURL(),
Wayne Jackson Jr.88f3db62025-01-07 12:53:341475 GURL("https://example.com/?a=1"));
1476}
1477
kenossbd9a3fc2025-03-28 05:15:571478TEST_P(PrefetchServiceTest, FailureCase_Browser_ServerErrorResponseCode) {
Wayne Jackson Jr.88f3db62025-01-07 12:53:341479 base::HistogramTester histogram_tester;
1480 MakePrefetchService(
1481 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1482 /*num_on_prefetch_likely_calls=*/std::nullopt));
1483
1484 std::unique_ptr<ProbePrefetchRequestStatusListener> probe_listener =
1485 std::make_unique<ProbePrefetchRequestStatusListener>();
1486
1487 std::unique_ptr<content::PrefetchRequestStatusListener>
1488 request_status_listener =
1489 std::make_unique<TestablePrefetchRequestStatusListener>(
1490 probe_listener->GetWeakPtr());
1491
elabadysayedab1b97c02025-02-13 11:06:261492 std::unique_ptr<content::PrefetchHandle> handle =
1493 MakePrefetchFromBrowserContext(GURL("https://example.com?b=1"),
1494 std::nullopt, {},
1495 std::move(request_status_listener));
Wayne Jackson Jr.88f3db62025-01-07 12:53:341496 task_environment()->RunUntilIdle();
1497
Taiyo Mizuhashid13f8ca2025-06-23 13:29:021498 VerifyCommonRequestStateForBrowserContextPrefetch(
1499 GURL("https://example.com?b=1"), {.use_prefetch_proxy = false});
Wayne Jackson Jr.88f3db62025-01-07 12:53:341500
1501 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1502 EXPECT_FALSE(probe_listener->GetPrefetchResponseCompletedCalled());
1503 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1504 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1505
1506 MakeResponseAndWait(net::HTTP_INTERNAL_SERVER_ERROR, net::OK, kHTMLMimeType,
1507 /*use_prefetch_proxy=*/false, {}, kHTMLBodyServerError);
1508
1509 histogram_tester.ExpectUniqueSample(
1510 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
1511 histogram_tester.ExpectUniqueSample(
1512 "PrefetchProxy.Prefetch.Mainframe.RespCode",
1513 net::HTTP_INTERNAL_SERVER_ERROR, 1);
1514 histogram_tester.ExpectUniqueSample(
1515 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
1516 histogram_tester.ExpectUniqueSample(
1517 "PrefetchProxy.Prefetch.Mainframe.BodyLength",
1518 std::size(kHTMLBodyServerError), 1);
1519 histogram_tester.ExpectUniqueSample(
1520 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
1521 histogram_tester.ExpectUniqueSample(
1522 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
1523
1524 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1525 EXPECT_FALSE(probe_listener->GetPrefetchResponseCompletedCalled());
1526 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1527 EXPECT_TRUE(probe_listener->GetPrefetchResponseServerErrorCalled());
1528
1529 NavigateInitiatedByBrowser(GURL("https://example.com?b=1"));
1530 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com?b=1")));
1531}
1532
kenossbd9a3fc2025-03-28 05:15:571533TEST_P(PrefetchServiceTest, FailureCase_Browser_NetError) {
Wayne Jackson Jr.88f3db62025-01-07 12:53:341534 base::HistogramTester histogram_tester;
1535 MakePrefetchService(
1536 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1537 /*num_on_prefetch_likely_calls=*/std::nullopt));
1538
1539 std::unique_ptr<ProbePrefetchRequestStatusListener> probe_listener =
1540 std::make_unique<ProbePrefetchRequestStatusListener>();
1541
1542 std::unique_ptr<content::PrefetchRequestStatusListener>
1543 request_status_listener =
1544 std::make_unique<TestablePrefetchRequestStatusListener>(
1545 probe_listener->GetWeakPtr());
1546
1547 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1548 EXPECT_FALSE(probe_listener->GetPrefetchResponseCompletedCalled());
1549 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1550 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1551
elabadysayedab1b97c02025-02-13 11:06:261552 std::unique_ptr<content::PrefetchHandle> handle =
1553 MakePrefetchFromBrowserContext(GURL("https://example.com?c=1"),
1554 std::nullopt, {},
1555 std::move(request_status_listener));
Wayne Jackson Jr.88f3db62025-01-07 12:53:341556 task_environment()->RunUntilIdle();
1557
Taiyo Mizuhashid13f8ca2025-06-23 13:29:021558 VerifyCommonRequestStateForBrowserContextPrefetch(
1559 GURL("https://example.com?c=1"), {.use_prefetch_proxy = false});
Wayne Jackson Jr.88f3db62025-01-07 12:53:341560 MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED, kHTMLMimeType,
1561 /*use_prefetch_proxy=*/false,
1562 {{"X-Testing", "Hello World"}}, kHTMLBody);
1563
1564 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1565 EXPECT_FALSE(probe_listener->GetPrefetchResponseCompletedCalled());
1566 EXPECT_TRUE(probe_listener->GetPrefetchResponseErrorCalled());
1567 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1568
1569 ExpectPrefetchFailedNetError(histogram_tester, net::ERR_FAILED,
Takashi Nakayama978f0a152025-06-17 08:26:251570 blink::mojom::SpeculationEagerness::kImmediate,
Wayne Jackson Jr.88f3db62025-01-07 12:53:341571 /*is_accurate_triggering=*/false,
1572 /*browser_initiated_prefetch=*/true);
1573
1574 NavigateInitiatedByBrowser(GURL("https://example.com?c=1"));
1575 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com?c=1")));
1576}
1577
kenossbd9a3fc2025-03-28 05:15:571578TEST_P(PrefetchServiceTest, FailureCase_Browser_NotEligibleNonHttps) {
Wayne Jackson Jr.88f3db62025-01-07 12:53:341579 base::HistogramTester histogram_tester;
1580 MakePrefetchService(
1581 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1582 /*num_on_prefetch_likely_calls=*/std::nullopt));
1583
1584 std::unique_ptr<ProbePrefetchRequestStatusListener> probe_listener =
1585 std::make_unique<ProbePrefetchRequestStatusListener>();
1586
1587 std::unique_ptr<content::PrefetchRequestStatusListener>
1588 request_status_listener =
1589 std::make_unique<TestablePrefetchRequestStatusListener>(
1590 probe_listener->GetWeakPtr());
1591
1592 EXPECT_FALSE(probe_listener->GetPrefetchStartFailedCalled());
1593 EXPECT_FALSE(probe_listener->GetPrefetchResponseCompletedCalled());
1594 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1595 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1596
elabadysayedab1b97c02025-02-13 11:06:261597 std::unique_ptr<content::PrefetchHandle> handle =
1598 MakePrefetchFromBrowserContext(GURL("http://example.com"), std::nullopt,
1599 {}, std::move(request_status_listener));
Wayne Jackson Jr.88f3db62025-01-07 12:53:341600 task_environment()->RunUntilIdle();
1601
1602 EXPECT_TRUE(probe_listener->GetPrefetchStartFailedCalled());
1603 EXPECT_FALSE(probe_listener->GetPrefetchResponseCompletedCalled());
1604 EXPECT_FALSE(probe_listener->GetPrefetchResponseErrorCalled());
1605 EXPECT_FALSE(probe_listener->GetPrefetchResponseServerErrorCalled());
1606
1607 EXPECT_EQ(RequestCount(), 0);
1608
1609 histogram_tester.ExpectUniqueSample(
1610 "Preloading.Prefetch.PrefetchStatus",
1611 PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps, 1);
1612 ExpectPrefetchNotEligible(
1613 histogram_tester, PreloadingEligibility::kSchemeIsNotHttps,
1614 /*is_accurate=*/false, /*browser_initiated_prefetch=*/true);
1615
1616 NavigateInitiatedByBrowser(GURL("http://example.com"));
1617 EXPECT_FALSE(GetPrefetchToServe(GURL("http://example.com")));
1618}
1619
kenossbd9a3fc2025-03-28 05:15:571620TEST_P(PrefetchServiceTest, BrowserContextPrefetchRespectsTTL) {
elabadysayed909e1a32025-02-13 11:06:071621 base::HistogramTester histogram_tester;
1622 MakePrefetchService(
1623 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1624 /*num_on_prefetch_likely_calls=*/std::nullopt));
1625
1626 net::HttpRequestHeaders request_additional_headers = {};
1627 request_additional_headers.SetHeader("foo", "bar");
1628 request_additional_headers.SetHeader("foo1", "bar1");
1629
1630 std::unique_ptr<ProbePrefetchRequestStatusListener> probe_listener =
1631 std::make_unique<ProbePrefetchRequestStatusListener>();
1632
1633 std::unique_ptr<content::PrefetchRequestStatusListener>
1634 request_status_listener =
1635 std::make_unique<TestablePrefetchRequestStatusListener>(
1636 probe_listener->GetWeakPtr());
1637
elabadysayedab1b97c02025-02-13 11:06:261638 std::unique_ptr<content::PrefetchHandle> handle =
1639 MakePrefetchFromBrowserContext(GURL("https://example.com?b=1"),
1640 std::nullopt, request_additional_headers,
1641 std::move(request_status_listener),
1642 base::Minutes(5));
elabadysayed909e1a32025-02-13 11:06:071643 task_environment()->RunUntilIdle();
1644
Taiyo Mizuhashid13f8ca2025-06-23 13:29:021645 VerifyCommonRequestStateForBrowserContextPrefetch(
Taiyo Mizuhashi2bef6682025-03-07 08:58:451646 GURL("https://example.com?b=1"),
1647 {.use_prefetch_proxy = false,
1648 .additional_headers = request_additional_headers});
elabadysayed909e1a32025-02-13 11:06:071649
1650 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
1651 /*use_prefetch_proxy=*/false,
1652 {{"X-Testing", "Hello World"}}, kHTMLBody);
1653 NavigateInitiatedByBrowser(GURL("https://example.com?b=1"));
1654
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571655 PrefetchServingHandle serving_handle =
elabadysayed909e1a32025-02-13 11:06:071656 GetPrefetchToServe(GURL("https://example.com?b=1"), std::nullopt);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571657 EXPECT_EQ(serving_handle.GetPrefetchContainer()->GetURL(),
elabadysayed909e1a32025-02-13 11:06:071658 GURL("https://example.com?b=1"));
1659
1660 task_environment()->FastForwardBy(base::Minutes(5));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571661 EXPECT_FALSE(serving_handle);
elabadysayed909e1a32025-02-13 11:06:071662}
1663
kenossbd9a3fc2025-03-28 05:15:571664TEST_P(PrefetchServiceTest, PrefetchDoesNotMatchIfDocumentTokenDoesNotMatch) {
kenoss972e8682024-09-05 00:20:141665 base::HistogramTester histogram_tester;
1666
1667 MakePrefetchService(
1668 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
1669
1670 MakePrefetchOnMainFrame(
1671 GURL("https://example.com"),
1672 PrefetchType(PreloadingTriggerType::kSpeculationRule,
1673 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:251674 blink::mojom::SpeculationEagerness::kImmediate));
kenoss972e8682024-09-05 00:20:141675 task_environment()->RunUntilIdle();
1676
1677 VerifyCommonRequestState(GURL("https://example.com"),
1678 {.use_prefetch_proxy = true});
1679 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
1680 /*use_prefetch_proxy=*/true,
1681 {{"X-Testing", "Hello World"}}, kHTMLBody);
1682
1683 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
1684
1685 NavigateInitiatedByRenderer(GURL("https://example.com"));
1686
1687 // No servable PrefetchContainer is returned for different DocumentToken.
1688 blink::DocumentToken different_document_token;
1689 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com"),
1690 different_document_token));
1691}
1692
kenossbd9a3fc2025-03-28 05:15:571693TEST_P(PrefetchServiceTest, SuccessCase_Embedder) {
kenoss972e8682024-09-05 00:20:141694 base::HistogramTester histogram_tester;
1695 MakePrefetchService(
1696 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1697 /*num_on_prefetch_likely_calls=*/std::nullopt));
Taiyo Mizuhashi43066071f2025-04-25 23:40:221698 const PrefetchType prefetch_type = PrefetchType(
1699 PreloadingTriggerType::kEmbedder, /*use_prefetch_proxy=*/false);
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:001700 auto handle =
Taiyo Mizuhashi43066071f2025-04-25 23:40:221701 MakePrefetchFromEmbedder(GURL("https://example.com"), prefetch_type);
kenoss972e8682024-09-05 00:20:141702 task_environment()->RunUntilIdle();
1703
Taiyo Mizuhashid13f8ca2025-06-23 13:29:021704 VerifyCommonRequestStateForWebContentsPrefetch(GURL("https://example.com"),
1705 {.use_prefetch_proxy = false});
kenoss972e8682024-09-05 00:20:141706 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
Taiyo Mizuhashi9dfeb632024-10-24 08:44:201707 /*use_prefetch_proxy=*/false,
kenoss972e8682024-09-05 00:20:141708 {{"X-Testing", "Hello World"}}, kHTMLBody);
1709
1710 // Verify that the prefetch request was successful.
1711 // TODO(crbug.com/40269462): Revise current helper functions (ExpectPrefetch*)
1712 // for browser-initiated prefetch.
1713 histogram_tester.ExpectUniqueSample(
1714 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
1715 histogram_tester.ExpectUniqueSample(
1716 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
1717 histogram_tester.ExpectUniqueSample(
1718 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
1719 histogram_tester.ExpectUniqueSample(
1720 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
1721 histogram_tester.ExpectUniqueSample(
1722 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
1723 histogram_tester.ExpectUniqueSample(
1724 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
1725
1726 NavigateInitiatedByBrowser(GURL("https://example.com"));
1727
1728 ExpectServingReaderSuccess(
1729 GetPrefetchToServe(GURL("https://example.com"), std::nullopt));
1730
1731 histogram_tester.ExpectUniqueSample(
1732 "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
Taiyo Mizuhashi43066071f2025-04-25 23:40:221733 histogram_tester.ExpectUniqueSample(
1734 base::StringPrintf(
1735 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
1736 GetMetricsSuffixTriggerTypeAndEagerness(
1737 prefetch_type,
1738 test::kPreloadingEmbedderHistgramSuffixForTesting)),
1739 false, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:541740
1741 histogram_tester.ExpectUniqueSample(
1742 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
1743 "PerMatchingCandidate.",
1744 GetMetricsSuffixTriggerTypeAndEagerness(
1745 prefetch_type,
1746 test::kPreloadingEmbedderHistgramSuffixForTesting)}),
1747 PrefetchPotentialCandidateServingResult::kServed, 1);
kenoss972e8682024-09-05 00:20:141748}
1749
kenossbd9a3fc2025-03-28 05:15:571750TEST_P(PrefetchServiceTest,
kenoss972e8682024-09-05 00:20:141751 PrefetchDoesNotMatchIfDocumentTokenDoesNotMatch_Embedder) {
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371752 base::HistogramTester histogram_tester;
1753 MakePrefetchService(
1754 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1755 /*num_on_prefetch_likely_calls=*/std::nullopt));
1756
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:001757 auto handle =
1758 MakePrefetchFromEmbedder(GURL("https://example.com"),
1759 PrefetchType(PreloadingTriggerType::kEmbedder,
1760 /*use_prefetch_proxy=*/false));
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371761 task_environment()->RunUntilIdle();
1762
Taiyo Mizuhashid13f8ca2025-06-23 13:29:021763 VerifyCommonRequestStateForWebContentsPrefetch(GURL("https://example.com"),
1764 {.use_prefetch_proxy = false});
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371765 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
Taiyo Mizuhashi9dfeb632024-10-24 08:44:201766 /*use_prefetch_proxy=*/false,
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371767 {{"X-Testing", "Hello World"}}, kHTMLBody);
1768
1769 // Verify that the prefetch request was successful.
Alison Gale81f4f2c72024-04-22 19:33:311770 // TODO(crbug.com/40269462): Revise current helper functions (ExpectPrefetch*)
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371771 // for browser-initiated prefetch.
1772 histogram_tester.ExpectUniqueSample(
1773 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
1774 histogram_tester.ExpectUniqueSample(
1775 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
1776 histogram_tester.ExpectUniqueSample(
1777 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
1778 histogram_tester.ExpectUniqueSample(
1779 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
1780 histogram_tester.ExpectUniqueSample(
1781 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
1782 histogram_tester.ExpectUniqueSample(
1783 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
1784
1785 NavigateInitiatedByBrowser(GURL("https://example.com"));
1786
kenossf6fbd5652024-09-04 00:40:281787 // No servable PrefetchContainer is returned for different DocumentToken.
1788 EXPECT_FALSE(
1789 GetPrefetchToServe(GURL("https://example.com"), MainDocumentToken()));
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371790}
1791
kenossbd9a3fc2025-03-28 05:15:571792TEST_P(PrefetchServiceTest, NoPrefetchingPreloadingDisabled) {
Max Curranead64a62022-06-22 01:10:521793 base::HistogramTester histogram_tester;
1794
1795 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
Max Curran210cffa2022-09-06 22:24:311796 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1797 /*num_on_prefetch_likely_calls=*/0);
Max Curranead64a62022-06-22 01:10:521798
1799 // When preloading is disabled, then |PrefetchService| doesn't take the
1800 // prefetch at all.
1801 EXPECT_CALL(*mock_prefetch_service_delegate, IsSomePreloadingEnabled)
1802 .Times(1)
Johann41a60832023-01-25 16:20:331803 .WillOnce(testing::Return(PreloadingEligibility::kPreloadingDisabled));
Max Curranead64a62022-06-22 01:10:521804
1805 MakePrefetchService(std::move(mock_prefetch_service_delegate));
1806
Max Curran2e83b5d2022-12-29 18:53:381807 MakePrefetchOnMainFrame(
1808 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:581809 PrefetchType(PreloadingTriggerType::kSpeculationRule,
1810 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:251811 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:541812 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:521813
1814 EXPECT_EQ(RequestCount(), 0);
1815
Adithya Srinivasan4d3dad02024-10-17 18:06:591816 histogram_tester.ExpectUniqueSample(
1817 "Preloading.Prefetch.PrefetchStatus",
1818 PrefetchStatus::kPrefetchIneligiblePreloadingDisabled, 1);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461819 ExpectPrefetchNotEligible(histogram_tester,
1820 PreloadingEligibility::kPreloadingDisabled);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:361821
Taiyo Mizuhashi01d86af22024-03-27 14:27:421822 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461823 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:121824 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligiblePreloadingDisabled);
Max Curranead64a62022-06-22 01:10:521825}
1826
kenossbd9a3fc2025-03-28 05:15:571827TEST_P(PrefetchServiceTest, NoPrefetchingDomainNotInAllowList) {
Max Curranead64a62022-06-22 01:10:521828 base::HistogramTester histogram_tester;
1829
1830 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
Max Curran210cffa2022-09-06 22:24:311831 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
1832 /*num_on_prefetch_likely_calls=*/0);
Max Curranead64a62022-06-22 01:10:521833
1834 // When referring page is not in allow list, then |PrefetchService| doesn't
1835 // take the prefetch at all.
1836 EXPECT_CALL(*mock_prefetch_service_delegate,
1837 IsDomainInPrefetchAllowList(testing::_))
1838 .Times(1)
1839 .WillOnce(testing::Return(false));
1840
1841 MakePrefetchService(std::move(mock_prefetch_service_delegate));
1842
Max Curran2e83b5d2022-12-29 18:53:381843 MakePrefetchOnMainFrame(
1844 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:581845 PrefetchType(PreloadingTriggerType::kSpeculationRule,
1846 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:251847 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:541848 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:521849
1850 EXPECT_EQ(RequestCount(), 0);
1851
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461852 // `IsDomainInPrefetchAllowList` returns false so we did not reach the
1853 // eligibility check.
1854 ExpectPrefetchNotEligible(histogram_tester,
1855 PreloadingEligibility::kUnspecified);
Hiroshige Hayashizakiaf5be312023-10-23 09:27:271856
Taiyo Mizuhashi01d86af22024-03-27 14:27:421857 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizakiaf5be312023-10-23 09:27:271858 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
1859
Arthur Sonzognic686e8f2024-01-11 08:36:371860 std::optional<PrefetchServingPageMetrics> serving_page_metrics =
Max Curran210cffa2022-09-06 22:24:311861 GetMetricsForMostRecentNavigation();
1862 ASSERT_TRUE(serving_page_metrics);
1863 EXPECT_FALSE(serving_page_metrics->prefetch_status);
Max Curranead64a62022-06-22 01:10:521864}
1865
kenossbd9a3fc2025-03-28 05:15:571866class PrefetchServiceAllowAllDomainsTest
1867 : public PrefetchServiceTestBase,
1868 public WithPrefetchServiceRearchParam,
1869 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
Max Curranead64a62022-06-22 01:10:521870 public:
kenossbd9a3fc2025-03-28 05:15:571871 PrefetchServiceAllowAllDomainsTest()
1872 : WithPrefetchServiceRearchParam(GetParam()) {}
1873
Max Curranead64a62022-06-22 01:10:521874 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:151875 InitBaseParams();
1876 InitRearchFeatures();
1877 // Override `kPrefetchUseContentRefactor`.
Liviu Tinta91455162022-12-14 22:06:231878 scoped_feature_list_.InitWithFeaturesAndParameters(
Johanna9fe85f2023-01-17 10:15:431879 {{features::kPrefetchUseContentRefactor,
Liviu Tinta91455162022-12-14 22:06:231880 {{"ineligible_decoy_request_probability", "0"},
1881 {"prefetch_container_lifetime_s", "-1"},
1882 {"allow_all_domains", "true"}}}},
Liviu Tinta5043cae52024-06-26 00:07:291883 {});
Max Curranead64a62022-06-22 01:10:521884 }
kenoss557d4092025-04-01 05:01:151885
1886 private:
1887 base::test::ScopedFeatureList scoped_feature_list_;
Max Curranead64a62022-06-22 01:10:521888};
1889
kenossbd9a3fc2025-03-28 05:15:571890INSTANTIATE_TEST_SUITE_P(
1891 ParametrizedTests,
1892 PrefetchServiceAllowAllDomainsTest,
1893 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
1894
1895TEST_P(PrefetchServiceAllowAllDomainsTest, AllowAllDomains) {
Max Curranead64a62022-06-22 01:10:521896 base::HistogramTester histogram_tester;
1897
1898 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
1899 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>();
1900
1901 // When "allow_all_domains" is set to true, then we can prefetch from all
1902 // domains, not just those in the allow list.
1903 EXPECT_CALL(*mock_prefetch_service_delegate,
1904 IsDomainInPrefetchAllowList(testing::_))
1905 .Times(0);
1906
1907 MakePrefetchService(std::move(mock_prefetch_service_delegate));
1908
Max Curran2e83b5d2022-12-29 18:53:381909 MakePrefetchOnMainFrame(
1910 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:581911 PrefetchType(PreloadingTriggerType::kSpeculationRule,
1912 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:251913 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:541914 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:521915
1916 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:051917 {.use_prefetch_proxy = true});
Max Curranead64a62022-06-22 01:10:521918 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
1919 /*use_prefetch_proxy=*/true,
1920 {{"X-Testing", "Hello World"}}, kHTMLBody);
1921
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461922 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:361923
Taiyo Mizuhashi01d86af22024-03-27 14:27:421924 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461925 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:361926 ExpectServingMetricsSuccess();
Max Curranead64a62022-06-22 01:10:521927}
1928
1929class PrefetchServiceAllowAllDomainsForExtendedPreloadingTest
kenossbd9a3fc2025-03-28 05:15:571930 : public PrefetchServiceTestBase,
1931 public WithPrefetchServiceRearchParam,
1932 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
Max Curranead64a62022-06-22 01:10:521933 public:
kenossbd9a3fc2025-03-28 05:15:571934 PrefetchServiceAllowAllDomainsForExtendedPreloadingTest()
1935 : WithPrefetchServiceRearchParam(GetParam()) {}
1936
Max Curranead64a62022-06-22 01:10:521937 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:151938 InitBaseParams();
1939 InitRearchFeatures();
1940 // Override `kPrefetchUseContentRefactor`.
Liviu Tinta91455162022-12-14 22:06:231941 scoped_feature_list_.InitWithFeaturesAndParameters(
Johanna9fe85f2023-01-17 10:15:431942 {{features::kPrefetchUseContentRefactor,
Liviu Tinta91455162022-12-14 22:06:231943 {{"ineligible_decoy_request_probability", "0"},
1944 {"prefetch_container_lifetime_s", "-1"},
1945 {"allow_all_domains_for_extended_preloading", "true"}}}},
Liviu Tinta5043cae52024-06-26 00:07:291946 {});
Max Curranead64a62022-06-22 01:10:521947 }
kenoss557d4092025-04-01 05:01:151948
1949 private:
1950 base::test::ScopedFeatureList scoped_feature_list_;
Max Curranead64a62022-06-22 01:10:521951};
1952
kenossbd9a3fc2025-03-28 05:15:571953INSTANTIATE_TEST_SUITE_P(
1954 ParametrizedTests,
1955 PrefetchServiceAllowAllDomainsForExtendedPreloadingTest,
1956 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
1957
1958TEST_P(PrefetchServiceAllowAllDomainsForExtendedPreloadingTest,
Max Curranead64a62022-06-22 01:10:521959 ExtendedPreloadingEnabled) {
1960 base::HistogramTester histogram_tester;
1961
1962 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
1963 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>();
1964
1965 // Allow all domains if and only if extended preloading is enabled.
1966 EXPECT_CALL(*mock_prefetch_service_delegate, IsExtendedPreloadingEnabled)
1967 .Times(1)
1968 .WillOnce(testing::Return(true));
1969 EXPECT_CALL(*mock_prefetch_service_delegate,
1970 IsDomainInPrefetchAllowList(testing::_))
1971 .Times(0);
1972
1973 MakePrefetchService(std::move(mock_prefetch_service_delegate));
1974
Max Curran2e83b5d2022-12-29 18:53:381975 MakePrefetchOnMainFrame(
1976 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:581977 PrefetchType(PreloadingTriggerType::kSpeculationRule,
1978 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:251979 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:541980 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:521981
1982 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:051983 {.use_prefetch_proxy = true});
Max Curranead64a62022-06-22 01:10:521984 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
1985 /*use_prefetch_proxy=*/true,
1986 {{"X-Testing", "Hello World"}}, kHTMLBody);
1987
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461988 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Max Curranead64a62022-06-22 01:10:521989
Taiyo Mizuhashi01d86af22024-03-27 14:27:421990 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:461991 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:361992 ExpectServingMetricsSuccess();
Max Curranead64a62022-06-22 01:10:521993}
1994
kenossbd9a3fc2025-03-28 05:15:571995TEST_P(PrefetchServiceAllowAllDomainsForExtendedPreloadingTest,
Max Curranead64a62022-06-22 01:10:521996 ExtendedPreloadingDisabled) {
1997 base::HistogramTester histogram_tester;
1998
1999 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
Max Curran210cffa2022-09-06 22:24:312000 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
2001 /*num_on_prefetch_likely_calls=*/0);
Max Curranead64a62022-06-22 01:10:522002
2003 // If extended preloading is disabled, then we check the allow list.
2004 EXPECT_CALL(*mock_prefetch_service_delegate, IsExtendedPreloadingEnabled)
2005 .Times(1)
2006 .WillOnce(testing::Return(false));
2007 EXPECT_CALL(*mock_prefetch_service_delegate,
2008 IsDomainInPrefetchAllowList(testing::_))
2009 .Times(1)
2010 .WillOnce(testing::Return(false));
2011
2012 MakePrefetchService(std::move(mock_prefetch_service_delegate));
2013
Max Curran2e83b5d2022-12-29 18:53:382014 MakePrefetchOnMainFrame(
2015 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582016 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2017 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252018 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542019 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:522020
2021 EXPECT_EQ(RequestCount(), 0);
2022
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462023 ExpectPrefetchNotEligible(histogram_tester,
2024 PreloadingEligibility::kUnspecified);
Hiroshige Hayashizakiaf5be312023-10-23 09:27:272025
Taiyo Mizuhashi01d86af22024-03-27 14:27:422026 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizakiaf5be312023-10-23 09:27:272027 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
2028
Arthur Sonzognic686e8f2024-01-11 08:36:372029 std::optional<PrefetchServingPageMetrics> serving_page_metrics =
Max Curran210cffa2022-09-06 22:24:312030 GetMetricsForMostRecentNavigation();
2031 ASSERT_TRUE(serving_page_metrics);
2032 EXPECT_FALSE(serving_page_metrics->prefetch_status);
Max Curranead64a62022-06-22 01:10:522033}
2034
kenossbd9a3fc2025-03-28 05:15:572035TEST_P(PrefetchServiceTest, NonProxiedPrefetchDoesNotRequireAllowList) {
Kevin McNee06824c72024-02-06 18:59:522036 NavigationSimulator::NavigateAndCommitFromBrowser(
2037 web_contents(), GURL("https://example.com/referrer"));
Jeremy Roman95c2194d2022-11-30 17:20:252038 base::HistogramTester histogram_tester;
2039
2040 // Assume we have a delegate which will not grant access to the proxy for this
2041 // domain. Nonetheless a non-proxied prefetch should work.
2042 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
2043 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>();
2044 ON_CALL(*mock_prefetch_service_delegate, IsExtendedPreloadingEnabled)
2045 .WillByDefault(testing::Return(false));
2046 ON_CALL(*mock_prefetch_service_delegate,
2047 IsDomainInPrefetchAllowList(testing::_))
2048 .WillByDefault(testing::Return(false));
2049
2050 MakePrefetchService(std::move(mock_prefetch_service_delegate));
2051
Max Curran2e83b5d2022-12-29 18:53:382052 MakePrefetchOnMainFrame(
2053 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582054 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2055 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:252056 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542057 task_environment()->RunUntilIdle();
Jeremy Roman95c2194d2022-11-30 17:20:252058
2059 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:052060 {.use_prefetch_proxy = false});
Jeremy Roman95c2194d2022-11-30 17:20:252061 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
2062 /*use_prefetch_proxy=*/false,
2063 {{"X-Testing", "Hello World"}}, kHTMLBody);
2064
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462065 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Jeremy Roman95c2194d2022-11-30 17:20:252066
Taiyo Mizuhashi01d86af22024-03-27 14:27:422067 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462068 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362069 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
Jeremy Roman95c2194d2022-11-30 17:20:252070}
2071
kenossbd9a3fc2025-03-28 05:15:572072TEST_P(PrefetchServiceTest, NotEligibleHostnameNonUnique) {
Max Curran6b93426f2022-05-03 00:55:522073 base::HistogramTester histogram_tester;
2074
Max Curranead64a62022-06-22 01:10:522075 MakePrefetchService(
2076 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2077
Max Curran146bf442022-03-28 23:22:142078 PrefetchService::SetHostNonUniqueFilterForTesting(
Md Hasibul Hasana963a9342024-04-03 10:15:142079 [](std::string_view) { return true; });
Max Curran146bf442022-03-28 23:22:142080
Max Curran2e83b5d2022-12-29 18:53:382081 MakePrefetchOnMainFrame(
2082 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582083 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2084 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252085 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542086 task_environment()->RunUntilIdle();
Max Curran146bf442022-03-28 23:22:142087
Max Curran18a6f2b2022-05-02 23:13:242088 EXPECT_EQ(RequestCount(), 0);
2089
Adithya Srinivasan4d3dad02024-10-17 18:06:592090 histogram_tester.ExpectUniqueSample(
2091 "Preloading.Prefetch.PrefetchStatus",
2092 PrefetchStatus::kPrefetchIneligibleHostIsNonUnique, 1);
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:212093 ExpectPrefetchNotEligible(histogram_tester,
2094 PreloadingEligibility::kHostIsNonUnique);
Max Curran6b93426f2022-05-03 00:55:522095
Taiyo Mizuhashi01d86af22024-03-27 14:27:422096 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462097 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:122098 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligibleHostIsNonUnique);
Max Curran146bf442022-03-28 23:22:142099}
2100
kenossbd9a3fc2025-03-28 05:15:572101TEST_P(PrefetchServiceTest, NotEligibleDataSaverEnabled) {
Jeremy Romanbae6a422022-05-17 22:41:172102 base::HistogramTester histogram_tester;
Max Curranead64a62022-06-22 01:10:522103
Johann41a60832023-01-25 16:20:332104 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
2105 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
2106 /*num_on_prefetch_likely_calls=*/0);
2107
2108 // When data saver is enabled, then |PrefetchService| doesn't start the
2109 // prefetch at all.
2110 EXPECT_CALL(*mock_prefetch_service_delegate, IsSomePreloadingEnabled)
2111 .Times(1)
2112 .WillOnce(testing::Return(PreloadingEligibility::kDataSaverEnabled));
2113
2114 MakePrefetchService(std::move(mock_prefetch_service_delegate));
Jeremy Romanbae6a422022-05-17 22:41:172115
Max Curran2e83b5d2022-12-29 18:53:382116 MakePrefetchOnMainFrame(
2117 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582118 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2119 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252120 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542121 task_environment()->RunUntilIdle();
Jeremy Romanbae6a422022-05-17 22:41:172122
2123 EXPECT_EQ(RequestCount(), 0);
2124
Adithya Srinivasan4d3dad02024-10-17 18:06:592125 histogram_tester.ExpectUniqueSample(
2126 "Preloading.Prefetch.PrefetchStatus",
2127 PrefetchStatus::kPrefetchIneligibleDataSaverEnabled, 1);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462128 ExpectPrefetchNotEligible(histogram_tester,
2129 PreloadingEligibility::kDataSaverEnabled);
Jeremy Romanbae6a422022-05-17 22:41:172130
Taiyo Mizuhashi01d86af22024-03-27 14:27:422131 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462132 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:122133 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligibleDataSaverEnabled);
Jeremy Romanbae6a422022-05-17 22:41:172134}
2135
kenossbd9a3fc2025-03-28 05:15:572136TEST_P(PrefetchServiceTest, NotEligibleNonHttps) {
Max Curran6b93426f2022-05-03 00:55:522137 base::HistogramTester histogram_tester;
2138
Max Curranead64a62022-06-22 01:10:522139 MakePrefetchService(
2140 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2141
Max Curran2e83b5d2022-12-29 18:53:382142 MakePrefetchOnMainFrame(
2143 GURL("http://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582144 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2145 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252146 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542147 task_environment()->RunUntilIdle();
Max Curran146bf442022-03-28 23:22:142148
Max Curran18a6f2b2022-05-02 23:13:242149 EXPECT_EQ(RequestCount(), 0);
2150
Adithya Srinivasan4d3dad02024-10-17 18:06:592151 histogram_tester.ExpectUniqueSample(
2152 "Preloading.Prefetch.PrefetchStatus",
2153 PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps, 1);
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:212154 ExpectPrefetchNotEligible(histogram_tester,
2155 PreloadingEligibility::kSchemeIsNotHttps);
Max Curran6b93426f2022-05-03 00:55:522156
Taiyo Mizuhashi01d86af22024-03-27 14:27:422157 NavigateInitiatedByRenderer(GURL("http://example.com"));
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:112158 EXPECT_FALSE(GetPrefetchToServe(GURL("http://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:122159 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps);
Max Curran146bf442022-03-28 23:22:142160}
2161
kenossbd9a3fc2025-03-28 05:15:572162TEST_P(PrefetchServiceTest, NotEligiblePrefetchProxyNotAvailable) {
Max Curran6b93426f2022-05-03 00:55:522163 base::HistogramTester histogram_tester;
2164
Max Curranead64a62022-06-22 01:10:522165 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
2166 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>();
2167
2168 // If the prefetch proxy URL is invalid, then we can't make prefetches that
2169 // require the proxy. However, non-proxied prefetches are fine.
2170 EXPECT_CALL(*mock_prefetch_service_delegate, GetDefaultPrefetchProxyHost)
2171 .Times(1)
2172 .WillOnce(testing::Return(GURL("")));
2173
2174 MakePrefetchService(std::move(mock_prefetch_service_delegate));
2175
Max Curran2e83b5d2022-12-29 18:53:382176 MakePrefetchOnMainFrame(
2177 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582178 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2179 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252180 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542181 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:522182
2183 EXPECT_EQ(RequestCount(), 0);
2184
Adithya Srinivasan4d3dad02024-10-17 18:06:592185 histogram_tester.ExpectUniqueSample(
2186 "Preloading.Prefetch.PrefetchStatus",
2187 PrefetchStatus::kPrefetchIneligiblePrefetchProxyNotAvailable, 1);
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:212188 ExpectPrefetchNotEligible(histogram_tester,
2189 PreloadingEligibility::kPrefetchProxyNotAvailable);
Max Curranead64a62022-06-22 01:10:522190
Taiyo Mizuhashi01d86af22024-03-27 14:27:422191 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462192 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:122193 ExpectServingMetrics(
2194 PrefetchStatus::kPrefetchIneligiblePrefetchProxyNotAvailable);
Max Curranead64a62022-06-22 01:10:522195}
2196
kenossbd9a3fc2025-03-28 05:15:572197TEST_P(PrefetchServiceTest,
Max Curranead64a62022-06-22 01:10:522198 EligiblePrefetchProxyNotAvailableNonProxiedPrefetch) {
2199 base::HistogramTester histogram_tester;
2200
2201 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
2202 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>();
2203
2204 // If the prefetch proxy URL is invalid, then we can't make prefetches that
2205 // require the proxy. However, non-proxied prefetches are fine.
2206 EXPECT_CALL(*mock_prefetch_service_delegate, GetDefaultPrefetchProxyHost)
2207 .Times(1)
2208 .WillOnce(testing::Return(GURL("")));
2209
2210 MakePrefetchService(std::move(mock_prefetch_service_delegate));
2211
Max Curran2e83b5d2022-12-29 18:53:382212 MakePrefetchOnMainFrame(
2213 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582214 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2215 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:252216 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542217 task_environment()->RunUntilIdle();
Max Curran146bf442022-03-28 23:22:142218
Jeremy Roman4c9524212023-07-27 22:01:052219 VerifyCommonRequestState(GURL("https://example.com"));
Max Curran18a6f2b2022-05-02 23:13:242220 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
Max Curran6b93426f2022-05-03 00:55:522221 /*use_prefetch_proxy=*/false,
Max Curran18a6f2b2022-05-02 23:13:242222 {{"X-Testing", "Hello World"}}, kHTMLBody);
2223
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462224 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Max Curran6b93426f2022-05-03 00:55:522225
Taiyo Mizuhashi01d86af22024-03-27 14:27:422226 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462227 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362228 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
Max Curranead64a62022-06-22 01:10:522229}
2230
kenossbd9a3fc2025-03-28 05:15:572231TEST_P(PrefetchServiceTest, NotEligibleOriginWithinRetryAfterWindow) {
Max Curranead64a62022-06-22 01:10:522232 base::HistogramTester histogram_tester;
2233
2234 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
2235 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>();
2236
2237 EXPECT_CALL(*mock_prefetch_service_delegate,
2238 IsOriginOutsideRetryAfterWindow(GURL("https://example.com")))
2239 .Times(1)
Max Curran02153242022-07-15 17:40:042240 .WillOnce(testing::Return(false));
Max Curranead64a62022-06-22 01:10:522241
2242 MakePrefetchService(std::move(mock_prefetch_service_delegate));
2243
Max Curran2e83b5d2022-12-29 18:53:382244 MakePrefetchOnMainFrame(
2245 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582246 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2247 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252248 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542249 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:522250
2251 EXPECT_EQ(RequestCount(), 0);
2252
Adithya Srinivasan4d3dad02024-10-17 18:06:592253 histogram_tester.ExpectUniqueSample(
2254 "Preloading.Prefetch.PrefetchStatus",
2255 PrefetchStatus::kPrefetchIneligibleRetryAfter, 1);
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:212256 ExpectPrefetchNotEligible(histogram_tester,
2257 PreloadingEligibility::kRetryAfter);
Max Curranead64a62022-06-22 01:10:522258
Taiyo Mizuhashi01d86af22024-03-27 14:27:422259 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462260 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362261 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligibleRetryAfter);
Max Curranead64a62022-06-22 01:10:522262}
2263
kenossbd9a3fc2025-03-28 05:15:572264TEST_P(PrefetchServiceTest, EligibleNonHttpsNonProxiedPotentiallyTrustworthy) {
Max Curranead64a62022-06-22 01:10:522265 base::HistogramTester histogram_tester;
2266
2267 MakePrefetchService(
2268 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2269
Max Curran2e83b5d2022-12-29 18:53:382270 MakePrefetchOnMainFrame(
2271 GURL("https://localhost"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582272 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2273 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:252274 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542275 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:522276
Jeremy Roman4c9524212023-07-27 22:01:052277 VerifyCommonRequestState(GURL("https://localhost"));
Max Curranead64a62022-06-22 01:10:522278 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
2279 /*use_prefetch_proxy=*/false,
2280 {{"X-Testing", "Hello World"}}, kHTMLBody);
2281
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462282 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362283
Taiyo Mizuhashi01d86af22024-03-27 14:27:422284 NavigateInitiatedByRenderer(GURL("https://localhost"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462285 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://localhost")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362286 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
Max Curran146bf442022-03-28 23:22:142287}
2288
kenossbd9a3fc2025-03-28 05:15:572289TEST_P(PrefetchServiceTest, NotEligibleServiceWorkerRegistered) {
Max Curran6b93426f2022-05-03 00:55:522290 base::HistogramTester histogram_tester;
2291
Max Curranead64a62022-06-22 01:10:522292 MakePrefetchService(
2293 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2294
Liviu Tinta91202302023-09-26 17:49:112295 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
Ari Chivukulac81e13e2023-02-15 20:44:572296 blink::StorageKey::CreateFromStringForTesting("https://example.com"));
Liviu Tinta91202302023-09-26 17:49:112297 service_worker_context_->AddServiceWorkerScope(
Liviu Tintaf7d62a72023-08-29 21:48:392298 GURL("https://example.com"),
2299 ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER);
Max Curran146bf442022-03-28 23:22:142300
Max Curran2e83b5d2022-12-29 18:53:382301 MakePrefetchOnMainFrame(
2302 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582303 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2304 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252305 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542306 task_environment()->RunUntilIdle();
Max Curran146bf442022-03-28 23:22:142307
Max Curran18a6f2b2022-05-02 23:13:242308 EXPECT_EQ(RequestCount(), 0);
2309
Adithya Srinivasan4d3dad02024-10-17 18:06:592310 histogram_tester.ExpectUniqueSample(
2311 "Preloading.Prefetch.PrefetchStatus",
2312 PrefetchStatus::kPrefetchIneligibleUserHasServiceWorker, 1);
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:212313 ExpectPrefetchNotEligible(histogram_tester,
2314 PreloadingEligibility::kUserHasServiceWorker);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362315
Taiyo Mizuhashi01d86af22024-03-27 14:27:422316 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362317 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:122318 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligibleUserHasServiceWorker);
Max Curran146bf442022-03-28 23:22:142319}
2320
kenossbd9a3fc2025-03-28 05:15:572321TEST_P(PrefetchServiceTest,
Liviu Tinta91202302023-09-26 17:49:112322 NotEligibleServiceWorkerRegisteredServiceWorkerCheckUKM) {
2323 // ukm::TestAutoSetUkmRecorder ukm_recorder;
2324 MakePrefetchService(
2325 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2326
2327 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
2328 blink::StorageKey::CreateFromStringForTesting("https://example.com"));
2329 service_worker_context_->AddServiceWorkerScope(
2330 GURL("https://example.com"),
2331 ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER);
2332 service_worker_context_->SetServiceWorkerCheckDuration(
2333 base::Microseconds(kServiceWorkerCheckDuration));
2334
2335 MakePrefetchOnMainFrame(
2336 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582337 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2338 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:252339 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542340 task_environment()->RunUntilIdle();
Liviu Tinta91202302023-09-26 17:49:112341
2342 EXPECT_EQ(RequestCount(), 0);
2343
Taiyo Mizuhashi01d86af22024-03-27 14:27:422344 NavigateInitiatedByRenderer(GURL("https://example.com"));
Liviu Tinta91202302023-09-26 17:49:112345
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462346 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Liviu Tinta91202302023-09-26 17:49:112347
2348 ForceLogsUploadAndGetUkmId();
2349 // Now check the UKM records.
2350 using UkmEntry = ukm::builders::Preloading_Attempt;
2351 auto actual_attempts = test_ukm_recorder()->GetEntries(
2352 UkmEntry::kEntryName,
2353 {
2354 UkmEntry::kPrefetchServiceWorkerRegisteredCheckName,
2355 UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName,
2356 });
2357 EXPECT_EQ(actual_attempts.size(), 1u);
2358 ASSERT_TRUE(actual_attempts[0].metrics.count(
2359 UkmEntry::kPrefetchServiceWorkerRegisteredCheckName));
2360 EXPECT_EQ(actual_attempts[0]
2361 .metrics[UkmEntry::kPrefetchServiceWorkerRegisteredCheckName],
2362 static_cast<int64_t>(
2363 PreloadingAttemptImpl::ServiceWorkerRegisteredCheck::kPath));
2364 ASSERT_TRUE(actual_attempts[0].metrics.count(
2365 UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName));
2366 EXPECT_EQ(
2367 actual_attempts[0].metrics
2368 [UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName],
2369 ukm::GetExponentialBucketMin(
2370 kServiceWorkerCheckDuration,
2371 PreloadingAttemptImpl::
2372 kServiceWorkerRegisteredCheckDurationBucketSpacing));
2373}
2374
kenossbd9a3fc2025-03-28 05:15:572375TEST_P(PrefetchServiceTest, EligibleServiceWorkerNotRegistered) {
Max Curran6b93426f2022-05-03 00:55:522376 base::HistogramTester histogram_tester;
2377
Max Curranead64a62022-06-22 01:10:522378 MakePrefetchService(
2379 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2380
Liviu Tinta91202302023-09-26 17:49:112381 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
Ari Chivukulac81e13e2023-02-15 20:44:572382 blink::StorageKey::CreateFromStringForTesting("https://other.com"));
Liviu Tinta91202302023-09-26 17:49:112383 service_worker_context_->AddServiceWorkerScope(
Liviu Tintaf7d62a72023-08-29 21:48:392384 GURL("https://other.com"),
2385 ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER);
Max Curran146bf442022-03-28 23:22:142386
Max Curran2e83b5d2022-12-29 18:53:382387 MakePrefetchOnMainFrame(
2388 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582389 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2390 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252391 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542392 task_environment()->RunUntilIdle();
Max Curran146bf442022-03-28 23:22:142393
Max Curran18a6f2b2022-05-02 23:13:242394 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:052395 {.use_prefetch_proxy = true});
Max Curran18a6f2b2022-05-02 23:13:242396 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
Max Curran6b93426f2022-05-03 00:55:522397 /*use_prefetch_proxy=*/true,
Max Curran18a6f2b2022-05-02 23:13:242398 {{"X-Testing", "Hello World"}}, kHTMLBody);
2399
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462400 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Max Curran6b93426f2022-05-03 00:55:522401
Taiyo Mizuhashi01d86af22024-03-27 14:27:422402 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462403 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362404 ExpectServingMetricsSuccess();
Max Curran146bf442022-03-28 23:22:142405}
2406
kenossbd9a3fc2025-03-28 05:15:572407TEST_P(PrefetchServiceTest,
Liviu Tinta91202302023-09-26 17:49:112408 EligibleServiceWorkerNotRegisteredServiceWorkerCheckUKM) {
2409 MakePrefetchService(
2410 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2411
2412 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
2413 blink::StorageKey::CreateFromStringForTesting("https://other.com"));
2414 service_worker_context_->AddServiceWorkerScope(
2415 GURL("https://other.com"),
2416 ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER);
2417
2418 MakePrefetchOnMainFrame(
2419 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582420 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2421 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252422 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542423 task_environment()->RunUntilIdle();
Liviu Tinta91202302023-09-26 17:49:112424
2425 VerifyCommonRequestState(GURL("https://example.com"),
2426 {.use_prefetch_proxy = true});
2427 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
2428 /*use_prefetch_proxy=*/true,
2429 {{"X-Testing", "Hello World"}}, kHTMLBody);
2430
Taiyo Mizuhashi01d86af22024-03-27 14:27:422431 NavigateInitiatedByRenderer(GURL("https://example.com"));
Liviu Tinta91202302023-09-26 17:49:112432
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462433 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Liviu Tinta91202302023-09-26 17:49:112434
2435 ForceLogsUploadAndGetUkmId();
2436 // Now check the UKM records.
2437 using UkmEntry = ukm::builders::Preloading_Attempt;
2438 auto actual_attempts = test_ukm_recorder()->GetEntries(
2439 UkmEntry::kEntryName,
2440 {
2441 UkmEntry::kPrefetchServiceWorkerRegisteredCheckName,
2442 UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName,
2443 });
2444 EXPECT_EQ(actual_attempts.size(), 1u);
2445
2446 ASSERT_TRUE(actual_attempts[0].metrics.count(
2447 UkmEntry::kPrefetchServiceWorkerRegisteredCheckName));
2448 EXPECT_EQ(
2449 actual_attempts[0]
2450 .metrics[UkmEntry::kPrefetchServiceWorkerRegisteredCheckName],
2451 static_cast<int64_t>(
2452 PreloadingAttemptImpl::ServiceWorkerRegisteredCheck::kOriginOnly));
2453 ASSERT_TRUE(actual_attempts[0].metrics.count(
2454 UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName));
2455 EXPECT_EQ(
2456 actual_attempts[0].metrics
2457 [UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName],
2458 ukm::GetExponentialBucketMin(
2459 0, PreloadingAttemptImpl::
2460 kServiceWorkerRegisteredCheckDurationBucketSpacing));
2461}
2462
Hiroshige Hayashizakie354fb632025-08-04 21:31:392463TEST_P(PrefetchServiceTest, NotEligibleServiceWorkerNoFetchHandlerRegistered) {
Liviu Tintaf7d62a72023-08-29 21:48:392464 base::HistogramTester histogram_tester;
2465
2466 MakePrefetchService(
2467 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2468
Liviu Tinta91202302023-09-26 17:49:112469 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
Liviu Tintaf7d62a72023-08-29 21:48:392470 blink::StorageKey::CreateFromStringForTesting("https://example.com"));
Liviu Tinta91202302023-09-26 17:49:112471 service_worker_context_->AddServiceWorkerScope(
Liviu Tintaf7d62a72023-08-29 21:48:392472 GURL("https://example.com"),
2473 ServiceWorkerCapability::SERVICE_WORKER_NO_FETCH_HANDLER);
2474
2475 MakePrefetchOnMainFrame(
2476 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582477 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2478 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252479 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542480 task_environment()->RunUntilIdle();
Liviu Tintaf7d62a72023-08-29 21:48:392481
Hiroshige Hayashizakie354fb632025-08-04 21:31:392482 EXPECT_EQ(RequestCount(), 0);
Liviu Tintaf7d62a72023-08-29 21:48:392483
Hiroshige Hayashizakie354fb632025-08-04 21:31:392484 histogram_tester.ExpectUniqueSample(
2485 "Preloading.Prefetch.PrefetchStatus",
2486 PrefetchStatus::kPrefetchIneligibleUserHasServiceWorkerNoFetchHandler, 1);
2487 ExpectPrefetchNotEligible(
2488 histogram_tester,
2489 PreloadingEligibility::kUserHasServiceWorkerNoFetchHandler);
Liviu Tintaf7d62a72023-08-29 21:48:392490
Taiyo Mizuhashi01d86af22024-03-27 14:27:422491 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizakie354fb632025-08-04 21:31:392492 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
2493 ExpectServingMetrics(
2494 PrefetchStatus::kPrefetchIneligibleUserHasServiceWorkerNoFetchHandler);
Liviu Tintaf7d62a72023-08-29 21:48:392495}
2496
kenossbd9a3fc2025-03-28 05:15:572497TEST_P(PrefetchServiceTest,
Hiroshige Hayashizakie354fb632025-08-04 21:31:392498 NotEligibleServiceWorkerNoFetchHandlerRegisteredServiceWorkerCheckUKM) {
Liviu Tinta91202302023-09-26 17:49:112499 ukm::TestAutoSetUkmRecorder ukm_recorder;
2500 MakePrefetchService(
2501 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2502
2503 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
2504 blink::StorageKey::CreateFromStringForTesting("https://example.com"));
2505 service_worker_context_->AddServiceWorkerScope(
2506 GURL("https://example.com"),
2507 ServiceWorkerCapability::SERVICE_WORKER_NO_FETCH_HANDLER);
2508 service_worker_context_->SetServiceWorkerCheckDuration(
2509 base::Microseconds(kServiceWorkerCheckDuration));
2510
2511 MakePrefetchOnMainFrame(
2512 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582513 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2514 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:252515 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542516 task_environment()->RunUntilIdle();
Liviu Tinta91202302023-09-26 17:49:112517
Hiroshige Hayashizakie354fb632025-08-04 21:31:392518 EXPECT_EQ(RequestCount(), 0);
Liviu Tinta91202302023-09-26 17:49:112519
Taiyo Mizuhashi01d86af22024-03-27 14:27:422520 NavigateInitiatedByRenderer(GURL("https://example.com"));
Liviu Tinta91202302023-09-26 17:49:112521
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462522 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Liviu Tinta91202302023-09-26 17:49:112523
2524 ForceLogsUploadAndGetUkmId();
2525 // Now check the UKM records.
2526 using UkmEntry = ukm::builders::Preloading_Attempt;
2527 auto actual_attempts = test_ukm_recorder()->GetEntries(
2528 UkmEntry::kEntryName,
2529 {
2530 UkmEntry::kPrefetchServiceWorkerRegisteredCheckName,
2531 UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName,
2532 });
2533 EXPECT_EQ(actual_attempts.size(), 1u);
2534
2535 ASSERT_TRUE(actual_attempts[0].metrics.count(
2536 UkmEntry::kPrefetchServiceWorkerRegisteredCheckName));
2537 EXPECT_EQ(actual_attempts[0]
2538 .metrics[UkmEntry::kPrefetchServiceWorkerRegisteredCheckName],
2539 static_cast<int64_t>(
2540 PreloadingAttemptImpl::ServiceWorkerRegisteredCheck::kPath));
2541 ASSERT_TRUE(actual_attempts[0].metrics.count(
2542 UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName));
2543 EXPECT_EQ(
2544 actual_attempts[0].metrics
2545 [UkmEntry::kPrefetchServiceWorkerRegisteredForURLCheckDurationName],
2546 ukm::GetExponentialBucketMin(
2547 kServiceWorkerCheckDuration,
2548 PreloadingAttemptImpl::
2549 kServiceWorkerRegisteredCheckDurationBucketSpacing));
2550}
2551
kenossbd9a3fc2025-03-28 05:15:572552TEST_P(PrefetchServiceTest, EligibleServiceWorkerNotRegisteredAtThisPath) {
Liviu Tintaf7d62a72023-08-29 21:48:392553 base::HistogramTester histogram_tester;
2554
2555 MakePrefetchService(
2556 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2557
Liviu Tinta91202302023-09-26 17:49:112558 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
Liviu Tintaf7d62a72023-08-29 21:48:392559 blink::StorageKey::CreateFromStringForTesting("https://example.com"));
Liviu Tinta91202302023-09-26 17:49:112560 service_worker_context_->AddServiceWorkerScope(
Liviu Tintaf7d62a72023-08-29 21:48:392561 GURL("https://example.com/sw"),
2562 ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER);
2563
2564 MakePrefetchOnMainFrame(
2565 GURL("https://example.com/non_sw/index.html"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582566 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2567 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252568 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542569 task_environment()->RunUntilIdle();
Liviu Tintaf7d62a72023-08-29 21:48:392570
2571 VerifyCommonRequestState(GURL("https://example.com/non_sw/index.html"),
2572 {.use_prefetch_proxy = true});
2573 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
2574 /*use_prefetch_proxy=*/true,
2575 {{"X-Testing", "Hello World"}}, kHTMLBody);
2576
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362577 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
2578
Taiyo Mizuhashi01d86af22024-03-27 14:27:422579 NavigateInitiatedByRenderer(GURL("https://example.com/non_sw/index.html"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462580 ExpectServingReaderSuccess(
2581 GetPrefetchToServe(GURL("https://example.com/non_sw/index.html")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362582 ExpectServingMetricsSuccess();
Liviu Tintaf7d62a72023-08-29 21:48:392583}
2584
kenossbd9a3fc2025-03-28 05:15:572585TEST_P(PrefetchServiceTest, NotEligibleUserHasCookies) {
Max Curran6b93426f2022-05-03 00:55:522586 base::HistogramTester histogram_tester;
2587
Max Curranead64a62022-06-22 01:10:522588 MakePrefetchService(
2589 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2590
Max Curran146bf442022-03-28 23:22:142591 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
2592
Max Curran2e83b5d2022-12-29 18:53:382593 MakePrefetchOnMainFrame(
2594 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582595 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2596 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252597 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542598 task_environment()->RunUntilIdle();
Max Curran146bf442022-03-28 23:22:142599
Max Curran18a6f2b2022-05-02 23:13:242600 EXPECT_EQ(RequestCount(), 0);
2601
Adithya Srinivasan4d3dad02024-10-17 18:06:592602 histogram_tester.ExpectUniqueSample(
2603 "Preloading.Prefetch.PrefetchStatus",
2604 PrefetchStatus::kPrefetchIneligibleUserHasCookies, 1);
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:212605 ExpectPrefetchNotEligible(histogram_tester,
2606 PreloadingEligibility::kUserHasCookies);
Max Curran6b93426f2022-05-03 00:55:522607
Taiyo Mizuhashi01d86af22024-03-27 14:27:422608 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462609 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:122610 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligibleUserHasCookies);
Max Curran146bf442022-03-28 23:22:142611}
2612
kenossbd9a3fc2025-03-28 05:15:572613TEST_P(PrefetchServiceTest, EligibleUserHasCookiesForDifferentUrl) {
Max Curran6b93426f2022-05-03 00:55:522614 base::HistogramTester histogram_tester;
2615
Max Curranead64a62022-06-22 01:10:522616 MakePrefetchService(
2617 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2618
Max Curran146bf442022-03-28 23:22:142619 ASSERT_TRUE(SetCookie(GURL("https://other.com"), "testing"));
2620
Max Curran2e83b5d2022-12-29 18:53:382621 MakePrefetchOnMainFrame(
2622 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582623 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2624 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252625 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542626 task_environment()->RunUntilIdle();
Max Curran146bf442022-03-28 23:22:142627
Max Curran18a6f2b2022-05-02 23:13:242628 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:052629 {.use_prefetch_proxy = true});
Max Curran18a6f2b2022-05-02 23:13:242630 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
Max Curran6b93426f2022-05-03 00:55:522631 /*use_prefetch_proxy=*/true,
Max Curran18a6f2b2022-05-02 23:13:242632 {{"X-Testing", "Hello World"}}, kHTMLBody);
2633
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462634 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Max Curran6b93426f2022-05-03 00:55:522635
Taiyo Mizuhashi01d86af22024-03-27 14:27:422636 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462637 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362638 ExpectServingMetricsSuccess();
Max Curran18a6f2b2022-05-02 23:13:242639}
2640
kenossbd9a3fc2025-03-28 05:15:572641TEST_P(PrefetchServiceTest, EligibleSameOriginPrefetchCanHaveExistingCookies) {
Kevin McNee06824c72024-02-06 18:59:522642 NavigationSimulator::NavigateAndCommitFromBrowser(
2643 web_contents(), GURL("https://example.com/referrer"));
Max Curran6b93426f2022-05-03 00:55:522644 base::HistogramTester histogram_tester;
2645
Max Curranead64a62022-06-22 01:10:522646 MakePrefetchService(
2647 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2648
Max Curran18a6f2b2022-05-02 23:13:242649 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
2650
Max Curran2e83b5d2022-12-29 18:53:382651 MakePrefetchOnMainFrame(
2652 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582653 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2654 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:252655 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542656 task_environment()->RunUntilIdle();
Max Curran18a6f2b2022-05-02 23:13:242657
Jeremy Roman4c9524212023-07-27 22:01:052658 VerifyCommonRequestState(GURL("https://example.com"));
Max Curran18a6f2b2022-05-02 23:13:242659 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
Max Curran6b93426f2022-05-03 00:55:522660 /*use_prefetch_proxy=*/false,
Max Curran18a6f2b2022-05-02 23:13:242661 {{"X-Testing", "Hello World"}}, kHTMLBody);
2662
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462663 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Max Curran6b93426f2022-05-03 00:55:522664
Taiyo Mizuhashi01d86af22024-03-27 14:27:422665 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462666 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362667 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
Max Curran6b93426f2022-05-03 00:55:522668}
2669
Georg Neis0dce3332024-12-13 01:51:182670// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:572671TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:482672 DISABLED_CHROMEOS(FailedCookiesChangedAfterPrefetchStarted)) {
Max Curran5b4806eb2023-05-25 20:07:202673 base::HistogramTester histogram_tester;
2674
2675 MakePrefetchService(
2676 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2677
2678 MakePrefetchOnMainFrame(
2679 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582680 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2681 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252682 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542683 task_environment()->RunUntilIdle();
Max Curran5b4806eb2023-05-25 20:07:202684
2685 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:052686 {.use_prefetch_proxy = true});
Max Curran5b4806eb2023-05-25 20:07:202687 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
2688 /*use_prefetch_proxy=*/true,
2689 {{"X-Testing", "Hello World"}}, kHTMLBody);
2690
2691 // Adding a cookie after the prefetch has started will cause it to fail when
2692 // being served.
2693 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542694 task_environment()->RunUntilIdle();
Max Curran5b4806eb2023-05-25 20:07:202695
Taiyo Mizuhashi01d86af22024-03-27 14:27:422696 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Curran5b4806eb2023-05-25 20:07:202697
2698 histogram_tester.ExpectUniqueSample(
2699 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
2700 histogram_tester.ExpectUniqueSample(
2701 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
2702 histogram_tester.ExpectUniqueSample(
2703 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
2704 histogram_tester.ExpectUniqueSample(
2705 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
2706 histogram_tester.ExpectUniqueSample(
2707 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
2708
Arthur Sonzognic686e8f2024-01-11 08:36:372709 std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
Max Curran5b4806eb2023-05-25 20:07:202710 PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
2711 EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1);
2712 EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1);
2713 EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1);
2714
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462715 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizakic79f2bbc2023-05-26 22:08:392716
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462717 ExpectServingMetrics(PrefetchStatus::kPrefetchNotUsedCookiesChanged,
2718 /*prefetch_header_latency=*/true);
Max Curran5b4806eb2023-05-25 20:07:202719
Max Curran5b4806eb2023-05-25 20:07:202720 // ReadyTime will be included in the UKM, because the prefetch was ready, and
2721 // then failed.
Adithya Srinivasan38e0c402023-07-19 16:12:582722 ExpectCorrectUkmLogs({.outcome = PreloadingTriggeringOutcome::kFailure,
2723 .failure = ToPreloadingFailureReason(
2724 PrefetchStatus::kPrefetchNotUsedCookiesChanged),
kenossbe8fbf22024-08-14 12:18:212725 .is_accurate = true,
Adithya Srinivasan38e0c402023-07-19 16:12:582726 .expect_ready_time = true});
Adithya Srinivasan4d3dad02024-10-17 18:06:592727 histogram_tester.ExpectUniqueSample(
2728 "Preloading.Prefetch.PrefetchStatus",
2729 PrefetchStatus::kPrefetchNotUsedCookiesChanged, 1);
Max Curran5b4806eb2023-05-25 20:07:202730}
2731
Georg Neis0dce3332024-12-13 01:51:182732// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:572733TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:482734 DISABLED_CHROMEOS(SameOriginPrefetchIgnoresProxyRequirement)) {
Kevin McNee06824c72024-02-06 18:59:522735 NavigationSimulator::NavigateAndCommitFromBrowser(
2736 web_contents(), GURL("https://example.com/referrer"));
Max Curranc0137502023-05-02 18:06:222737 base::HistogramTester histogram_tester;
2738
2739 MakePrefetchService(
2740 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2741
2742 // Make a same-origin prefetch that requires the proxy. The proxy requirement
2743 // is only enforced for cross-origin requests.
2744 MakePrefetchOnMainFrame(
2745 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582746 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2747 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252748 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542749 task_environment()->RunUntilIdle();
Max Curranc0137502023-05-02 18:06:222750
Jeremy Roman4c9524212023-07-27 22:01:052751 VerifyCommonRequestState(GURL("https://example.com"));
Max Curranc0137502023-05-02 18:06:222752 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
2753 /*use_prefetch_proxy=*/false,
2754 {{"X-Testing", "Hello World"}}, kHTMLBody);
2755
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462756 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362757
Max Curranc0137502023-05-02 18:06:222758 // serving_page_metrics->required_private_prefetch_proxy will be true if the
2759 // prefetch is marked as requiring the proxy when cross origin, even if only
2760 // prefetch request was same-origin.
Taiyo Mizuhashi01d86af22024-03-27 14:27:422761 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462762 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362763 ExpectServingMetricsSuccess();
Max Curranc0137502023-05-02 18:06:222764}
2765
Georg Neis0dce3332024-12-13 01:51:182766// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:572767TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:482768 DISABLED_CHROMEOS(NotEligibleSameSiteCrossOriginPrefetchRequiresProxy)) {
Kevin McNee06824c72024-02-06 18:59:522769 NavigationSimulator::NavigateAndCommitFromBrowser(
2770 web_contents(), GURL("https://example.com/referrer"));
Max Curranc0137502023-05-02 18:06:222771 base::HistogramTester histogram_tester;
2772
2773 MakePrefetchService(
2774 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2775
2776 // Make a same-site cross-origin prefetch that requires the proxy. These types
2777 // of prefetches are blocked.
2778 MakePrefetchOnMainFrame(
2779 GURL("https://other.example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582780 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2781 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252782 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542783 task_environment()->RunUntilIdle();
Max Curranc0137502023-05-02 18:06:222784
2785 EXPECT_EQ(RequestCount(), 0);
2786
Adithya Srinivasan4d3dad02024-10-17 18:06:592787 histogram_tester.ExpectUniqueSample(
2788 "Preloading.Prefetch.PrefetchStatus",
2789 PrefetchStatus::
2790 kPrefetchIneligibleSameSiteCrossOriginPrefetchRequiredProxy,
2791 1);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462792 ExpectPrefetchNotEligible(
2793 histogram_tester,
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:212794 PreloadingEligibility::kSameSiteCrossOriginPrefetchRequiredProxy);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362795
Taiyo Mizuhashi01d86af22024-03-27 14:27:422796 NavigateInitiatedByRenderer(GURL("https://other.example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362797 EXPECT_FALSE(GetPrefetchToServe(GURL("https://other.example.com")));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462798 ExpectServingMetrics(
2799 PrefetchStatus::
Hiroshige Hayashizaki238198d82023-10-23 23:04:122800 kPrefetchIneligibleSameSiteCrossOriginPrefetchRequiredProxy);
Max Curranc0137502023-05-02 18:06:222801}
2802
kenossbd9a3fc2025-03-28 05:15:572803TEST_P(PrefetchServiceTest, NotEligibleExistingConnectProxy) {
Max Currancc1ab0c2022-09-12 22:03:112804 base::HistogramTester histogram_tester;
2805
2806 MakePrefetchService(
2807 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2808
2809 net::ProxyInfo proxy_info;
2810 proxy_info.UseNamedProxy("proxy.com");
2811 TestNetworkContext network_context_for_proxy_lookup(proxy_info);
2812 PrefetchService::SetNetworkContextForProxyLookupForTesting(
2813 &network_context_for_proxy_lookup);
2814
Max Curran2e83b5d2022-12-29 18:53:382815 MakePrefetchOnMainFrame(
2816 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582817 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2818 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252819 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542820 task_environment()->RunUntilIdle();
Max Currancc1ab0c2022-09-12 22:03:112821
2822 EXPECT_EQ(RequestCount(), 0);
2823
Adithya Srinivasan4d3dad02024-10-17 18:06:592824 histogram_tester.ExpectUniqueSample(
2825 "Preloading.Prefetch.PrefetchStatus",
2826 PrefetchStatus::kPrefetchIneligibleExistingProxy, 1);
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:212827 ExpectPrefetchNotEligible(histogram_tester,
2828 PreloadingEligibility::kExistingProxy);
Max Currancc1ab0c2022-09-12 22:03:112829
Taiyo Mizuhashi01d86af22024-03-27 14:27:422830 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462831 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:122832 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligibleExistingProxy);
William Liu77089052022-12-15 18:53:352833
Max Currancc1ab0c2022-09-12 22:03:112834 PrefetchService::SetNetworkContextForProxyLookupForTesting(nullptr);
2835}
2836
kenossbd9a3fc2025-03-28 05:15:572837TEST_P(PrefetchServiceTest, EligibleExistingConnectProxyButSameOriginPrefetch) {
Kevin McNee06824c72024-02-06 18:59:522838 NavigationSimulator::NavigateAndCommitFromBrowser(
2839 web_contents(), GURL("https://example.com/referrer"));
Max Currancc1ab0c2022-09-12 22:03:112840 base::HistogramTester histogram_tester;
2841
2842 MakePrefetchService(
2843 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2844
2845 net::ProxyInfo proxy_info;
2846 proxy_info.UseNamedProxy("proxy.com");
2847 TestNetworkContext network_context_for_proxy_lookup(proxy_info);
2848 PrefetchService::SetNetworkContextForProxyLookupForTesting(
2849 &network_context_for_proxy_lookup);
2850
Max Curran2e83b5d2022-12-29 18:53:382851 MakePrefetchOnMainFrame(
2852 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582853 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2854 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:252855 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542856 task_environment()->RunUntilIdle();
Max Currancc1ab0c2022-09-12 22:03:112857
Jeremy Roman4c9524212023-07-27 22:01:052858 VerifyCommonRequestState(GURL("https://example.com"));
Max Currancc1ab0c2022-09-12 22:03:112859 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
2860 /*use_prefetch_proxy=*/false,
2861 {{"X-Testing", "Hello World"}}, kHTMLBody);
2862
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462863 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Max Currancc1ab0c2022-09-12 22:03:112864
Taiyo Mizuhashi01d86af22024-03-27 14:27:422865 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462866 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362867 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
William Liu77089052022-12-15 18:53:352868
Max Currancc1ab0c2022-09-12 22:03:112869 PrefetchService::SetNetworkContextForProxyLookupForTesting(nullptr);
2870}
2871
kenossbd9a3fc2025-03-28 05:15:572872TEST_P(PrefetchServiceTest, FailedNon2XXResponseCode) {
Max Curran6b93426f2022-05-03 00:55:522873 base::HistogramTester histogram_tester;
2874
Max Curranead64a62022-06-22 01:10:522875 MakePrefetchService(
2876 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2877
Max Curran2e83b5d2022-12-29 18:53:382878 MakePrefetchOnMainFrame(
2879 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582880 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2881 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252882 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542883 task_environment()->RunUntilIdle();
Max Curran6b93426f2022-05-03 00:55:522884
2885 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:052886 {.use_prefetch_proxy = true});
Max Curran6b93426f2022-05-03 00:55:522887 MakeResponseAndWait(net::HTTP_NOT_FOUND, net::OK, kHTMLMimeType,
2888 /*use_prefetch_proxy=*/true,
2889 {{"X-Testing", "Hello World"}}, kHTMLBody);
2890
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462891 ExpectPrefetchFailedAfterResponseReceived(
2892 histogram_tester, net::HTTP_NOT_FOUND, std::size(kHTMLBody),
2893 PrefetchStatus::kPrefetchFailedNon2XX);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362894
Taiyo Mizuhashi01d86af22024-03-27 14:27:422895 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362896 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462897 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedNon2XX,
2898 /*prefetch_header_latency=*/true);
Max Curran6b93426f2022-05-03 00:55:522899}
2900
kenossbd9a3fc2025-03-28 05:15:572901TEST_P(PrefetchServiceTest, FailedNetError) {
Max Curran6b93426f2022-05-03 00:55:522902 base::HistogramTester histogram_tester;
2903
Max Curranead64a62022-06-22 01:10:522904 MakePrefetchService(
2905 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2906
Max Curran2e83b5d2022-12-29 18:53:382907 MakePrefetchOnMainFrame(
2908 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582909 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2910 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252911 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542912 task_environment()->RunUntilIdle();
Max Curran6b93426f2022-05-03 00:55:522913
2914 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:052915 {.use_prefetch_proxy = true});
Max Curran6b93426f2022-05-03 00:55:522916 MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED, kHTMLMimeType,
2917 /*use_prefetch_proxy=*/true,
2918 {{"X-Testing", "Hello World"}}, kHTMLBody);
2919
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462920 ExpectPrefetchFailedNetError(histogram_tester, net::ERR_FAILED);
Max Curran6b93426f2022-05-03 00:55:522921
Taiyo Mizuhashi01d86af22024-03-27 14:27:422922 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462923 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362924 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedNetError);
Max Curran6b93426f2022-05-03 00:55:522925}
2926
kenossbd9a3fc2025-03-28 05:15:572927TEST_P(PrefetchServiceTest, HandleRetryAfterResponse) {
Max Curranead64a62022-06-22 01:10:522928 base::HistogramTester histogram_tester;
2929
2930 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
2931 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>();
2932
2933 EXPECT_CALL(
2934 *mock_prefetch_service_delegate,
2935 ReportOriginRetryAfter(GURL("https://example.com"), base::Seconds(1234)))
2936 .Times(1);
2937
2938 MakePrefetchService(std::move(mock_prefetch_service_delegate));
2939
Max Curran2e83b5d2022-12-29 18:53:382940 MakePrefetchOnMainFrame(
2941 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582942 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2943 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252944 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542945 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:522946
2947 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:052948 {.use_prefetch_proxy = true});
Max Curranead64a62022-06-22 01:10:522949
2950 // Simulate the origin responding with a "retry-after" header.
2951 MakeResponseAndWait(net::HTTP_SERVICE_UNAVAILABLE, net::OK, kHTMLMimeType,
2952 /*use_prefetch_proxy=*/true,
2953 {{"Retry-After", "1234"}, {"X-Testing", "Hello World"}},
2954 "");
2955
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462956 ExpectPrefetchFailedAfterResponseReceived(
2957 histogram_tester, net::HTTP_SERVICE_UNAVAILABLE, 0,
2958 PrefetchStatus::kPrefetchFailedNon2XX);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362959
Taiyo Mizuhashi01d86af22024-03-27 14:27:422960 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362961 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462962 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedNon2XX,
2963 /*prefetch_header_latency=*/true);
Max Curranead64a62022-06-22 01:10:522964}
2965
kenossbd9a3fc2025-03-28 05:15:572966TEST_P(PrefetchServiceTest, SuccessNonHTML) {
Max Curran6b93426f2022-05-03 00:55:522967 base::HistogramTester histogram_tester;
2968
Max Curranead64a62022-06-22 01:10:522969 MakePrefetchService(
2970 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
2971
Max Curran2e83b5d2022-12-29 18:53:382972 MakePrefetchOnMainFrame(
2973 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:582974 PrefetchType(PreloadingTriggerType::kSpeculationRule,
2975 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:252976 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:542977 task_environment()->RunUntilIdle();
Max Curran6b93426f2022-05-03 00:55:522978
2979 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:052980 {.use_prefetch_proxy = true});
Max Curran6b93426f2022-05-03 00:55:522981
2982 std::string body = "fake PDF";
2983 MakeResponseAndWait(net::HTTP_OK, net::OK, "application/pdf",
2984 /*use_prefetch_proxy=*/true,
2985 {{"X-Testing", "Hello World"}}, body);
2986
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462987 ExpectPrefetchSuccess(histogram_tester, body.size());
Max Curran6b93426f2022-05-03 00:55:522988
Taiyo Mizuhashi01d86af22024-03-27 14:27:422989 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:462990 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:362991 ExpectServingMetricsSuccess();
Max Curranc4445fc2022-06-02 18:43:432992}
2993
Adithya Srinivasanad5158e2024-01-26 15:41:552994// Regression test for crbug.com/1491889. Completes a prefetch, and then changes
2995// the cookies for the prefetched URL. It then creates two NavigationRequests
2996// (to the same URL) and calls GetPrefetchToServe for each request. This can
2997// happen in practice when a user clicks on a link to a URL twice).
kenossbd9a3fc2025-03-28 05:15:572998TEST_P(PrefetchServiceTest,
Adithya Srinivasanad5158e2024-01-26 15:41:552999 MultipleNavigationRequestsCallGetPrefetchAfterCookieChange) {
3000 MakePrefetchService(
3001 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3002
3003 MakePrefetchOnMainFrame(
3004 GURL("https://example.com"),
3005 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3006 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253007 blink::mojom::SpeculationEagerness::kImmediate));
Adithya Srinivasanad5158e2024-01-26 15:41:553008 task_environment()->RunUntilIdle();
3009
3010 VerifyCommonRequestState(GURL("https://example.com"),
3011 {.use_prefetch_proxy = true});
3012 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3013 /*use_prefetch_proxy=*/true,
3014 {{"X-Testing", "Hello World"}}, kHTMLBody);
3015
3016 // Adding a cookie after the prefetch has started will cause it to fail when
3017 // being served.
3018 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
3019 task_environment()->RunUntilIdle();
3020
Taiyo Mizuhashi01d86af22024-03-27 14:27:423021 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573022 base::test::TestFuture<PrefetchServingHandle> future_1;
Taiyo Mizuhashi01d86af22024-03-27 14:27:423023 GetPrefetchToServe(future_1, GURL("https://example.com"),
3024 MainDocumentToken());
Adithya Srinivasanad5158e2024-01-26 15:41:553025 EXPECT_TRUE(future_1.IsReady());
3026 // No prefetch should be returned (the example.com prefetch had its cookies
3027 // changed).
3028 EXPECT_FALSE(future_1.Get().GetPrefetchContainer());
3029
Taiyo Mizuhashi01d86af22024-03-27 14:27:423030 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573031 base::test::TestFuture<PrefetchServingHandle> future_2;
Taiyo Mizuhashi01d86af22024-03-27 14:27:423032 GetPrefetchToServe(future_2, GURL("https://example.com"),
3033 MainDocumentToken());
Adithya Srinivasanad5158e2024-01-26 15:41:553034 EXPECT_TRUE(future_2.IsReady());
3035 EXPECT_FALSE(future_2.Get().GetPrefetchContainer());
3036}
3037
kenossbd9a3fc2025-03-28 05:15:573038TEST_P(PrefetchServiceTest, NotServeableNavigationInDifferentRenderFrameHost) {
Max Curranc4445fc2022-06-02 18:43:433039 base::HistogramTester histogram_tester;
3040
Max Curranead64a62022-06-22 01:10:523041 MakePrefetchService(
3042 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3043
Max Curran2e83b5d2022-12-29 18:53:383044 MakePrefetchOnMainFrame(
3045 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583046 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3047 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253048 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543049 task_environment()->RunUntilIdle();
Max Curranc4445fc2022-06-02 18:43:433050
3051 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053052 {.use_prefetch_proxy = true});
Max Curranc4445fc2022-06-02 18:43:433053 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3054 /*use_prefetch_proxy=*/true,
3055 {{"X-Testing", "Hello World"}}, kHTMLBody);
3056
Hiroshige Hayashizaki96f3d63132023-05-23 23:14:523057 // Since the navigation is occurring in a LocalFrameToken other than where the
Max Curranc4445fc2022-06-02 18:43:433058 // prefetch was requested from, we cannot use it.
Hiroshige Hayashizaki96f3d63132023-05-23 23:14:523059 blink::LocalFrameToken other_token(base::UnguessableToken::Create());
3060 ASSERT_NE(other_token, main_rfh()->GetFrameToken());
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:113061 blink::DocumentToken different_document_token;
3062 ASSERT_NE(different_document_token, MainDocumentToken());
Emily Andrewsd15fd762024-12-10 20:41:543063 Navigate(GURL("https://example.com"),
3064 main_rfh()->GetProcess()->GetDeprecatedID(), other_token,
3065 different_document_token);
Max Curranc4445fc2022-06-02 18:43:433066
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463067 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:113068 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com"),
3069 different_document_token));
Arthur Sonzognic686e8f2024-01-11 08:36:373070 std::optional<PrefetchServingPageMetrics> serving_page_metrics =
Max Curran210cffa2022-09-06 22:24:313071 GetMetricsForMostRecentNavigation();
3072 EXPECT_FALSE(serving_page_metrics);
Max Curran6b93426f2022-05-03 00:55:523073}
3074
kenossbd9a3fc2025-03-28 05:15:573075class PrefetchServiceWithHTMLOnlyTest
3076 : public PrefetchServiceTestBase,
3077 public WithPrefetchServiceRearchParam,
3078 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
Max Curran6b93426f2022-05-03 00:55:523079 public:
kenossbd9a3fc2025-03-28 05:15:573080 PrefetchServiceWithHTMLOnlyTest()
3081 : WithPrefetchServiceRearchParam(GetParam()) {}
3082
Max Curran6b93426f2022-05-03 00:55:523083 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:153084 InitBaseParams();
3085 InitRearchFeatures();
3086 // Override `kPrefetchUseContentRefactor`.
Liviu Tinta91455162022-12-14 22:06:233087 scoped_feature_list_.InitWithFeaturesAndParameters(
Johanna9fe85f2023-01-17 10:15:433088 {{features::kPrefetchUseContentRefactor,
Liviu Tinta91455162022-12-14 22:06:233089 {{"ineligible_decoy_request_probability", "0"},
3090 {"prefetch_container_lifetime_s", "-1"},
3091 {"html_only", "true"}}}},
Liviu Tinta5043cae52024-06-26 00:07:293092 {});
Max Curran6b93426f2022-05-03 00:55:523093 }
kenoss557d4092025-04-01 05:01:153094
3095 private:
3096 base::test::ScopedFeatureList scoped_feature_list_;
Max Curran6b93426f2022-05-03 00:55:523097};
3098
kenossbd9a3fc2025-03-28 05:15:573099INSTANTIATE_TEST_SUITE_P(
3100 ParametrizedTests,
3101 PrefetchServiceWithHTMLOnlyTest,
3102 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
3103
3104TEST_P(PrefetchServiceWithHTMLOnlyTest, FailedNonHTMLWithHTMLOnly) {
Max Curran6b93426f2022-05-03 00:55:523105 base::HistogramTester histogram_tester;
3106
Max Curranead64a62022-06-22 01:10:523107 MakePrefetchService(
3108 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3109
Max Curran2e83b5d2022-12-29 18:53:383110 MakePrefetchOnMainFrame(
3111 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583112 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3113 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253114 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543115 task_environment()->RunUntilIdle();
Max Curran6b93426f2022-05-03 00:55:523116
3117 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053118 {.use_prefetch_proxy = true});
Max Curran6b93426f2022-05-03 00:55:523119
3120 std::string body = "fake PDF";
3121 MakeResponseAndWait(net::HTTP_OK, net::OK, "application/pdf",
3122 /*use_prefetch_proxy=*/true,
3123 {{"X-Testing", "Hello World"}}, body);
3124
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463125 ExpectPrefetchFailedAfterResponseReceived(
3126 histogram_tester, net::HTTP_OK, body.size(),
3127 PrefetchStatus::kPrefetchFailedMIMENotSupported);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363128
Taiyo Mizuhashi01d86af22024-03-27 14:27:423129 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363130 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463131 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedMIMENotSupported,
3132 /*prefetch_header_latency=*/true);
Max Curran6b93426f2022-05-03 00:55:523133}
3134
kenoss972e8682024-09-05 00:20:143135class PrefetchServiceAlwaysMakeDecoyRequestTest
kenossbd9a3fc2025-03-28 05:15:573136 : public PrefetchServiceTestBase,
3137 public WithPrefetchServiceRearchParam,
3138 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
Max Curran6b93426f2022-05-03 00:55:523139 public:
kenossbd9a3fc2025-03-28 05:15:573140 PrefetchServiceAlwaysMakeDecoyRequestTest()
3141 : WithPrefetchServiceRearchParam(GetParam()) {}
3142
Max Curran6b93426f2022-05-03 00:55:523143 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:153144 InitBaseParams();
3145 InitRearchFeatures();
3146 // Override `kPrefetchUseContentRefactor`.
Liviu Tinta91455162022-12-14 22:06:233147 scoped_feature_list_.InitWithFeaturesAndParameters(
Johanna9fe85f2023-01-17 10:15:433148 {{features::kPrefetchUseContentRefactor,
Liviu Tinta91455162022-12-14 22:06:233149 {{"ineligible_decoy_request_probability", "1"},
Jeremy Roman337e4952024-07-04 20:36:493150 {"prefetch_container_lifetime_s", "-1"}}}},
Liviu Tinta5043cae52024-06-26 00:07:293151 {});
Max Curran6b93426f2022-05-03 00:55:523152 }
kenoss557d4092025-04-01 05:01:153153
3154 private:
3155 base::test::ScopedFeatureList scoped_feature_list_;
Max Curran6b93426f2022-05-03 00:55:523156};
3157
kenossbd9a3fc2025-03-28 05:15:573158INSTANTIATE_TEST_SUITE_P(
3159 ParametrizedTests,
3160 PrefetchServiceAlwaysMakeDecoyRequestTest,
3161 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
3162
3163TEST_P(PrefetchServiceAlwaysMakeDecoyRequestTest, DecoyRequest) {
Max Curran892ca5422022-12-12 20:55:343164 base::HistogramTester histogram_tester;
3165
Max Curranead64a62022-06-22 01:10:523166 MakePrefetchService(
3167 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3168
Max Curran6b93426f2022-05-03 00:55:523169 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
3170
Max Curran2e83b5d2022-12-29 18:53:383171 MakePrefetchOnMainFrame(
3172 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583173 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3174 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253175 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543176 task_environment()->RunUntilIdle();
Max Curran6b93426f2022-05-03 00:55:523177
3178 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053179 {.use_prefetch_proxy = true});
Max Curran6b93426f2022-05-03 00:55:523180 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3181 /*use_prefetch_proxy=*/true,
3182 {{"X-Testing", "Hello World"}}, kHTMLBody);
3183
William Liu77089052022-12-15 18:53:353184 // A decoy is considered a failure.
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463185 ExpectPrefetchFailedBeforeResponseReceived(
3186 histogram_tester, PrefetchStatus::kPrefetchIsPrivacyDecoy);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363187
Taiyo Mizuhashi01d86af22024-03-27 14:27:423188 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363189 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463190 ExpectServingMetrics(PrefetchStatus::kPrefetchIsPrivacyDecoy,
3191 /*prefetch_header_latency=*/true);
Max Curran146bf442022-03-28 23:22:143192}
3193
kenossbd9a3fc2025-03-28 05:15:573194TEST_P(PrefetchServiceAlwaysMakeDecoyRequestTest,
Adithya Srinivasan7c65f142024-01-29 19:02:033195 NavigateBeforeDecoyResponseReceived) {
3196 base::HistogramTester histogram_tester;
3197
3198 MakePrefetchService(
3199 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3200
3201 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
3202
3203 MakePrefetchOnMainFrame(
3204 GURL("https://example.com"),
3205 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3206 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253207 blink::mojom::SpeculationEagerness::kImmediate));
Adithya Srinivasan7c65f142024-01-29 19:02:033208 task_environment()->RunUntilIdle();
3209
3210 VerifyCommonRequestState(GURL("https://example.com"),
3211 {.use_prefetch_proxy = true});
3212
Taiyo Mizuhashi01d86af22024-03-27 14:27:423213 NavigateInitiatedByRenderer(GURL("https://example.com"));
Adithya Srinivasan7c65f142024-01-29 19:02:033214 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
3215
3216 ExpectCorrectUkmLogs({.outcome = PreloadingTriggeringOutcome::kUnspecified});
3217}
3218
kenossbd9a3fc2025-03-28 05:15:573219TEST_P(PrefetchServiceAlwaysMakeDecoyRequestTest,
Max Curranead64a62022-06-22 01:10:523220 NoDecoyRequestDisableDecoysBasedOnUserSettings) {
Max Curran892ca5422022-12-12 20:55:343221 base::HistogramTester histogram_tester;
3222
Max Curranead64a62022-06-22 01:10:523223 std::unique_ptr<MockPrefetchServiceDelegate> mock_prefetch_service_delegate =
3224 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>();
3225
3226 EXPECT_CALL(*mock_prefetch_service_delegate, DisableDecoysBasedOnUserSettings)
3227 .Times(1)
3228 .WillOnce(testing::Return(true));
3229
3230 MakePrefetchService(std::move(mock_prefetch_service_delegate));
3231
3232 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
3233
Max Curran2e83b5d2022-12-29 18:53:383234 MakePrefetchOnMainFrame(
3235 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583236 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3237 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253238 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543239 task_environment()->RunUntilIdle();
Max Curranead64a62022-06-22 01:10:523240
3241 EXPECT_EQ(RequestCount(), 0);
3242
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:213243 ExpectPrefetchNotEligible(histogram_tester,
3244 PreloadingEligibility::kUserHasCookies);
Max Curran892ca5422022-12-12 20:55:343245
Taiyo Mizuhashi01d86af22024-03-27 14:27:423246 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463247 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki238198d82023-10-23 23:04:123248 ExpectServingMetrics(PrefetchStatus::kPrefetchIneligibleUserHasCookies);
Max Curranead64a62022-06-22 01:10:523249}
3250
Georg Neis0dce3332024-12-13 01:51:183251// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573252TEST_P(PrefetchServiceAlwaysMakeDecoyRequestTest,
kenossda6656b2024-07-23 02:18:483253 DISABLED_CHROMEOS(RedirectDecoyRequest)) {
Max Curran5d4da4b42023-03-10 23:41:463254 base::HistogramTester histogram_tester;
3255
3256 MakePrefetchService(
3257 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3258
Liviu Tinta91202302023-09-26 17:49:113259 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
Max Curran5d4da4b42023-03-10 23:41:463260 blink::StorageKey::CreateFromStringForTesting("https://redirect.com"));
Liviu Tinta91202302023-09-26 17:49:113261 service_worker_context_->AddServiceWorkerScope(
Liviu Tintaf7d62a72023-08-29 21:48:393262 GURL("https://redirect.com"),
3263 ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER);
Max Curran5d4da4b42023-03-10 23:41:463264
3265 MakePrefetchOnMainFrame(
3266 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583267 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3268 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253269 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543270 task_environment()->RunUntilIdle();
Max Curran5d4da4b42023-03-10 23:41:463271
3272 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053273 {.use_prefetch_proxy = true});
Max Curran5d4da4b42023-03-10 23:41:463274 VerifyFollowRedirectParams(0);
3275
3276 net::RedirectInfo redirect_info;
3277 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013278 redirect_info.new_referrer_policy =
3279 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Curran5d4da4b42023-03-10 23:41:463280 redirect_info.new_url = GURL("https://redirect.com");
3281 MakeSingleRedirectAndWait(
3282 redirect_info,
3283 CreateURLResponseHeadForPrefetch(
3284 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
3285 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com")));
3286
3287 // The redirect is ineligible, but will be followed since the prefetch is now
3288 // a decoy.
3289 VerifyFollowRedirectParams(1);
3290
3291 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3292 /*use_prefetch_proxy=*/true,
3293 {{"X-Testing", "Hello World"}}, kHTMLBody);
3294
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463295 ExpectPrefetchFailedBeforeResponseReceived(
3296 histogram_tester, PrefetchStatus::kPrefetchIsPrivacyDecoy);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363297
Taiyo Mizuhashi01d86af22024-03-27 14:27:423298 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363299 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463300 ExpectServingMetrics(PrefetchStatus::kPrefetchIsPrivacyDecoy,
3301 /*prefetch_header_latency=*/true);
Max Curran5d4da4b42023-03-10 23:41:463302}
3303
kenossbd9a3fc2025-03-28 05:15:573304class PrefetchServiceIncognitoTest
3305 : public PrefetchServiceTestBase,
3306 public WithPrefetchServiceRearchParam,
3307 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
3308 public:
3309 PrefetchServiceIncognitoTest() : WithPrefetchServiceRearchParam(GetParam()) {}
3310
3311 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:153312 InitBaseParams();
kenossbd9a3fc2025-03-28 05:15:573313 InitRearchFeatures();
3314 }
3315
William Liu77089052022-12-15 18:53:353316 protected:
3317 std::unique_ptr<BrowserContext> CreateBrowserContext() override {
3318 auto browser_context = std::make_unique<TestBrowserContext>();
3319 browser_context->set_is_off_the_record(true);
3320 return browser_context;
3321 }
3322};
3323
kenossbd9a3fc2025-03-28 05:15:573324INSTANTIATE_TEST_SUITE_P(
3325 ParametrizedTests,
3326 PrefetchServiceIncognitoTest,
3327 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
3328
3329TEST_P(PrefetchServiceIncognitoTest, OffTheRecordEligible) {
Jeremy Romancc6e4e62024-05-13 21:29:033330 base::HistogramTester histogram_tester;
3331
3332 MakePrefetchService(
3333 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3334
3335 MakePrefetchOnMainFrame(
3336 GURL("https://example.com/"),
3337 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3338 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:253339 blink::mojom::SpeculationEagerness::kImmediate));
Jeremy Romancc6e4e62024-05-13 21:29:033340 task_environment()->RunUntilIdle();
3341
3342 VerifyCommonRequestState(GURL("https://example.com/"));
3343 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3344 /*use_prefetch_proxy=*/false, {}, kHTMLBody);
3345 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
3346}
3347
kenossbd9a3fc2025-03-28 05:15:573348TEST_P(PrefetchServiceTest, NonDefaultStoragePartition) {
William Liu77089052022-12-15 18:53:353349 base::HistogramTester histogram_tester;
3350
3351 MakePrefetchService(
3352 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3353 test_content_browser_client_->UseOffTheRecordContextForStoragePartition(true);
3354
Max Curran2e83b5d2022-12-29 18:53:383355 MakePrefetchOnMainFrame(
3356 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583357 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Jeremy Romancc6e4e62024-05-13 21:29:033358 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:253359 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543360 task_environment()->RunUntilIdle();
William Liu77089052022-12-15 18:53:353361
3362 EXPECT_EQ(RequestCount(), 0);
3363
Adithya Srinivasan4d3dad02024-10-17 18:06:593364 histogram_tester.ExpectUniqueSample(
3365 "Preloading.Prefetch.PrefetchStatus",
3366 PrefetchStatus::kPrefetchIneligibleNonDefaultStoragePartition, 1);
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:213367 ExpectPrefetchNotEligible(histogram_tester,
3368 PreloadingEligibility::kNonDefaultStoragePartition);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363369
Taiyo Mizuhashi01d86af22024-03-27 14:27:423370 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363371 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463372 ExpectServingMetrics(
Jeremy Romancc6e4e62024-05-13 21:29:033373 PrefetchStatus::kPrefetchIneligibleNonDefaultStoragePartition,
3374 /*prefetch_header_latency=*/false,
3375 /*required_private_prefetch_proxy=*/false);
William Liu77089052022-12-15 18:53:353376}
3377
Georg Neis0dce3332024-12-13 01:51:183378// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573379TEST_P(PrefetchServiceTest, DISABLED_CHROMEOS(StreamingURLLoaderSuccessCase)) {
Max Curran892ca5422022-12-12 20:55:343380 base::HistogramTester histogram_tester;
3381
3382 MakePrefetchService(
3383 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3384
Max Curran2e83b5d2022-12-29 18:53:383385 MakePrefetchOnMainFrame(
3386 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583387 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3388 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253389 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543390 task_environment()->RunUntilIdle();
Max Curran892ca5422022-12-12 20:55:343391
3392 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053393 {.use_prefetch_proxy = true});
Max Curran892ca5422022-12-12 20:55:343394
3395 // Send the head of the navigation. The prefetch should be servable after this
3396 // point. The body of the response will be streaming to the serving URL loader
3397 // as its received.
3398 SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
3399 /*use_prefetch_proxy=*/true,
3400 {{"X-Testing", "Hello World"}},
3401 std::size(kHTMLBody));
3402
3403 // Navigate to the URL before the prefetch response is complete.
Taiyo Mizuhashi01d86af22024-03-27 14:27:423404 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Curran892ca5422022-12-12 20:55:343405
3406 // Check the metrics while the prefetch is still in progress.
3407 histogram_tester.ExpectUniqueSample(
3408 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
3409 histogram_tester.ExpectUniqueSample(
3410 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
3411 histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError",
3412 0);
3413 histogram_tester.ExpectTotalCount(
3414 "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0);
3415 histogram_tester.ExpectUniqueSample(
3416 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
3417 histogram_tester.ExpectUniqueSample(
3418 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
3419
Arthur Sonzognic686e8f2024-01-11 08:36:373420 std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
Max Curran892ca5422022-12-12 20:55:343421 PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
3422 EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1);
3423 EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1);
3424 EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0);
3425
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573426 PrefetchServingHandle serving_handle =
Max Curran2e83b5d2022-12-29 18:53:383427 GetPrefetchToServe(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573428 ASSERT_TRUE(serving_handle);
3429 EXPECT_TRUE(serving_handle.HasPrefetchStatus());
3430 EXPECT_EQ(serving_handle.GetPrefetchStatus(),
Max Curran892ca5422022-12-12 20:55:343431 PrefetchStatus::kPrefetchNotFinishedInTime);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573432 EXPECT_EQ(serving_handle.GetServableState(base::TimeDelta::Max()),
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:083433 PrefetchServableState::kServable);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573434 EXPECT_TRUE(serving_handle.GetPrefetchContainer()->GetNonRedirectHead());
3435 EXPECT_TRUE(serving_handle.GetPrefetchContainer()
kenossf7b4d60d2024-07-16 15:15:083436 ->GetNonRedirectHead()
Hiroshige Hayashizaki00b3f002023-07-15 00:32:483437 ->was_in_prefetch_cache);
Max Curran892ca5422022-12-12 20:55:343438
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363439 ExpectServingMetrics(PrefetchStatus::kPrefetchNotFinishedInTime);
3440
Max Curran892ca5422022-12-12 20:55:343441 // Send the body and completion status of the request, then recheck all of the
3442 // metrics.
3443 SendBodyContentOfResponseAndWait(kHTMLBody);
3444 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
3445
3446 // Check the metrics now that the prefetch is complete.
kenossbe8fbf22024-08-14 12:18:213447 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
Takashi Nakayama978f0a152025-06-17 08:26:253448 blink::mojom::SpeculationEagerness::kImmediate,
kenossbe8fbf22024-08-14 12:18:213449 /*is_accurate=*/true);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573450 ExpectServingReaderSuccess(serving_handle);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363451 ExpectServingMetricsSuccess();
Max Curran892ca5422022-12-12 20:55:343452}
3453
Georg Neis0dce3332024-12-13 01:51:183454// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573455TEST_P(PrefetchServiceTest, DISABLED_CHROMEOS(NoVarySearchSuccessCase)) {
William Liu77089052022-12-15 18:53:353456 base::HistogramTester histogram_tester;
3457
Liviu Tintad97a6a32022-12-08 23:28:403458 MakePrefetchService(
3459 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3460
Max Curran2e83b5d2022-12-29 18:53:383461 MakePrefetchOnMainFrame(
3462 GURL("https://example.com/?a=1"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583463 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3464 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253465 blink::mojom::SpeculationEagerness::kImmediate),
Liviu Tinta5043cae52024-06-26 00:07:293466 /*referrer=*/blink::mojom::Referrer());
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543467 task_environment()->RunUntilIdle();
Liviu Tintad97a6a32022-12-08 23:28:403468
3469 VerifyCommonRequestState(GURL("https://example.com/?a=1"),
Jeremy Roman4c9524212023-07-27 22:01:053470 {.use_prefetch_proxy = true});
Liviu Tintad97a6a32022-12-08 23:28:403471 MakeResponseAndWait(
3472 net::HTTP_OK, net::OK, kHTMLMimeType,
3473 /*use_prefetch_proxy=*/true,
3474 {{"X-Testing", "Hello World"}, {"No-Vary-Search", R"(params=("a"))"}},
3475 kHTMLBody);
3476
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463477 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463478
Taiyo Mizuhashi01d86af22024-03-27 14:27:423479 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573480 PrefetchServingHandle serving_handle =
Max Curran2e83b5d2022-12-29 18:53:383481 GetPrefetchToServe(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573482 ExpectServingReaderSuccess(serving_handle);
3483 EXPECT_EQ(serving_handle.GetPrefetchContainer()->GetURL(),
Liviu Tintad97a6a32022-12-08 23:28:403484 GURL("https://example.com/?a=1"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363485 ExpectServingMetricsSuccess();
William Liu77089052022-12-15 18:53:353486}
3487
kenossbd9a3fc2025-03-28 05:15:573488TEST_P(PrefetchServiceTest, NoVarySearchSuccessCase_Embedder) {
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:083489 base::HistogramTester histogram_tester;
3490
3491 MakePrefetchService(
3492 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
3493 /*num_on_prefetch_likely_calls=*/std::nullopt));
3494
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:003495 auto handle =
3496 MakePrefetchFromEmbedder(GURL("https://example.com?a=1"),
3497 PrefetchType(PreloadingTriggerType::kEmbedder,
3498 /*use_prefetch_proxy=*/false));
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:083499 task_environment()->RunUntilIdle();
3500
Taiyo Mizuhashid13f8ca2025-06-23 13:29:023501 VerifyCommonRequestStateForWebContentsPrefetch(
3502 GURL("https://example.com?a=1"), {.use_prefetch_proxy = false});
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:083503 MakeResponseAndWait(
3504 net::HTTP_OK, net::OK, kHTMLMimeType,
3505 /*use_prefetch_proxy=*/false,
3506 {{"X-Testing", "Hello World"}, {"No-Vary-Search", R"(params=("a"))"}},
3507 kHTMLBody);
3508
3509 // TODO(crbug.com/40269462): Revise current helper functions (ExpectPrefetch*)
3510 // for browser-initiated prefetch.
3511 histogram_tester.ExpectUniqueSample(
3512 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 1);
3513 histogram_tester.ExpectUniqueSample(
3514 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
3515 histogram_tester.ExpectUniqueSample(
3516 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
3517 histogram_tester.ExpectUniqueSample(
3518 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
3519 histogram_tester.ExpectUniqueSample(
3520 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
3521 histogram_tester.ExpectUniqueSample(
3522 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
3523
3524 NavigateInitiatedByBrowser(GURL("https://example.com"));
3525
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573526 PrefetchServingHandle serving_handle =
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:083527 GetPrefetchToServe(GURL("https://example.com"), std::nullopt);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573528 ExpectServingReaderSuccess(serving_handle);
3529 EXPECT_EQ(serving_handle.GetPrefetchContainer()->GetURL(),
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:083530 GURL("https://example.com/?a=1"));
3531}
3532
Georg Neis0dce3332024-12-13 01:51:183533// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573534TEST_P(PrefetchServiceTest, DISABLED_CHROMEOS(PrefetchEligibleRedirect)) {
William Liu77089052022-12-15 18:53:353535 base::HistogramTester histogram_tester;
3536
3537 MakePrefetchService(
3538 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3539
Max Curran2e83b5d2022-12-29 18:53:383540 MakePrefetchOnMainFrame(
3541 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583542 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3543 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253544 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543545 task_environment()->RunUntilIdle();
William Liu77089052022-12-15 18:53:353546
3547 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053548 {.use_prefetch_proxy = true});
Max Curran5d4da4b42023-03-10 23:41:463549 VerifyFollowRedirectParams(0);
William Liu77089052022-12-15 18:53:353550
Max Curran5d4da4b42023-03-10 23:41:463551 net::RedirectInfo redirect_info;
3552 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013553 redirect_info.new_referrer_policy =
3554 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Curran5d4da4b42023-03-10 23:41:463555 redirect_info.new_url = GURL("https://redirect.com");
3556 MakeSingleRedirectAndWait(
3557 redirect_info,
3558 CreateURLResponseHeadForPrefetch(
3559 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
3560 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com")));
3561 VerifyFollowRedirectParams(1);
3562
Max Curran7d2578b2023-04-12 19:19:283563 histogram_tester.ExpectUniqueSample(
3564 "PrefetchProxy.Redirect.Result",
3565 PrefetchRedirectResult::kSuccessRedirectFollowed, 1);
3566 histogram_tester.ExpectUniqueSample(
3567 "PrefetchProxy.Redirect.NetworkContextStateTransition",
3568 PrefetchRedirectNetworkContextTransition::kIsolatedToIsolated, 1);
3569
Max Curran5d4da4b42023-03-10 23:41:463570 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3571 /*use_prefetch_proxy=*/true,
3572 {{"X-Testing", "Hello World"}}, kHTMLBody);
3573
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463574 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363575
Taiyo Mizuhashi01d86af22024-03-27 14:27:423576 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463577 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363578 ExpectServingMetricsSuccess();
Max Curran5d4da4b42023-03-10 23:41:463579
Max Curran7d2578b2023-04-12 19:19:283580 histogram_tester.ExpectUniqueSample(
3581 "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1);
Max Curran5d4da4b42023-03-10 23:41:463582}
3583
Georg Neis0dce3332024-12-13 01:51:183584// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573585TEST_P(PrefetchServiceTest, DISABLED_CHROMEOS(IneligibleRedirectCookies)) {
Max Curran5d4da4b42023-03-10 23:41:463586 base::HistogramTester histogram_tester;
3587
3588 MakePrefetchService(
3589 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3590
3591 ASSERT_TRUE(SetCookie(GURL("https://redirect.com"), "testing"));
3592
3593 MakePrefetchOnMainFrame(
3594 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583595 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3596 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253597 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543598 task_environment()->RunUntilIdle();
Max Curran5d4da4b42023-03-10 23:41:463599
3600 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053601 {.use_prefetch_proxy = true});
Max Curran5d4da4b42023-03-10 23:41:463602 VerifyFollowRedirectParams(0);
3603
kenossb2776822024-09-13 16:07:563604 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:573605 base::test::TestFuture<PrefetchServingHandle> future;
kenossb2776822024-09-13 16:07:563606 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
3607
Max Curran5d4da4b42023-03-10 23:41:463608 net::RedirectInfo redirect_info;
3609 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013610 redirect_info.new_referrer_policy =
3611 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Curran5d4da4b42023-03-10 23:41:463612 redirect_info.new_url = GURL("https://redirect.com");
3613 MakeSingleRedirectAndWait(
3614 redirect_info,
3615 CreateURLResponseHeadForPrefetch(
3616 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
3617 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com")));
3618
3619 // Since the redirect URL has cookies, it is ineligible for prefetching and
3620 // causes the prefetch to fail. Also since checking if the URL has cookies
3621 // requires mojo, the eligibility check will not complete immediately.
3622 VerifyFollowRedirectParams(0);
3623
kenossb2776822024-09-13 16:07:563624 // Falls back to normal navigation.
3625 EXPECT_FALSE(future.Take());
3626
Max Curran7d2578b2023-04-12 19:19:283627 histogram_tester.ExpectUniqueSample("PrefetchProxy.Redirect.Result",
3628 PrefetchRedirectResult::kFailedIneligible,
3629 1);
3630 histogram_tester.ExpectUniqueSample(
3631 "PrefetchProxy.Redirect.NetworkContextStateTransition",
3632 PrefetchRedirectNetworkContextTransition::kIsolatedToIsolated, 1);
3633
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463634 ExpectPrefetchFailedBeforeResponseReceived(
kenossb2776822024-09-13 16:07:563635 histogram_tester, PrefetchStatus::kPrefetchFailedIneligibleRedirect,
3636 /*is_accurate=*/true);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363637
Taiyo Mizuhashi01d86af22024-03-27 14:27:423638 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463639 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363640 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedIneligibleRedirect);
Max Curran5d4da4b42023-03-10 23:41:463641
Max Curran7d2578b2023-04-12 19:19:283642 histogram_tester.ExpectTotalCount(
3643 "PrefetchProxy.AfterClick.RedirectChainSize", 0);
Max Curran5d4da4b42023-03-10 23:41:463644}
3645
Georg Neis0dce3332024-12-13 01:51:183646// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573647TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:483648 DISABLED_CHROMEOS(IneligibleRedirectServiceWorker)) {
Max Curran5d4da4b42023-03-10 23:41:463649 base::HistogramTester histogram_tester;
3650
3651 MakePrefetchService(
3652 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3653
Liviu Tinta91202302023-09-26 17:49:113654 service_worker_context_->AddRegistrationToRegisteredStorageKeys(
Max Curran5d4da4b42023-03-10 23:41:463655 blink::StorageKey::CreateFromStringForTesting("https://redirect.com"));
Liviu Tinta91202302023-09-26 17:49:113656 service_worker_context_->AddServiceWorkerScope(
Liviu Tintaf7d62a72023-08-29 21:48:393657 GURL("https://redirect.com"),
3658 ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER);
Max Curran5d4da4b42023-03-10 23:41:463659
3660 MakePrefetchOnMainFrame(
3661 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583662 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3663 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253664 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543665 task_environment()->RunUntilIdle();
Max Curran5d4da4b42023-03-10 23:41:463666
3667 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053668 {.use_prefetch_proxy = true});
Max Curran5d4da4b42023-03-10 23:41:463669 VerifyFollowRedirectParams(0);
3670
3671 net::RedirectInfo redirect_info;
3672 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013673 redirect_info.new_referrer_policy =
3674 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Curran5d4da4b42023-03-10 23:41:463675 redirect_info.new_url = GURL("https://redirect.com");
3676 MakeSingleRedirectAndWait(
3677 redirect_info,
3678 CreateURLResponseHeadForPrefetch(
3679 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
3680 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com")));
3681
3682 // Since the redirect URL has cookies, it is ineligible for prefetching and
3683 // causes the prefetch to fail. Also the eligibility check should fail
3684 // immediately.
3685 VerifyFollowRedirectParams(0);
3686
Max Curran7d2578b2023-04-12 19:19:283687 histogram_tester.ExpectUniqueSample("PrefetchProxy.Redirect.Result",
3688 PrefetchRedirectResult::kFailedIneligible,
3689 1);
3690 histogram_tester.ExpectUniqueSample(
3691 "PrefetchProxy.Redirect.NetworkContextStateTransition",
3692 PrefetchRedirectNetworkContextTransition::kIsolatedToIsolated, 1);
3693
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463694 ExpectPrefetchFailedBeforeResponseReceived(
3695 histogram_tester, PrefetchStatus::kPrefetchFailedIneligibleRedirect);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363696
Taiyo Mizuhashi01d86af22024-03-27 14:27:423697 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463698 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363699 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedIneligibleRedirect);
Max Curran5d4da4b42023-03-10 23:41:463700
Max Curran7d2578b2023-04-12 19:19:283701 histogram_tester.ExpectTotalCount(
3702 "PrefetchProxy.AfterClick.RedirectChainSize", 0);
Max Curran5d4da4b42023-03-10 23:41:463703}
3704
Georg Neis0dce3332024-12-13 01:51:183705// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573706TEST_P(PrefetchServiceTest, DISABLED_CHROMEOS(InvalidRedirect)) {
Max Curran5d4da4b42023-03-10 23:41:463707 base::HistogramTester histogram_tester;
3708
3709 MakePrefetchService(
3710 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3711
3712 MakePrefetchOnMainFrame(
3713 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583714 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3715 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253716 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543717 task_environment()->RunUntilIdle();
Max Curran5d4da4b42023-03-10 23:41:463718
3719 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053720 {.use_prefetch_proxy = true});
Max Curran5d4da4b42023-03-10 23:41:463721 VerifyFollowRedirectParams(0);
3722
3723 // The redirect is considered invalid because it has a non-3XX HTTP code.
3724 net::RedirectInfo redirect_info;
3725 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013726 redirect_info.new_referrer_policy =
3727 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Curran5d4da4b42023-03-10 23:41:463728 redirect_info.new_url = GURL("https://redirect.com");
3729 MakeSingleRedirectAndWait(redirect_info, CreateURLResponseHeadForPrefetch(
3730 net::HTTP_OK, kHTMLMimeType,
3731 /*use_prefetch_proxy=*/true, {},
3732 GURL("https://redirect.com")));
3733 VerifyFollowRedirectParams(0);
William Liu77089052022-12-15 18:53:353734
Max Curran7d2578b2023-04-12 19:19:283735 histogram_tester.ExpectUniqueSample(
3736 "PrefetchProxy.Redirect.Result",
3737 PrefetchRedirectResult::kFailedInvalidResponseCode, 1);
3738 histogram_tester.ExpectTotalCount(
3739 "PrefetchProxy.Redirect.NetworkContextStateTransition", 0);
3740
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463741 ExpectPrefetchFailedBeforeResponseReceived(
3742 histogram_tester, PrefetchStatus::kPrefetchFailedInvalidRedirect);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363743
Taiyo Mizuhashi01d86af22024-03-27 14:27:423744 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463745 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363746 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedInvalidRedirect);
William Liu77089052022-12-15 18:53:353747
Max Curran7d2578b2023-04-12 19:19:283748 histogram_tester.ExpectTotalCount(
3749 "PrefetchProxy.AfterClick.RedirectChainSize", 0);
Max Curran7d2578b2023-04-12 19:19:283750}
3751
Georg Neis0dce3332024-12-13 01:51:183752// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573753TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:483754 DISABLED_CHROMEOS(PrefetchSameOriginEligibleRedirect)) {
Kevin McNee06824c72024-02-06 18:59:523755 NavigationSimulator::NavigateAndCommitFromBrowser(
3756 web_contents(), GURL("https://example.com/referrer"));
Max Curran7d2578b2023-04-12 19:19:283757 base::HistogramTester histogram_tester;
3758
3759 MakePrefetchService(
3760 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3761
3762 MakePrefetchOnMainFrame(
3763 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583764 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3765 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:253766 blink::mojom::SpeculationEagerness::kImmediate));
Max Curran7d2578b2023-04-12 19:19:283767
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543768 task_environment()->RunUntilIdle();
Max Curran7d2578b2023-04-12 19:19:283769
Jeremy Roman4c9524212023-07-27 22:01:053770 VerifyCommonRequestState(GURL("https://example.com"));
Max Curran7d2578b2023-04-12 19:19:283771 VerifyFollowRedirectParams(0);
3772
3773 net::RedirectInfo redirect_info;
3774 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013775 redirect_info.new_referrer_policy =
3776 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Curran7d2578b2023-04-12 19:19:283777 redirect_info.new_url = GURL("https://example.com/redirect");
3778 MakeSingleRedirectAndWait(redirect_info,
3779 CreateURLResponseHeadForPrefetch(
3780 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
3781 /*use_prefetch_proxy=*/true, {},
3782 GURL("https://example.com/redirect")));
3783 VerifyFollowRedirectParams(1);
3784
3785 histogram_tester.ExpectUniqueSample(
3786 "PrefetchProxy.Redirect.Result",
3787 PrefetchRedirectResult::kSuccessRedirectFollowed, 1);
3788 histogram_tester.ExpectUniqueSample(
3789 "PrefetchProxy.Redirect.NetworkContextStateTransition",
3790 PrefetchRedirectNetworkContextTransition::kDefaultToDefault, 1);
3791
3792 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3793 /*use_prefetch_proxy=*/false,
3794 {{"X-Testing", "Hello World"}}, kHTMLBody);
3795
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463796 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363797
Taiyo Mizuhashi01d86af22024-03-27 14:27:423798 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463799 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363800 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
Max Curran7d2578b2023-04-12 19:19:283801
3802 histogram_tester.ExpectUniqueSample(
3803 "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1);
Max Curran7d2578b2023-04-12 19:19:283804}
3805
Georg Neis0dce3332024-12-13 01:51:183806// TODO(crbug.com/40249481): Test flaky on trybots.
Alison Gale770f3fc2024-04-27 00:39:583807// TODO(crbug.com/40265797): This test is testing the current
Max Curranc0137502023-05-02 18:06:223808// functionality, and should be removed while fixing this bug.
kenossbd9a3fc2025-03-28 05:15:573809TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:483810 DISABLED_CHROMEOS(IneligibleSameSiteCrossOriginRequiresProxyRedirect)) {
Kevin McNee06824c72024-02-06 18:59:523811 NavigationSimulator::NavigateAndCommitFromBrowser(
3812 web_contents(), GURL("https://example.com/referrer"));
Max Curranc0137502023-05-02 18:06:223813 base::HistogramTester histogram_tester;
3814
3815 MakePrefetchService(
3816 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3817
3818 MakePrefetchOnMainFrame(
3819 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583820 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3821 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253822 blink::mojom::SpeculationEagerness::kImmediate));
Max Curranc0137502023-05-02 18:06:223823
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543824 task_environment()->RunUntilIdle();
Max Curranc0137502023-05-02 18:06:223825
3826 // The request to the same-origin prefetch URL should ignore the proxy
3827 // requirement, since it only applies to cross-origin prefetches.
Jeremy Roman4c9524212023-07-27 22:01:053828 VerifyCommonRequestState(GURL("https://example.com"));
Max Curranc0137502023-05-02 18:06:223829 VerifyFollowRedirectParams(0);
3830
3831 // Redirect to a same-site cross-origin URL. The proxy requirement should
3832 // apply to this URL, and result in the redirect being marked as ineligible,
3833 // because we cannot make same-site cross-origin requests that require the
3834 // proxy.
3835 net::RedirectInfo redirect_info;
3836 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013837 redirect_info.new_referrer_policy =
3838 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Curranc0137502023-05-02 18:06:223839 redirect_info.new_url = GURL("https://other.example.com/redirect");
3840 MakeSingleRedirectAndWait(redirect_info,
3841 CreateURLResponseHeadForPrefetch(
3842 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
3843 /*use_prefetch_proxy=*/true, {},
3844 GURL("https://example.com/redirect")));
3845 VerifyFollowRedirectParams(0);
3846
3847 histogram_tester.ExpectUniqueSample("PrefetchProxy.Redirect.Result",
3848 PrefetchRedirectResult::kFailedIneligible,
3849 1);
3850 histogram_tester.ExpectUniqueSample(
3851 "PrefetchProxy.Redirect.NetworkContextStateTransition",
3852 PrefetchRedirectNetworkContextTransition::kDefaultToDefault, 1);
3853
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463854 ExpectPrefetchFailedBeforeResponseReceived(
3855 histogram_tester, PrefetchStatus::kPrefetchFailedIneligibleRedirect);
Max Curranc0137502023-05-02 18:06:223856
Taiyo Mizuhashi01d86af22024-03-27 14:27:423857 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363858 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
3859 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedIneligibleRedirect);
Max Curranc0137502023-05-02 18:06:223860 histogram_tester.ExpectTotalCount(
3861 "PrefetchProxy.AfterClick.RedirectChainSize", 0);
Max Curranc0137502023-05-02 18:06:223862}
3863
Georg Neis0dce3332024-12-13 01:51:183864// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573865TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:483866 DISABLED_CHROMEOS(RedirectDefaultToIsolatedNetworkContextTransition)) {
Kevin McNee06824c72024-02-06 18:59:523867 NavigationSimulator::NavigateAndCommitFromBrowser(
3868 web_contents(), GURL("https://example.com/referrer"));
Max Curran7d2578b2023-04-12 19:19:283869 base::HistogramTester histogram_tester;
3870
3871 MakePrefetchService(
3872 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3873
3874 MakePrefetchOnMainFrame(
3875 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583876 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3877 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:253878 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543879 task_environment()->RunUntilIdle();
Max Curran7d2578b2023-04-12 19:19:283880
Jeremy Roman4c9524212023-07-27 22:01:053881 VerifyCommonRequestState(GURL("https://example.com"));
Max Curran7d2578b2023-04-12 19:19:283882 VerifyFollowRedirectParams(0);
3883
3884 net::RedirectInfo redirect_info;
3885 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013886 redirect_info.new_referrer_policy =
3887 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Curran7d2578b2023-04-12 19:19:283888 redirect_info.new_url = GURL("https://redirect.com");
3889 MakeSingleRedirectAndWait(
3890 redirect_info,
3891 CreateURLResponseHeadForPrefetch(
3892 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
3893 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com")));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543894 task_environment()->RunUntilIdle();
Max Currana84311e2023-05-16 20:40:253895
3896 // Since the redirect is cross-site compared to the referrer. A new request
3897 // will be started in an isolated network context, and the redirect will not
3898 // be followed directly.
Max Curran7d2578b2023-04-12 19:19:283899 VerifyFollowRedirectParams(0);
Max Currana84311e2023-05-16 20:40:253900 ClearCompletedRequests();
Jeremy Roman4c9524212023-07-27 22:01:053901 VerifyCommonRequestState(GURL("https://redirect.com"));
Max Curran7d2578b2023-04-12 19:19:283902
3903 histogram_tester.ExpectUniqueSample(
3904 "PrefetchProxy.Redirect.Result",
Max Currana84311e2023-05-16 20:40:253905 PrefetchRedirectResult::kSuccessRedirectFollowed, 1);
Max Curran7d2578b2023-04-12 19:19:283906 histogram_tester.ExpectUniqueSample(
3907 "PrefetchProxy.Redirect.NetworkContextStateTransition",
3908 PrefetchRedirectNetworkContextTransition::kDefaultToIsolated, 1);
3909
Max Currana84311e2023-05-16 20:40:253910 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3911 /*use_prefetch_proxy=*/false,
3912 {{"X-Testing", "Hello World"}}, kHTMLBody);
3913
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463914 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363915
Taiyo Mizuhashi01d86af22024-03-27 14:27:423916 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463917 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363918 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
Max Curran7d2578b2023-04-12 19:19:283919
Max Currana84311e2023-05-16 20:40:253920 histogram_tester.ExpectUniqueSample(
3921 "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1);
Max Currana84311e2023-05-16 20:40:253922}
3923
Georg Neis0dce3332024-12-13 01:51:183924// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573925TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:483926 DISABLED_CHROMEOS(
3927 RedirectDefaultToIsolatedNetworkContextTransitionWithProxy)) {
Kevin McNee06824c72024-02-06 18:59:523928 NavigationSimulator::NavigateAndCommitFromBrowser(
3929 web_contents(), GURL("https://example.com/referrer"));
Max Currana84311e2023-05-16 20:40:253930 base::HistogramTester histogram_tester;
3931
3932 MakePrefetchService(
3933 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3934
3935 MakePrefetchOnMainFrame(
3936 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:583937 PrefetchType(PreloadingTriggerType::kSpeculationRule,
3938 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:253939 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543940 task_environment()->RunUntilIdle();
Max Currana84311e2023-05-16 20:40:253941
3942 // The same-origin request should not use the proxy.
3943 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:053944 {.use_prefetch_proxy = false});
Max Currana84311e2023-05-16 20:40:253945 VerifyFollowRedirectParams(0);
3946
3947 net::RedirectInfo redirect_info;
3948 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:013949 redirect_info.new_referrer_policy =
3950 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Currana84311e2023-05-16 20:40:253951 redirect_info.new_url = GURL("https://redirect.com");
3952 MakeSingleRedirectAndWait(
3953 redirect_info,
3954 CreateURLResponseHeadForPrefetch(
3955 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
3956 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com")));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:543957 task_environment()->RunUntilIdle();
Max Currana84311e2023-05-16 20:40:253958
3959 // Since the redirect is cross-site compared to the referrer. A new request
3960 // will be started in an isolated network context, and the redirect will not
3961 // be followed directly. The new request should use the proxy.
3962 VerifyFollowRedirectParams(0);
3963 ClearCompletedRequests();
3964 VerifyCommonRequestState(GURL("https://redirect.com"),
Jeremy Roman4c9524212023-07-27 22:01:053965 {.use_prefetch_proxy = true});
Max Currana84311e2023-05-16 20:40:253966
3967 histogram_tester.ExpectUniqueSample(
3968 "PrefetchProxy.Redirect.Result",
3969 PrefetchRedirectResult::kSuccessRedirectFollowed, 1);
3970 histogram_tester.ExpectUniqueSample(
3971 "PrefetchProxy.Redirect.NetworkContextStateTransition",
3972 PrefetchRedirectNetworkContextTransition::kDefaultToIsolated, 1);
3973
3974 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
3975 /*use_prefetch_proxy=*/false,
3976 {{"X-Testing", "Hello World"}}, kHTMLBody);
3977
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463978 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363979
Taiyo Mizuhashi01d86af22024-03-27 14:27:423980 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:463981 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:363982 ExpectServingMetricsSuccess();
Max Currana84311e2023-05-16 20:40:253983
3984 histogram_tester.ExpectUniqueSample(
3985 "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1);
Max Currana84311e2023-05-16 20:40:253986}
3987
Georg Neis0dce3332024-12-13 01:51:183988// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:573989TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:483990 DISABLED_CHROMEOS(RedirectIsolatedToDefaultNetworkContextTransition)) {
Kevin McNee06824c72024-02-06 18:59:523991 NavigationSimulator::NavigateAndCommitFromBrowser(
3992 web_contents(), GURL("https://example.com/referrer"));
Max Currana84311e2023-05-16 20:40:253993 base::HistogramTester histogram_tester;
3994
3995 MakePrefetchService(
3996 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
3997
3998 MakePrefetchOnMainFrame(
3999 GURL("https://other.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:584000 PrefetchType(PreloadingTriggerType::kSpeculationRule,
4001 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:254002 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544003 task_environment()->RunUntilIdle();
Max Currana84311e2023-05-16 20:40:254004
4005 VerifyCommonRequestState(GURL("https://other.com"),
Jeremy Roman4c9524212023-07-27 22:01:054006 {.use_prefetch_proxy = false});
Max Currana84311e2023-05-16 20:40:254007 VerifyFollowRedirectParams(0);
4008
4009 net::RedirectInfo redirect_info;
4010 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:014011 redirect_info.new_referrer_policy =
4012 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Currana84311e2023-05-16 20:40:254013 redirect_info.new_url = GURL("https://example.com/redirect");
4014 MakeSingleRedirectAndWait(redirect_info,
4015 CreateURLResponseHeadForPrefetch(
4016 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
4017 /*use_prefetch_proxy=*/true, {},
4018 GURL("https://example.com/redirect")));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544019 task_environment()->RunUntilIdle();
Max Currana84311e2023-05-16 20:40:254020
4021 // Since the redirect is same-site compared to the referrer. A new request
4022 // will be started in the default network context, and the redirect will not
4023 // be followed directly.
4024 VerifyFollowRedirectParams(0);
4025 ClearCompletedRequests();
4026 VerifyCommonRequestState(GURL("https://example.com/redirect"),
Jeremy Roman4c9524212023-07-27 22:01:054027 {.use_prefetch_proxy = false});
Max Currana84311e2023-05-16 20:40:254028
4029 histogram_tester.ExpectUniqueSample(
4030 "PrefetchProxy.Redirect.Result",
4031 PrefetchRedirectResult::kSuccessRedirectFollowed, 1);
4032 histogram_tester.ExpectUniqueSample(
4033 "PrefetchProxy.Redirect.NetworkContextStateTransition",
4034 PrefetchRedirectNetworkContextTransition::kIsolatedToDefault, 1);
4035
4036 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
4037 /*use_prefetch_proxy=*/false,
4038 {{"X-Testing", "Hello World"}}, kHTMLBody);
4039
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464040 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:364041
Taiyo Mizuhashi01d86af22024-03-27 14:27:424042 NavigateInitiatedByRenderer(GURL("https://other.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464043 ExpectServingReaderSuccess(GetPrefetchToServe(GURL("https://other.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:364044 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
Max Currana84311e2023-05-16 20:40:254045
4046 histogram_tester.ExpectUniqueSample(
4047 "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1);
Max Currana84311e2023-05-16 20:40:254048}
4049
Georg Neis0dce3332024-12-13 01:51:184050// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:574051TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:484052 DISABLED_CHROMEOS(RedirectNetworkContextTransitionBlockUntilHead)) {
Kevin McNee06824c72024-02-06 18:59:524053 NavigationSimulator::NavigateAndCommitFromBrowser(
4054 web_contents(), GURL("https://example.com/referrer"));
Max Currana84311e2023-05-16 20:40:254055 base::HistogramTester histogram_tester;
4056
4057 MakePrefetchService(
4058 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
4059
4060 MakePrefetchOnMainFrame(
4061 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:584062 PrefetchType(PreloadingTriggerType::kSpeculationRule,
4063 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:254064 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544065 task_environment()->RunUntilIdle();
Max Currana84311e2023-05-16 20:40:254066
Jeremy Roman4c9524212023-07-27 22:01:054067 VerifyCommonRequestState(GURL("https://example.com"));
Max Currana84311e2023-05-16 20:40:254068 VerifyFollowRedirectParams(0);
4069
Taiyo Mizuhashi01d86af22024-03-27 14:27:424070 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Currana84311e2023-05-16 20:40:254071
4072 // Request the prefetch from the PrefetchService. The given callback shouldn't
4073 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574074 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizaki2df45292023-10-10 22:59:034075 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:334076 EXPECT_FALSE(future.IsReady());
Max Currana84311e2023-05-16 20:40:254077
4078 net::RedirectInfo redirect_info;
4079 redirect_info.new_method = "GET";
Devlin Cronin7f318c12023-06-09 00:57:014080 redirect_info.new_referrer_policy =
4081 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
Max Currana84311e2023-05-16 20:40:254082 redirect_info.new_url = GURL("https://redirect.com");
4083 MakeSingleRedirectAndWait(
4084 redirect_info,
4085 CreateURLResponseHeadForPrefetch(
4086 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
4087 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com")));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544088 task_environment()->RunUntilIdle();
Max Currana84311e2023-05-16 20:40:254089
4090 // Since the redirect is cross-site compared to the referrer. A new request
4091 // will be started in an isolated network context, and the redirect will not
4092 // be followed directly.
4093 VerifyFollowRedirectParams(0);
4094 ClearCompletedRequests();
Jeremy Roman4c9524212023-07-27 22:01:054095 VerifyCommonRequestState(GURL("https://redirect.com"));
Max Currana84311e2023-05-16 20:40:254096
4097 histogram_tester.ExpectUniqueSample(
4098 "PrefetchProxy.Redirect.Result",
4099 PrefetchRedirectResult::kSuccessRedirectFollowed, 1);
4100 histogram_tester.ExpectUniqueSample(
4101 "PrefetchProxy.Redirect.NetworkContextStateTransition",
4102 PrefetchRedirectNetworkContextTransition::kDefaultToIsolated, 1);
4103
4104 // Once the final response to the prefetch is received, then callback given to
4105 // |GetPrefetchToServe| should be run.
4106 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
4107 /*use_prefetch_proxy=*/false,
4108 {{"X-Testing", "Hello World"}}, kHTMLBody);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574109 PrefetchServingHandle serving_handle = future.Take();
4110 ASSERT_TRUE(serving_handle);
Max Currana84311e2023-05-16 20:40:254111
kenossbe8fbf22024-08-14 12:18:214112 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
Takashi Nakayama978f0a152025-06-17 08:26:254113 blink::mojom::SpeculationEagerness::kImmediate,
kenossbe8fbf22024-08-14 12:18:214114 /*is_accurate=*/true);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574115 ExpectServingReaderSuccess(serving_handle);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:364116 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
Max Currana84311e2023-05-16 20:40:254117
4118 histogram_tester.ExpectUniqueSample(
4119 "PrefetchProxy.AfterClick.RedirectChainSize", 2, 1);
Liviu Tintad97a6a32022-12-08 23:28:404120}
4121
Georg Neis0dce3332024-12-13 01:51:184122// TODO(crbug.com/40249481): Test flaky on trybots.
kenossbd9a3fc2025-03-28 05:15:574123TEST_P(PrefetchServiceTest,
kenossda6656b2024-07-23 02:18:484124 DISABLED_CHROMEOS(RedirectInsufficientReferrerPolicy)) {
Kevin McNee06824c72024-02-06 18:59:524125 NavigationSimulator::NavigateAndCommitFromBrowser(
4126 web_contents(), GURL("https://referrer.com"));
Devlin Cronin7f318c12023-06-09 00:57:014127 base::HistogramTester histogram_tester;
4128
4129 MakePrefetchService(
4130 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
4131
4132 blink::mojom::Referrer referrer;
4133 referrer.url = GURL("https://referrer.com");
4134 referrer.policy = network::mojom::ReferrerPolicy::kDefault;
4135 MakePrefetchOnMainFrame(
4136 GURL("https://example.com"),
Kouhei Uenoc5c2ea202023-11-14 08:58:584137 PrefetchType(PreloadingTriggerType::kSpeculationRule,
4138 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:254139 blink::mojom::SpeculationEagerness::kImmediate),
Devlin Cronin7f318c12023-06-09 00:57:014140 /*referrer=*/referrer);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544141 task_environment()->RunUntilIdle();
Devlin Cronin7f318c12023-06-09 00:57:014142
4143 VerifyCommonRequestState(GURL("https://example.com"),
Jeremy Roman4c9524212023-07-27 22:01:054144 {.use_prefetch_proxy = true});
Devlin Cronin7f318c12023-06-09 00:57:014145 VerifyFollowRedirectParams(0);
4146
4147 // Redirect to a different site. This will check the referrer policy, but
4148 // since it is not sufficiently strict, the redirect should fail.
4149 net::RedirectInfo redirect_info;
4150 redirect_info.new_method = "GET";
4151 redirect_info.new_referrer_policy = net::ReferrerPolicy::NEVER_CLEAR;
4152 redirect_info.new_url = GURL("https://redirect.com");
4153 MakeSingleRedirectAndWait(
4154 redirect_info,
4155 CreateURLResponseHeadForPrefetch(
4156 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
4157 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com")));
4158 VerifyFollowRedirectParams(0);
4159
4160 histogram_tester.ExpectUniqueSample(
4161 "PrefetchProxy.Redirect.Result",
4162 PrefetchRedirectResult::kFailedInsufficientReferrerPolicy, 1);
4163 histogram_tester.ExpectTotalCount(
4164 "PrefetchProxy.Redirect.NetworkContextStateTransition", 0);
4165
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464166 ExpectPrefetchFailedBeforeResponseReceived(
4167 histogram_tester, PrefetchStatus::kPrefetchFailedInvalidRedirect);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:364168
Taiyo Mizuhashi01d86af22024-03-27 14:27:424169 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464170 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com")));
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:364171 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedInvalidRedirect);
Devlin Cronin7f318c12023-06-09 00:57:014172
4173 histogram_tester.ExpectTotalCount(
4174 "PrefetchProxy.AfterClick.RedirectChainSize", 0);
Devlin Cronin7f318c12023-06-09 00:57:014175}
4176
Iman Saboori6e3bb0c2023-01-13 19:57:084177class PrefetchServiceAlwaysBlockUntilHeadTest
kenoss972e8682024-09-05 00:20:144178 : public PrefetchServiceTestBase,
kenossbd9a3fc2025-03-28 05:15:574179 public WithPrefetchServiceRearchParam,
4180 public ::testing::WithParamInterface<
4181 std::tuple<PrefetchServiceRearchParam::Arg,
4182 blink::mojom::SpeculationEagerness>> {
Max Curran2e83b5d2022-12-29 18:53:384183 public:
kenossbd9a3fc2025-03-28 05:15:574184 PrefetchServiceAlwaysBlockUntilHeadTest()
4185 : WithPrefetchServiceRearchParam(std::get<0>(GetParam())) {}
4186
Liviu Tinta1fe1a6d2023-09-20 19:44:044187 const int kPrefetchTimeout = 10000;
Hiroshige Hayashizaki8d4cc042023-10-18 01:13:544188 const int kBlockUntilHeadTimeout = 1000;
Max Curran2e83b5d2022-12-29 18:53:384189 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:154190 InitBaseParams();
4191 InitRearchFeatures();
4192 // Override `kPrefetchUseContentRefactor`.
Max Curran2e83b5d2022-12-29 18:53:384193 scoped_feature_list_.InitWithFeaturesAndParameters(
Johanna9fe85f2023-01-17 10:15:434194 {{features::kPrefetchUseContentRefactor,
Max Currane6679ca2023-06-06 19:01:394195 {
4196 {"ineligible_decoy_request_probability", "0"},
4197 {"prefetch_container_lifetime_s", "-1"},
4198 {"prefetch_timeout_ms", "10000"},
Liviu Tinta1fe1a6d2023-09-20 19:44:044199 // Initialize timeouts > 0ms for testing purposes.
Takashi Nakayamac96a5de2025-06-24 04:07:434200 {"block_until_head_timeout_immediate_prefetch", "1000"},
Takashi Nakayama8ec9dbf2025-06-27 04:03:154201 {"block_until_head_timeout_eager_prefetch", "1000"},
Liviu Tinta1fe1a6d2023-09-20 19:44:044202 {"block_until_head_timeout_moderate_prefetch", "1000"},
4203 {"block_until_head_timeout_conservative_prefetch", "1000"},
kenossc6c712a2025-03-10 23:17:024204 }}},
kenoss968f5dd2025-03-10 06:27:404205 {});
kenossbd9a3fc2025-03-28 05:15:574206 }
4207
4208 blink::mojom::SpeculationEagerness GetEagernessParam() {
4209 return std::get<1>(GetParam());
Max Curran2e83b5d2022-12-29 18:53:384210 }
kenoss557d4092025-04-01 05:01:154211
4212 private:
4213 base::test::ScopedFeatureList scoped_feature_list_;
Max Curran2e83b5d2022-12-29 18:53:384214};
4215
kenossbd9a3fc2025-03-28 05:15:574216INSTANTIATE_TEST_SUITE_P(
4217 ParametrizedTests,
4218 PrefetchServiceAlwaysBlockUntilHeadTest,
4219 testing::Combine(
4220 testing::ValuesIn(PrefetchServiceRearchParam::Params()),
4221 testing::Values(blink::mojom::SpeculationEagerness::kModerate,
4222 blink::mojom::SpeculationEagerness::kConservative)));
4223
Georg Neis0dce3332024-12-13 01:51:184224// TODO(crbug.com/40249481): Test flaky on trybots.
kenossda6656b2024-07-23 02:18:484225TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
4226 DISABLED_CHROMEOS(BlockUntilHeadReceived)) {
Max Curran2e83b5d2022-12-29 18:53:384227 base::HistogramTester histogram_tester;
4228
4229 MakePrefetchService(
4230 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
4231
Taiyo Mizuhashi43066071f2025-04-25 23:40:224232 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:584233 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:224234 /*use_prefetch_proxy=*/true, GetEagernessParam());
4235 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544236 task_environment()->RunUntilIdle();
Max Curran2e83b5d2022-12-29 18:53:384237
Jeremy Roman4c9524212023-07-27 22:01:054238 VerifyCommonRequestState(
4239 GURL("https://example.com"),
4240 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:574241 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Max Curran2e83b5d2022-12-29 18:53:384242
4243 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:424244 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Curran2e83b5d2022-12-29 18:53:384245
4246 // Request the prefetch from the PrefetchService. The given callback shouldn't
4247 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574248 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizaki2df45292023-10-10 22:59:034249 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:334250 EXPECT_FALSE(future.IsReady());
Max Curran2e83b5d2022-12-29 18:53:384251
Max Currand74e811f2023-06-01 19:40:044252 task_environment()->FastForwardBy(base::Milliseconds(500));
4253
Max Curran2e83b5d2022-12-29 18:53:384254 // Sends the head of the prefetch response. This should trigger the above
4255 // callback.
4256 SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
4257 /*use_prefetch_proxy=*/true,
4258 {{"X-Testing", "Hello World"}},
4259 std::size(kHTMLBody));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574260 PrefetchServingHandle serving_handle = future.Take();
4261 ASSERT_TRUE(serving_handle);
Max Curran2e83b5d2022-12-29 18:53:384262
4263 // Send the body and completion status of the request,
4264 SendBodyContentOfResponseAndWait(kHTMLBody);
4265 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
4266
4267 // Check the metrics now that the prefetch is complete.
kenossbd9a3fc2025-03-28 05:15:574268 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
4269 GetEagernessParam(),
kenossbe8fbf22024-08-14 12:18:214270 /*is_accurate=*/true);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574271 ExpectServingReaderSuccess(serving_handle);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:364272 ExpectServingMetricsSuccess();
Max Currand74e811f2023-06-01 19:40:044273
4274 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224275 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
Max Currand74e811f2023-06-01 19:40:044276 histogram_tester.ExpectUniqueTimeSample(
4277 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224278 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404279 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044280 base::Milliseconds(500), 1);
kenoss968f5dd2025-03-10 06:27:404281 histogram_tester.ExpectTotalCount(
Max Currand74e811f2023-06-01 19:40:044282 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224283 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404284 histogram_suffix),
4285 0);
4286 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224287 base::StringPrintf(
4288 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4289 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044290 true, 1);
Max Curran2e83b5d2022-12-29 18:53:384291}
4292
Georg Neis0dce3332024-12-13 01:51:184293// TODO(crbug.com/40249481): Test flaky on trybots.
Liviu Tintad608e012023-05-10 19:16:414294TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:484295 DISABLED_CHROMEOS(NVSBlockUntilHeadReceived)) {
Liviu Tintad608e012023-05-10 19:16:414296 base::HistogramTester histogram_tester;
4297
4298 MakePrefetchService(
4299 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
4300
4301 network::mojom::NoVarySearchPtr no_vary_search_hint =
4302 network::mojom::NoVarySearch::New();
4303 no_vary_search_hint->vary_on_key_order = true;
4304 no_vary_search_hint->search_variance =
4305 network::mojom::SearchParamsVariance::NewNoVaryParams(
4306 std::vector<std::string>({"a"}));
Taiyo Mizuhashi43066071f2025-04-25 23:40:224307 const PrefetchType prefetch_type =
kenossbd9a3fc2025-03-28 05:15:574308 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:224309 /*use_prefetch_proxy=*/true, GetEagernessParam());
4310 MakePrefetchOnMainFrame(
4311 GURL("https://example.com/index.html?a=5"), prefetch_type,
kenossbd9a3fc2025-03-28 05:15:574312 /* referrer */ blink::mojom::Referrer(), std::move(no_vary_search_hint));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544313 task_environment()->RunUntilIdle();
Liviu Tintad608e012023-05-10 19:16:414314
Jeremy Roman4c9524212023-07-27 22:01:054315 VerifyCommonRequestState(
4316 GURL("https://example.com/index.html?a=5"),
4317 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:574318 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Liviu Tintad608e012023-05-10 19:16:414319
4320 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:424321 NavigateInitiatedByRenderer(GURL("https://example.com/index.html"));
Liviu Tintad608e012023-05-10 19:16:414322
4323 // Request the prefetch from the PrefetchService. The given callback shouldn't
4324 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574325 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizakibc780622023-09-05 19:07:334326 GetPrefetchToServe(future, GURL("https://example.com/index.html"),
Hiroshige Hayashizaki2df45292023-10-10 22:59:034327 MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:334328 EXPECT_FALSE(future.IsReady());
Max Currand74e811f2023-06-01 19:40:044329 task_environment()->FastForwardBy(base::Milliseconds(600));
4330
Liviu Tintad608e012023-05-10 19:16:414331 // Sends the head of the prefetch response. This should trigger the above
4332 // callback.
4333 SendHeadOfResponseAndWait(
4334 net::HTTP_OK, kHTMLMimeType,
4335 /*use_prefetch_proxy=*/true,
4336 {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"a\")"}},
4337 std::size(kHTMLBody));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574338 PrefetchServingHandle serving_handle = future.Take();
4339 ASSERT_TRUE(serving_handle);
Liviu Tintad608e012023-05-10 19:16:414340
4341 // Send the body and completion status of the request,
4342 SendBodyContentOfResponseAndWait(kHTMLBody);
4343 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
4344
4345 // Check the metrics now that the prefetch is complete.
kenossbd9a3fc2025-03-28 05:15:574346 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
4347 GetEagernessParam(),
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464348 /* is_accurate=*/true);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574349 ExpectServingReaderSuccess(serving_handle);
Hiroshige Hayashizaki3d7b94a2023-10-21 01:19:364350 ExpectServingMetricsSuccess();
Max Currand74e811f2023-06-01 19:40:044351
4352 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224353 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
Max Currand74e811f2023-06-01 19:40:044354 histogram_tester.ExpectUniqueTimeSample(
4355 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224356 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404357 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044358 base::Milliseconds(600), 1);
kenoss968f5dd2025-03-10 06:27:404359 histogram_tester.ExpectTotalCount(
Max Currand74e811f2023-06-01 19:40:044360 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224361 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404362 histogram_suffix),
4363 0);
kenoss968f5dd2025-03-10 06:27:404364 histogram_tester.ExpectTotalCount(
4365 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224366 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404367 histogram_suffix),
4368 0);
4369 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224370 base::StringPrintf(
4371 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4372 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044373 true, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:544374
4375 histogram_tester.ExpectUniqueSample(
4376 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
4377 "PerMatchingCandidate.",
4378 histogram_suffix}),
4379 PrefetchPotentialCandidateServingResult::kServed, 1);
Liviu Tintad608e012023-05-10 19:16:414380}
4381
Georg Neis0dce3332024-12-13 01:51:184382// TODO(crbug.com/40249481): Test flaky on trybots.
Liviu Tintad608e012023-05-10 19:16:414383TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:484384 DISABLED_CHROMEOS(NVSBlockUntilHeadReceivedNoMatchNoNVSHeader)) {
Liviu Tintad608e012023-05-10 19:16:414385 base::HistogramTester histogram_tester;
4386
4387 MakePrefetchService(
4388 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
4389
4390 network::mojom::NoVarySearchPtr no_vary_search_hint =
4391 network::mojom::NoVarySearch::New();
4392 no_vary_search_hint->vary_on_key_order = true;
4393 no_vary_search_hint->search_variance =
4394 network::mojom::SearchParamsVariance::NewNoVaryParams(
4395 std::vector<std::string>({"a"}));
Taiyo Mizuhashi43066071f2025-04-25 23:40:224396 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:584397 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:224398 /*use_prefetch_proxy=*/true, GetEagernessParam());
4399 MakePrefetchOnMainFrame(
4400 GURL("https://example.com/index.html?a=5"), prefetch_type,
Devlin Cronin7f318c12023-06-09 00:57:014401 /* referrer */ blink::mojom::Referrer(),
Liviu Tintad608e012023-05-10 19:16:414402 /* no_vary_search_hint */ std::move(no_vary_search_hint));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544403 task_environment()->RunUntilIdle();
Liviu Tintad608e012023-05-10 19:16:414404
Jeremy Roman4c9524212023-07-27 22:01:054405 VerifyCommonRequestState(
4406 GURL("https://example.com/index.html?a=5"),
4407 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:574408 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Liviu Tintad608e012023-05-10 19:16:414409
4410 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:424411 NavigateInitiatedByRenderer(GURL("https://example.com/index.html"));
Liviu Tintad608e012023-05-10 19:16:414412
4413 // Request the prefetch from the PrefetchService. The given callback shouldn't
4414 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574415 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizakibc780622023-09-05 19:07:334416 GetPrefetchToServe(future, GURL("https://example.com/index.html"),
Hiroshige Hayashizaki2df45292023-10-10 22:59:034417 MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:334418 EXPECT_FALSE(future.IsReady());
Liviu Tintad608e012023-05-10 19:16:414419
Max Currand74e811f2023-06-01 19:40:044420 task_environment()->FastForwardBy(base::Milliseconds(700));
4421
Liviu Tintad608e012023-05-10 19:16:414422 // Sends the head of the prefetch response. This should trigger the above
4423 // callback with a nullptr argument.
4424 SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
4425 /*use_prefetch_proxy=*/true,
4426 {{"X-Testing", "Hello World"}},
4427 std::size(kHTMLBody));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574428 PrefetchServingHandle serving_handle = future.Take();
4429 ASSERT_FALSE(serving_handle);
Liviu Tintad608e012023-05-10 19:16:414430
4431 // Send the body and completion status of the request,
4432 SendBodyContentOfResponseAndWait(kHTMLBody);
4433 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
4434
4435 // Check the metrics now that the prefetch is complete.
kenossbd9a3fc2025-03-28 05:15:574436 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
4437 GetEagernessParam());
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464438 ExpectServingMetricsSuccess();
Max Currand74e811f2023-06-01 19:40:044439
4440 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224441 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:404442 histogram_tester.ExpectTotalCount(
4443 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224444 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404445 histogram_suffix),
4446 0);
Max Currand74e811f2023-06-01 19:40:044447 histogram_tester.ExpectUniqueTimeSample(
4448 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224449 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404450 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044451 base::Milliseconds(700), 1);
4452 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224453 base::StringPrintf(
4454 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4455 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044456 true, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:544457
4458 histogram_tester.ExpectUniqueSample(
4459 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
4460 "PerMatchingCandidate.",
4461 histogram_suffix}),
4462 PrefetchPotentialCandidateServingResult::
4463 kNotServedDeterminedNVSHeaderMismatch,
4464 1);
Liviu Tintad608e012023-05-10 19:16:414465}
4466
Georg Neis0dce3332024-12-13 01:51:184467// TODO(crbug.com/40249481): Test flaky on trybots.
Liviu Tintad608e012023-05-10 19:16:414468TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:484469 DISABLED_CHROMEOS(NVSBlockUntilHeadReceivedNoMatchByNVSHeader)) {
Liviu Tintad608e012023-05-10 19:16:414470 base::HistogramTester histogram_tester;
4471
4472 MakePrefetchService(
4473 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
4474
4475 network::mojom::NoVarySearchPtr no_vary_search_hint =
4476 network::mojom::NoVarySearch::New();
4477 no_vary_search_hint->vary_on_key_order = true;
4478 no_vary_search_hint->search_variance =
4479 network::mojom::SearchParamsVariance::NewNoVaryParams(
4480 std::vector<std::string>({"a"}));
Taiyo Mizuhashi43066071f2025-04-25 23:40:224481 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:584482 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:224483 /*use_prefetch_proxy=*/true, GetEagernessParam());
4484 MakePrefetchOnMainFrame(
4485 GURL("https://example.com/index.html?a=5"), prefetch_type,
Devlin Cronin7f318c12023-06-09 00:57:014486 /* referrer */ blink::mojom::Referrer(),
Liviu Tintad608e012023-05-10 19:16:414487 /* no_vary_search_hint */ std::move(no_vary_search_hint));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544488 task_environment()->RunUntilIdle();
Liviu Tintad608e012023-05-10 19:16:414489
Jeremy Roman4c9524212023-07-27 22:01:054490 VerifyCommonRequestState(
4491 GURL("https://example.com/index.html?a=5"),
4492 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:574493 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Liviu Tintad608e012023-05-10 19:16:414494
4495 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:424496 NavigateInitiatedByRenderer(GURL("https://example.com/index.html"));
Liviu Tintad608e012023-05-10 19:16:414497
4498 // Request the prefetch from the PrefetchService. The given callback shouldn't
4499 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574500 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizakibc780622023-09-05 19:07:334501 GetPrefetchToServe(future, GURL("https://example.com/index.html"),
Hiroshige Hayashizaki2df45292023-10-10 22:59:034502 MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:334503 EXPECT_FALSE(future.IsReady());
Liviu Tintad608e012023-05-10 19:16:414504
Taiyo Mizuhashi98ecb382025-06-03 15:24:554505 task_environment()->FastForwardBy(
4506 base::Milliseconds(kAddedToURLRequestStartLatency + kHeaderLatency));
Max Currand74e811f2023-06-01 19:40:044507
Liviu Tintad608e012023-05-10 19:16:414508 // Sends the head of the prefetch response. This should trigger the above
4509 // callback with a nullptr argument.
4510 SendHeadOfResponseAndWait(
4511 net::HTTP_OK, kHTMLMimeType,
4512 /*use_prefetch_proxy=*/true,
4513 {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"b\")"}},
4514 std::size(kHTMLBody));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574515 PrefetchServingHandle serving_handle = future.Take();
4516 ASSERT_FALSE(serving_handle);
Liviu Tintad608e012023-05-10 19:16:414517
4518 // Send the body and completion status of the request,
4519 SendBodyContentOfResponseAndWait(kHTMLBody);
4520 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
4521
4522 // Check the metrics now that the prefetch is complete.
kenossbd9a3fc2025-03-28 05:15:574523 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
4524 GetEagernessParam());
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464525 ExpectServingMetricsSuccess();
Max Currand74e811f2023-06-01 19:40:044526
4527 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224528 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:404529 histogram_tester.ExpectTotalCount(
4530 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224531 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404532 histogram_suffix),
4533 0);
Max Currand74e811f2023-06-01 19:40:044534 histogram_tester.ExpectUniqueTimeSample(
4535 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224536 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404537 histogram_suffix),
Taiyo Mizuhashi98ecb382025-06-03 15:24:554538 base::Milliseconds(kAddedToURLRequestStartLatency + kHeaderLatency), 1);
Max Currand74e811f2023-06-01 19:40:044539 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224540 base::StringPrintf(
4541 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4542 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044543 true, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:544544
4545 histogram_tester.ExpectUniqueSample(
4546 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
4547 "PerMatchingCandidate.",
4548 histogram_suffix}),
4549 PrefetchPotentialCandidateServingResult::
4550 kNotServedDeterminedNVSHeaderMismatch,
4551 1);
Liviu Tintad608e012023-05-10 19:16:414552}
4553
Georg Neis0dce3332024-12-13 01:51:184554// TODO(crbug.com/40249481): Test flaky on trybots.
Max Curran5b4806eb2023-05-25 20:07:204555TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:484556 DISABLED_CHROMEOS(FailedCookiesChangedWhileBlockUntilHead)) {
Max Curran5b4806eb2023-05-25 20:07:204557 base::HistogramTester histogram_tester;
4558
4559 MakePrefetchService(
4560 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
Taiyo Mizuhashi43066071f2025-04-25 23:40:224561 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:584562 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:224563 /*use_prefetch_proxy=*/true, GetEagernessParam());
4564 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544565 task_environment()->RunUntilIdle();
Max Curran5b4806eb2023-05-25 20:07:204566
Jeremy Roman4c9524212023-07-27 22:01:054567 VerifyCommonRequestState(
4568 GURL("https://example.com"),
4569 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:574570 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Max Curran5b4806eb2023-05-25 20:07:204571
4572 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:424573 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Curran5b4806eb2023-05-25 20:07:204574
4575 // Request the prefetch from the PrefetchService. The given callback shouldn't
4576 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574577 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizaki2df45292023-10-10 22:59:034578 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:334579 EXPECT_FALSE(future.IsReady());
Max Curran5b4806eb2023-05-25 20:07:204580
Max Currand74e811f2023-06-01 19:40:044581 task_environment()->FastForwardBy(base::Milliseconds(800));
4582
Max Curran5b4806eb2023-05-25 20:07:204583 // Adding a cookie after while blocking until the head is received will cause
4584 // it to fail.
4585 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544586 task_environment()->RunUntilIdle();
Max Curran5b4806eb2023-05-25 20:07:204587
4588 // Sends the head of the prefetch response. This should trigger the above
4589 // callback.
4590 SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
4591 /*use_prefetch_proxy=*/true,
4592 {{"X-Testing", "Hello World"}},
4593 std::size(kHTMLBody));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574594 PrefetchServingHandle serving_handle = future.Take();
4595 EXPECT_FALSE(serving_handle);
Max Curran5b4806eb2023-05-25 20:07:204596
Max Curran5b4806eb2023-05-25 20:07:204597 histogram_tester.ExpectUniqueSample(
Max Curran5b4806eb2023-05-25 20:07:204598 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
Max Curran59b8ab992023-07-26 21:58:124599 histogram_tester.ExpectTotalCount("PrefetchProxy.Prefetch.Mainframe.NetError",
4600 0);
4601 histogram_tester.ExpectTotalCount(
4602 "PrefetchProxy.Prefetch.Mainframe.BodyLength", 0);
Max Curran5b4806eb2023-05-25 20:07:204603 histogram_tester.ExpectUniqueSample(
4604 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
4605 histogram_tester.ExpectUniqueSample(
4606 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
4607
Arthur Sonzognic686e8f2024-01-11 08:36:374608 std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
Max Curran5b4806eb2023-05-25 20:07:204609 PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
4610 EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 1);
4611 EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 1);
Max Curran59b8ab992023-07-26 21:58:124612 EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0);
Max Curran5b4806eb2023-05-25 20:07:204613
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464614 ExpectServingMetrics(PrefetchStatus::kPrefetchNotUsedCookiesChanged);
Max Curran5b4806eb2023-05-25 20:07:204615
Adithya Srinivasan38e0c402023-07-19 16:12:584616 ExpectCorrectUkmLogs({.outcome = PreloadingTriggeringOutcome::kFailure,
4617 .failure = ToPreloadingFailureReason(
4618 PrefetchStatus::kPrefetchNotUsedCookiesChanged),
kenossbe8fbf22024-08-14 12:18:214619 .is_accurate = true,
kenossbd9a3fc2025-03-28 05:15:574620 .eagerness = GetEagernessParam()});
Max Currand74e811f2023-06-01 19:40:044621
4622 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224623 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:404624 histogram_tester.ExpectTotalCount(
4625 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224626 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404627 histogram_suffix),
4628 0);
Max Currand74e811f2023-06-01 19:40:044629 histogram_tester.ExpectUniqueTimeSample(
4630 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224631 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404632 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044633 base::Milliseconds(800), 1);
4634 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224635 base::StringPrintf(
4636 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4637 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044638 true, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:544639
4640 histogram_tester.ExpectUniqueSample(
4641 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
4642 "PerMatchingCandidate.",
4643 histogram_suffix}),
4644 PrefetchPotentialCandidateServingResult::kNotServedCookiesChanged, 1);
Max Currand74e811f2023-06-01 19:40:044645}
4646
Georg Neis0dce3332024-12-13 01:51:184647// TODO(crbug.com/40249481): Test flaky on trybots.
Max Currand74e811f2023-06-01 19:40:044648TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:484649 DISABLED_CHROMEOS(FailedTimeoutWhileBlockUntilHead)) {
Max Currand74e811f2023-06-01 19:40:044650 base::HistogramTester histogram_tester;
4651
4652 MakePrefetchService(
4653 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
4654
Taiyo Mizuhashi6b0a4952024-03-18 22:40:374655 PrefetchType prefetch_type(PreloadingTriggerType::kSpeculationRule,
kenossbd9a3fc2025-03-28 05:15:574656 /*use_prefetch_proxy=*/true, GetEagernessParam());
Taiyo Mizuhashi6b0a4952024-03-18 22:40:374657 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544658 task_environment()->RunUntilIdle();
Max Currand74e811f2023-06-01 19:40:044659
Jeremy Roman4c9524212023-07-27 22:01:054660 VerifyCommonRequestState(
4661 GURL("https://example.com"),
4662 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:574663 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Max Currand74e811f2023-06-01 19:40:044664
4665 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:424666 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Currand74e811f2023-06-01 19:40:044667
4668 // Request the prefetch from the PrefetchService. The given callback shouldn't
4669 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574670 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizaki2df45292023-10-10 22:59:034671 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:334672 EXPECT_FALSE(future.IsReady());
Max Currand74e811f2023-06-01 19:40:044673
4674 // If the prefetch times out while PrefetchService is blocking until head,
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574675 // then it should unblock without setting serving_handle.
Liviu Tinta1fe1a6d2023-09-20 19:44:044676 task_environment()->FastForwardBy(base::Milliseconds(kPrefetchTimeout));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574677 PrefetchServingHandle serving_handle = future.Take();
4678 EXPECT_FALSE(serving_handle);
Max Currand74e811f2023-06-01 19:40:044679
kenossbd9a3fc2025-03-28 05:15:574680 ExpectPrefetchFailedNetError(histogram_tester, net::ERR_TIMED_OUT,
4681 GetEagernessParam(),
kenossbe8fbf22024-08-14 12:18:214682 /*is_accurate_triggering=*/true);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464683 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedNetError);
Max Currand74e811f2023-06-01 19:40:044684
4685 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224686 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
Taiyo Mizuhashicd08a8f2025-06-10 17:27:324687 base::TimeDelta block_until_head_timeout = PrefetchBlockUntilHeadTimeout(
4688 prefetch_type, /*should_disable_block_until_head_timeout=*/false,
4689 /*is_nav_prerender=*/false);
kenoss968f5dd2025-03-10 06:27:404690 histogram_tester.ExpectTotalCount(
4691 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224692 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404693 histogram_suffix),
4694 0);
Max Currand74e811f2023-06-01 19:40:044695 histogram_tester.ExpectUniqueTimeSample(
4696 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224697 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404698 histogram_suffix),
Liviu Tinta1fe1a6d2023-09-20 19:44:044699 block_until_head_timeout, 1);
4700 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224701 base::StringPrintf(
4702 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4703 histogram_suffix),
Liviu Tinta1fe1a6d2023-09-20 19:44:044704 true, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:544705
4706 histogram_tester.ExpectUniqueSample(
4707 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
4708 "PerMatchingCandidate.",
4709 histogram_suffix}),
4710 PrefetchPotentialCandidateServingResult::kNotServedBlockUntilHeadTimeout,
4711 1);
Liviu Tinta1fe1a6d2023-09-20 19:44:044712}
4713
Georg Neis0dce3332024-12-13 01:51:184714// TODO(crbug.com/40249481): Test flaky on trybots.
Liviu Tinta1fe1a6d2023-09-20 19:44:044715TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:484716 DISABLED_CHROMEOS(FailedTimeoutWhileBlockUntilHeadForOlderNavigation)) {
Liviu Tinta1fe1a6d2023-09-20 19:44:044717 base::HistogramTester histogram_tester;
4718
4719 MakePrefetchService(
4720 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
Taiyo Mizuhashi43066071f2025-04-25 23:40:224721 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:584722 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:224723 /*use_prefetch_proxy=*/false, GetEagernessParam());
4724 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544725 task_environment()->RunUntilIdle();
Liviu Tinta1fe1a6d2023-09-20 19:44:044726
4727 VerifyCommonRequestState(
4728 GURL("https://example.com"),
kenossbd9a3fc2025-03-28 05:15:574729 {.expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Liviu Tinta1fe1a6d2023-09-20 19:44:044730
Hiroshige Hayashizaki8d4cc042023-10-18 01:13:544731 // The first navigation is started and then gone before the head of the
4732 // prefetch response is received. The given callback shouldn't be called (and
4733 // the metrics shouldn't be recorded) because the navigation is gone before
4734 // the request is unblocked.
Taiyo Mizuhashi01d86af22024-03-27 14:27:424735 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574736 base::test::TestFuture<PrefetchServingHandle> first_future;
Taiyo Mizuhashi01d86af22024-03-27 14:27:424737 GetPrefetchToServe(first_future, GURL("https://example.com"),
4738 MainDocumentToken());
Hiroshige Hayashizakifc691c52023-10-23 10:07:544739 EXPECT_FALSE(first_future.IsReady());
Liviu Tinta1fe1a6d2023-09-20 19:44:044740
Hiroshige Hayashizaki8d4cc042023-10-18 01:13:544741 // The second navigation is started before the head of the prefetch response
4742 // is received.
Taiyo Mizuhashi01d86af22024-03-27 14:27:424743 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574744 base::test::TestFuture<PrefetchServingHandle> second_future;
Taiyo Mizuhashi01d86af22024-03-27 14:27:424745 GetPrefetchToServe(second_future, GURL("https://example.com"),
4746 MainDocumentToken());
Hiroshige Hayashizakifc691c52023-10-23 10:07:544747 EXPECT_FALSE(second_future.IsReady());
Hiroshige Hayashizaki8d4cc042023-10-18 01:13:544748
4749 // The prefetch times out while PrefetchService is blocking until head.
4750 // This should unblock the request after `kBlockUntilHeadTimeout` msec without
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574751 // setting serving_handle.
Hiroshige Hayashizaki8d4cc042023-10-18 01:13:544752 task_environment()->FastForwardBy(base::Milliseconds(kPrefetchTimeout));
Liviu Tinta1fe1a6d2023-09-20 19:44:044753
kenoss968f5dd2025-03-10 06:27:404754 EXPECT_TRUE(first_future.IsReady());
4755 EXPECT_TRUE(second_future.IsReady());
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574756 PrefetchServingHandle serving_handle = second_future.Take();
4757 EXPECT_FALSE(serving_handle);
kenossbd9a3fc2025-03-28 05:15:574758 ExpectPrefetchFailedNetError(histogram_tester, net::ERR_TIMED_OUT,
4759 GetEagernessParam(),
kenossbe8fbf22024-08-14 12:18:214760 /*is_accurate_triggering=*/true);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464761 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedNetError,
4762 /*prefetch_header_latency=*/false,
4763 /*required_private_prefetch_proxy=*/false);
Liviu Tinta1fe1a6d2023-09-20 19:44:044764
kenoss968f5dd2025-03-10 06:27:404765 // The metrics are recorded for the first and the second navigation, as the
4766 // PrefetchContainers were initially considered as a candidate at the time of
4767 // navigation start but decided not to be used later (after
4768 // `kBlockUntilHeadTimeout` msec) due to timeout.
Liviu Tinta1fe1a6d2023-09-20 19:44:044769 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224770 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:404771 histogram_tester.ExpectTotalCount(
4772 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224773 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404774 histogram_suffix),
4775 0);
Liviu Tinta1fe1a6d2023-09-20 19:44:044776 histogram_tester.ExpectUniqueTimeSample(
4777 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224778 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404779 histogram_suffix),
4780 base::Milliseconds(kBlockUntilHeadTimeout), 2);
4781 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224782 base::StringPrintf(
4783 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4784 histogram_suffix),
kenoss968f5dd2025-03-10 06:27:404785 true, 2);
Hiroshige Hayashizaki8d4cc042023-10-18 01:13:544786
4787 // The third navigation is started after the PrefetchContainer became not
4788 // servable.
Taiyo Mizuhashi01d86af22024-03-27 14:27:424789 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574790 base::test::TestFuture<PrefetchServingHandle> third_future;
Taiyo Mizuhashi01d86af22024-03-27 14:27:424791 GetPrefetchToServe(third_future, GURL("https://example.com"),
4792 MainDocumentToken());
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574793 serving_handle = third_future.Take();
4794 EXPECT_FALSE(serving_handle);
Hiroshige Hayashizaki8d4cc042023-10-18 01:13:544795
4796 // The metric should not be recorded for the third navigation, because the
4797 // PrefetchContainer was not servable when the third navigation starts and
4798 // thus shouldn't be considered as a candidate in the first place.
kenoss968f5dd2025-03-10 06:27:404799 histogram_tester.ExpectTotalCount(
4800 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224801 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404802 histogram_suffix),
4803 0);
Hiroshige Hayashizakib35b1002023-10-18 02:13:254804 histogram_tester.ExpectUniqueTimeSample(
Hiroshige Hayashizaki8d4cc042023-10-18 01:13:544805 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224806 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404807 histogram_suffix),
4808 base::Milliseconds(kBlockUntilHeadTimeout), 2);
4809 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224810 base::StringPrintf(
4811 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4812 histogram_suffix),
kenoss968f5dd2025-03-10 06:27:404813 true, 2);
Max Currand74e811f2023-06-01 19:40:044814}
4815
Georg Neis0dce3332024-12-13 01:51:184816// TODO(crbug.com/40249481): Test flaky on trybots.
Max Currand74e811f2023-06-01 19:40:044817TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:484818 DISABLED_CHROMEOS(FailedNetErrorWhileBlockUntilHead)) {
Max Currand74e811f2023-06-01 19:40:044819 base::HistogramTester histogram_tester;
4820
4821 MakePrefetchService(
4822 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
Taiyo Mizuhashi43066071f2025-04-25 23:40:224823 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:584824 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:224825 /*use_prefetch_proxy=*/false, GetEagernessParam());
4826 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:544827 task_environment()->RunUntilIdle();
Max Currand74e811f2023-06-01 19:40:044828
Jeremy Roman4c9524212023-07-27 22:01:054829 VerifyCommonRequestState(
4830 GURL("https://example.com"),
kenossbd9a3fc2025-03-28 05:15:574831 {.expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Max Currand74e811f2023-06-01 19:40:044832
4833 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:424834 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Currand74e811f2023-06-01 19:40:044835
4836 // Request the prefetch from the PrefetchService. The given callback shouldn't
4837 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574838 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizaki2df45292023-10-10 22:59:034839 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:334840 EXPECT_FALSE(future.IsReady());
Max Currand74e811f2023-06-01 19:40:044841
4842 task_environment()->FastForwardBy(base::Milliseconds(300));
4843
4844 // If the prefetch encounters a net error while PrefetchService is blocking
4845 // until head, then it should unblock without setting
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574846 // serving_handle.
Max Currand74e811f2023-06-01 19:40:044847 CompleteResponseAndWait(net::ERR_ACCESS_DENIED, 0);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574848 PrefetchServingHandle serving_handle = future.Take();
4849 EXPECT_FALSE(serving_handle);
Max Currand74e811f2023-06-01 19:40:044850
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464851 ExpectPrefetchFailedNetError(histogram_tester, net::ERR_ACCESS_DENIED,
kenossbd9a3fc2025-03-28 05:15:574852 GetEagernessParam(),
4853 /*is_accurate_triggering=*/true);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:464854 ExpectServingMetrics(PrefetchStatus::kPrefetchFailedNetError,
4855 /*prefetch_header_latency=*/false,
4856 /*required_private_prefetch_proxy=*/false);
Max Currand74e811f2023-06-01 19:40:044857
4858 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224859 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:404860 histogram_tester.ExpectTotalCount(
4861 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224862 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:404863 histogram_suffix),
4864 0);
Max Currand74e811f2023-06-01 19:40:044865 histogram_tester.ExpectUniqueTimeSample(
4866 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224867 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:404868 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044869 base::Milliseconds(300), 1);
4870 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224871 base::StringPrintf(
4872 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
4873 histogram_suffix),
Max Currand74e811f2023-06-01 19:40:044874 true, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:544875
4876 histogram_tester.ExpectUniqueSample(
4877 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
4878 "PerMatchingCandidate.",
4879 histogram_suffix}),
4880 PrefetchPotentialCandidateServingResult::
4881 kNotServedUnsatisfiedPrefetchServeableState,
4882 1);
Max Curran5b4806eb2023-05-25 20:07:204883}
4884
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354885// TODO(crbug.com/40064525): For NVSBlockUntilHeadReceivedOneMatchOneTimeout,
4886// FailedCookiesChangedAfterPrefetchStartedTimedoutNVSHintPrefetch,
4887// FailedCookiesChangedAfterPrefetchStartedNVSHintPrefetch and
4888// NVSBlockUntilHeadReceivedMultipleMatchesByNVSHint, consider only keeping one
4889// of them and removing the remaining, as they almost test the same logic.
Georg Neis0dce3332024-12-13 01:51:184890// TODO(crbug.com/40249481): Test flaky on trybots.
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354891TEST_P(
4892 PrefetchServiceAlwaysBlockUntilHeadTest,
4893 DISABLED_CHROMEOS_AND_CASTOS(NVSBlockUntilHeadReceivedOneMatchOneTimeout)) {
4894 // The scenario is:
kenoss968f5dd2025-03-10 06:27:404895 // * Prefetch https://example.com/index.html?a=5 with NVS hint to
4896 // ignore "a" and send request.
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354897 // * Queue a prefetch for https://example.com/index.html?b=3 with NVS hint to
kenoss968f5dd2025-03-10 06:27:404898 // match but send no request.
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354899 // * Navigate to https://example.com/index.html.
kenoss968f5dd2025-03-10 06:27:404900 // * Receive a response for the first prefetch containing NVS header
4901 // equivalent to the NVS hint.
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354902 // * Expect https://example.com/index.html?a=5 to be served.
4903 const std::string kTestUrl = "https://example.com/index.html";
4904 base::HistogramTester histogram_tester;
4905
4906 MakePrefetchService(
4907 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(2));
4908
Taiyo Mizuhashi43066071f2025-04-25 23:40:224909 const PrefetchType prefetch_type =
4910 PrefetchType(PreloadingTriggerType::kSpeculationRule,
4911 /*use_prefetch_proxy=*/false, GetEagernessParam());
4912
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354913 {
4914 network::mojom::NoVarySearchPtr no_vary_search_hint =
4915 network::mojom::NoVarySearch::New();
4916 no_vary_search_hint->vary_on_key_order = true;
4917 no_vary_search_hint->search_variance =
4918 network::mojom::SearchParamsVariance::NewNoVaryParams(
4919 std::vector<std::string>({"a"}));
4920 MakePrefetchOnMainFrame(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224921 GURL(kTestUrl + "?a=5"), prefetch_type,
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354922 /* referrer */ blink::mojom::Referrer(),
4923 /* no_vary_search_hint */ std::move(no_vary_search_hint));
4924 task_environment()->RunUntilIdle();
4925
kenossbd9a3fc2025-03-28 05:15:574926 VerifyCommonRequestState(GURL(kTestUrl + "?a=5"),
4927 {.expected_priority = ExpectedPriorityForEagerness(
4928 GetEagernessParam())});
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354929 }
4930 {
4931 network::mojom::NoVarySearchPtr no_vary_search_hint =
4932 network::mojom::NoVarySearch::New();
4933 no_vary_search_hint->vary_on_key_order = true;
4934 no_vary_search_hint->search_variance =
4935 network::mojom::SearchParamsVariance::NewNoVaryParams(
4936 std::vector<std::string>({"b"}));
4937 MakePrefetchOnMainFrame(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224938 GURL(kTestUrl + "?b=3"), prefetch_type,
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354939 /* referrer */ blink::mojom::Referrer(),
4940 /* no_vary_search_hint */ std::move(no_vary_search_hint));
4941 task_environment()->RunUntilIdle();
4942
4943 VerifyPrefetchAttemptIsPending(GURL(kTestUrl + "?b=3"));
4944 }
4945 // Navigate to the URL before the head of the prefetch response is received
4946 NavigateInitiatedByRenderer(GURL(kTestUrl));
4947
4948 // Request the prefetch from the PrefetchService. `future` should be blocked
4949 // until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574950 base::test::TestFuture<PrefetchServingHandle> future;
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354951 GetPrefetchToServe(future, GURL(kTestUrl), MainDocumentToken());
4952
Taiyo Mizuhashi98ecb382025-06-03 15:24:554953 task_environment()->FastForwardBy(
4954 base::Milliseconds(kAddedToURLRequestStartLatency + kHeaderLatency));
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354955 EXPECT_FALSE(future.IsReady());
4956
4957 // Sends the head of the prefetch response. This should unblock `future`.
4958 SendHeadOfResponseAndWait(
4959 net::HTTP_OK, kHTMLMimeType,
4960 /*use_prefetch_proxy=*/false,
4961 {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"a\")"}},
4962 std::size(kHTMLBody));
4963
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:574964 PrefetchServingHandle serving_handle = future.Take();
4965 ASSERT_TRUE(serving_handle);
4966 EXPECT_EQ(serving_handle.GetPrefetchContainer()->GetURL(),
Adithya Srinivasan9d1b6fa2024-08-29 15:04:354967 GURL(kTestUrl + "?a=5"));
4968
4969 // Send the body and completion status of the request,
4970 SendBodyContentOfResponseAndWait(kHTMLBody);
4971 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
4972
4973 // Check the metrics now that the prefetch is complete.
4974 histogram_tester.ExpectUniqueSample(
4975 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 2);
4976 histogram_tester.ExpectUniqueSample(
4977 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
4978 histogram_tester.ExpectUniqueSample(
4979 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
4980 histogram_tester.ExpectUniqueSample(
4981 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
4982 histogram_tester.ExpectUniqueSample(
4983 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
4984 histogram_tester.ExpectUniqueSample(
4985 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
4986
4987 std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
4988 PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
4989 EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 2);
4990 EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 2);
4991 EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1);
4992
4993 ExpectServingMetricsSuccess(/*required_private_prefetch_proxy=*/false);
4994
4995 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:224996 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:404997 histogram_tester.ExpectUniqueTimeSample(
4998 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:224999 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:405000 histogram_suffix),
Taiyo Mizuhashi98ecb382025-06-03 15:24:555001 base::Milliseconds(kAddedToURLRequestStartLatency + kHeaderLatency), 1);
kenoss968f5dd2025-03-10 06:27:405002 histogram_tester.ExpectTotalCount(
5003 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225004 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:405005 histogram_suffix),
5006 0);
5007 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225008 base::StringPrintf(
5009 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
5010 histogram_suffix),
kenoss968f5dd2025-03-10 06:27:405011 true, 1);
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355012}
5013
Georg Neis0dce3332024-12-13 01:51:185014// TODO(crbug.com/40249481): Test flaky on trybots.
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355015TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
5016 DISABLED_CHROMEOS_AND_CASTOS(
5017 FailedCookiesChangedAfterPrefetchStartedTimedoutNVSHintPrefetch)) {
5018 // The scenario is:
5019 // * Prefetch https://example.com/index.html.
5020 // * Queue a prefetch for https://example.com/index.html?a=1 with NVS hint to
5021 // match but send no head/body.
5022 // * Change the cookies.
5023 // * Navigate to https://example.com/index.html.
5024 // * Expect no prefetch to be served.
5025 const std::string kTestUrl = "https://example.com/index.html";
5026 base::HistogramTester histogram_tester;
5027
5028 MakePrefetchService(
5029 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(2));
kenossf6fbd5652024-09-04 00:40:285030
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355031 MakePrefetchOnMainFrame(
kenossbd9a3fc2025-03-28 05:15:575032 GURL(kTestUrl),
5033 PrefetchType(PreloadingTriggerType::kSpeculationRule,
5034 /*use_prefetch_proxy=*/false, GetEagernessParam()));
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355035 VerifyCommonRequestState(
5036 GURL(kTestUrl),
kenossbd9a3fc2025-03-28 05:15:575037 {.expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355038 task_environment()->RunUntilIdle();
5039 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
5040 /*use_prefetch_proxy=*/false,
5041 {{"X-Testing", "Hello World"}}, kHTMLBody);
kenossf6fbd5652024-09-04 00:40:285042
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355043 {
5044 network::mojom::NoVarySearchPtr no_vary_search_hint =
5045 network::mojom::NoVarySearch::New();
5046 no_vary_search_hint->vary_on_key_order = true;
5047 no_vary_search_hint->search_variance =
5048 network::mojom::SearchParamsVariance::NewNoVaryParams(
5049 std::vector<std::string>({"a"}));
5050 MakePrefetchOnMainFrame(
5051 GURL(kTestUrl + "?a=1"),
5052 PrefetchType(PreloadingTriggerType::kSpeculationRule,
kenossbd9a3fc2025-03-28 05:15:575053 /*use_prefetch_proxy=*/false, GetEagernessParam()),
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355054 /* referrer */ blink::mojom::Referrer(),
5055 /* no_vary_search_hint */ std::move(no_vary_search_hint));
5056 task_environment()->RunUntilIdle();
5057 VerifyCommonRequestStateByUrl(
5058 GURL(kTestUrl + "?a=1"),
kenossbd9a3fc2025-03-28 05:15:575059 {.expected_priority =
5060 ExpectedPriorityForEagerness(GetEagernessParam())});
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355061 }
5062
5063 // Adding a cookie after the prefetch has started will cause it to fail when
5064 // being served.
5065 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
5066 task_environment()->RunUntilIdle();
5067
5068 NavigateInitiatedByRenderer(GURL(kTestUrl));
5069
5070 histogram_tester.ExpectUniqueSample(
5071 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
5072 histogram_tester.ExpectUniqueSample(
5073 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 1);
5074 histogram_tester.ExpectUniqueSample(
5075 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 1);
5076 histogram_tester.ExpectUniqueSample(
5077 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
5078 histogram_tester.ExpectUniqueSample(
5079 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
5080
5081 std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
5082 PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
5083 EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 2);
5084 EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 2);
5085 EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 1);
5086
5087 // Request the prefetch from the PrefetchService. Since both prefetch
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575088 // candidates are not eligible serving_handle will be falsy.
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355089 EXPECT_FALSE(GetPrefetchToServe(GURL("https://example.com/index.html")));
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355090}
5091
Georg Neis0dce3332024-12-13 01:51:185092// TODO(crbug.com/40249481): Test flaky on trybots.
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355093TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
5094 DISABLED_CHROMEOS_AND_CASTOS(
5095 FailedCookiesChangedAfterPrefetchStartedNVSHintPrefetch)) {
5096 // The scenario is:
5097 // * Start prefetching https://example.com/index.html but send no head/body.
5098 // * Queue a prefetch for https://example.com/index.html?a=1 with NVS hint to
5099 // match but send no head/body.
5100 // * Change the cookies.
5101 // * Navigate to https://example.com/index.html.
5102 // * Send head/body for https://example.com/index.html.
5103 // * Verify that the navigation is not waiting anymore on
5104 // https://example.com/index.html?a=1 head.
5105 // * Expect no prefetch to be served.
5106 const std::string kTestUrl = "https://example.com/index.html";
5107 base::HistogramTester histogram_tester;
5108
5109 MakePrefetchService(
5110 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(2));
5111 MakePrefetchOnMainFrame(
kenossbd9a3fc2025-03-28 05:15:575112 GURL(kTestUrl),
5113 PrefetchType(PreloadingTriggerType::kSpeculationRule,
5114 /*use_prefetch_proxy=*/false, GetEagernessParam()));
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355115 VerifyCommonRequestState(
5116 GURL(kTestUrl),
kenossbd9a3fc2025-03-28 05:15:575117 {.expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355118 task_environment()->RunUntilIdle();
5119 {
5120 network::mojom::NoVarySearchPtr no_vary_search_hint =
5121 network::mojom::NoVarySearch::New();
5122 no_vary_search_hint->vary_on_key_order = true;
5123 no_vary_search_hint->search_variance =
5124 network::mojom::SearchParamsVariance::NewNoVaryParams(
5125 std::vector<std::string>({"a"}));
5126 GURL url_2{kTestUrl + "?a=1"};
5127 MakePrefetchOnMainFrame(
5128 url_2,
5129 PrefetchType(PreloadingTriggerType::kSpeculationRule,
kenossbd9a3fc2025-03-28 05:15:575130 /*use_prefetch_proxy=*/false, GetEagernessParam()),
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355131 /* referrer */ blink::mojom::Referrer(),
5132 /* no_vary_search_hint */ std::move(no_vary_search_hint));
5133 task_environment()->RunUntilIdle();
5134 VerifyPrefetchAttemptIsPending(url_2);
5135 }
5136
5137 // Adding a cookie after the prefetch has started will cause it to fail when
5138 // being served.
5139 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
5140 task_environment()->RunUntilIdle();
5141
5142 NavigateInitiatedByRenderer(GURL(kTestUrl));
5143
5144 // Request the prefetch from the PrefetchService.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575145 base::test::TestFuture<PrefetchServingHandle> future;
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355146 GetPrefetchToServe(future, GURL(kTestUrl), MainDocumentToken());
5147 EXPECT_FALSE(future.IsReady());
5148 task_environment()->RunUntilIdle();
5149 EXPECT_FALSE(future.IsReady());
5150
5151 // Sends the head of the prefetch response. This should not trigger the above
5152 // callback.
5153 SendHeadOfResponseAndWait(
5154 net::HTTP_OK, kHTMLMimeType,
5155 /*use_prefetch_proxy=*/false,
5156 {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"e\")"}},
5157 std::size(kHTMLBody));
5158
5159 EXPECT_TRUE(future.IsReady());
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575160 PrefetchServingHandle serving_handle = future.Take();
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355161 // Both prefetch candidates are not eligible.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575162 EXPECT_FALSE(serving_handle);
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355163
5164 // Send the body and completion status of the request,
5165 SendBodyContentOfResponseAndWait(kHTMLBody);
5166 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
5167
5168 histogram_tester.ExpectUniqueSample(
5169 "PrefetchProxy.Prefetch.Mainframe.RespCode", net::HTTP_OK, 1);
5170 // We cancel the streaming of this prefetch because we know we cannot use it.
5171 histogram_tester.ExpectUniqueSample(
5172 "PrefetchProxy.Prefetch.Mainframe.NetError", net::OK, 0);
5173 histogram_tester.ExpectUniqueSample(
5174 "PrefetchProxy.Prefetch.Mainframe.BodyLength", std::size(kHTMLBody), 0);
5175 histogram_tester.ExpectUniqueSample(
5176 "PrefetchProxy.Prefetch.Mainframe.TotalTime", kTotalTimeDuration, 1);
5177 histogram_tester.ExpectUniqueSample(
5178 "PrefetchProxy.Prefetch.Mainframe.ConnectTime", kConnectTimeDuration, 1);
5179
5180 std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
5181 PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
5182 EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 2);
5183 EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 2);
5184 // None of the prefetches were successful because of the cookie change.
5185 EXPECT_EQ(referring_page_metrics->prefetch_successful_count, 0);
5186
5187 // Serving page metrics prefetch_header_latency is logged at response
5188 // complete. Since we cancel streaming the response, this should not be set.
5189 ExpectServingMetrics(PrefetchStatus::kPrefetchNotUsedCookiesChanged,
5190 /*prefetch_header_latency=*/false,
5191 /*required_private_prefetch_proxy=*/false);
5192}
5193
Georg Neis0dce3332024-12-13 01:51:185194// TODO(crbug.com/40249481): Test flaky on trybots.
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355195TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
5196 DISABLED_CHROMEOS_AND_CASTOS(
5197 NVSBlockUntilHeadReceivedMultipleMatchesByNVSHint)) {
5198 // The scenario is:
5199 // * Prefetch https://example.com/index.html?a=5 with NVS hint to ignore "a"
5200 // but mismatched NVS header and send head/body.
5201 // * Queue a prefetch for https://example.com/index.html?b=3 with NVS hint/NVS
5202 // header to ignore "b" and send head/body.
5203 // * Navigate to https://example.com/index.html.
5204 // * Make sure to receive ?a=5 prefetch before ?b=3.
5205 // * Expect https://example.com/index.html?b=3 not to be served, because it
5206 // cannot improve the performance (even worse, it would block the real
5207 // navigation).
5208 const std::string kTestUrl = "https://example.com/index.html";
5209 base::HistogramTester histogram_tester;
5210
5211 MakePrefetchService(
5212 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(2));
5213
Taiyo Mizuhashi43066071f2025-04-25 23:40:225214 const PrefetchType prefetch_type =
5215 PrefetchType(PreloadingTriggerType::kSpeculationRule,
5216 /*use_prefetch_proxy=*/false, GetEagernessParam());
5217
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355218 {
5219 network::mojom::NoVarySearchPtr no_vary_search_hint =
5220 network::mojom::NoVarySearch::New();
5221 no_vary_search_hint->vary_on_key_order = true;
5222 no_vary_search_hint->search_variance =
5223 network::mojom::SearchParamsVariance::NewNoVaryParams(
5224 std::vector<std::string>({"a"}));
5225 GURL not_matched_url = GURL(kTestUrl + "?a=5");
5226 MakePrefetchOnMainFrame(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225227 not_matched_url, prefetch_type,
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355228 /* referrer */ blink::mojom::Referrer(),
5229 /* no_vary_search_hint */ std::move(no_vary_search_hint));
5230 task_environment()->RunUntilIdle();
5231
kenossbd9a3fc2025-03-28 05:15:575232 VerifyCommonRequestState(not_matched_url,
5233 {.expected_priority = ExpectedPriorityForEagerness(
5234 GetEagernessParam())});
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355235 }
5236 {
5237 network::mojom::NoVarySearchPtr no_vary_search_hint =
5238 network::mojom::NoVarySearch::New();
5239 no_vary_search_hint->vary_on_key_order = true;
5240 no_vary_search_hint->search_variance =
5241 network::mojom::SearchParamsVariance::NewNoVaryParams(
5242 std::vector<std::string>({"b"}));
5243 GURL matched_url = GURL(kTestUrl + "?b=3");
5244 MakePrefetchOnMainFrame(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225245 matched_url, prefetch_type,
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355246 /* referrer */ blink::mojom::Referrer(),
5247 /* no_vary_search_hint */ std::move(no_vary_search_hint));
5248 task_environment()->RunUntilIdle();
5249 VerifyPrefetchAttemptIsPending(matched_url);
5250 }
5251 // Navigate to the URL before the head of the prefetch response is received
5252 NavigateInitiatedByRenderer(GURL(kTestUrl));
5253
5254 // Request the prefetch from the PrefetchService. The given callback shouldn't
5255 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575256 base::test::TestFuture<PrefetchServingHandle> future;
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355257 GetPrefetchToServe(future, GURL(kTestUrl), MainDocumentToken());
5258 EXPECT_FALSE(future.IsReady());
5259 task_environment()->RunUntilIdle();
5260 // Sends the head of the prefetch response. This should not trigger the above
5261 // callback.
5262 SendHeadOfResponseAndWait(
5263 net::HTTP_OK, kHTMLMimeType,
5264 /*use_prefetch_proxy=*/false,
5265 {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"e\")"}},
5266 std::size(kHTMLBody));
5267
5268 // The second should not be used for a real navigation. The rationale is that:
5269 // if a prefetch request has not started before a real navigation starts, then
5270 // it cannot help improve the performance, and in the worst case it would
5271 // block the real navigation.
5272 EXPECT_TRUE(future.IsReady());
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575273 PrefetchServingHandle serving_handle = future.Take();
5274 ASSERT_FALSE(serving_handle);
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355275
5276 // Send the body and completion status of the request,
5277 SendBodyContentOfResponseAndWait(kHTMLBody);
5278 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
5279
5280 SendHeadOfResponseForUrlAndWait(
5281 GURL(kTestUrl + "?b=3"), net::HTTP_OK, kHTMLMimeType,
5282 /*use_prefetch_proxy=*/false,
5283 {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"b\")"}},
5284 std::size(kHTMLBody));
5285 // Check the metrics now that the prefetch is complete.
5286 histogram_tester.ExpectUniqueSample(
5287 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL", false, 2);
5288
5289 std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
5290 PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
5291 EXPECT_EQ(referring_page_metrics->prefetch_attempted_count, 2);
5292 EXPECT_EQ(referring_page_metrics->prefetch_eligible_count, 2);
5293
5294 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:225295 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:405296 histogram_tester.ExpectTotalCount(
5297 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225298 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:405299 histogram_suffix),
5300 0);
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355301 histogram_tester.ExpectUniqueTimeSample(
5302 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225303 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:405304 histogram_suffix),
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355305 base::Milliseconds(0), 1);
5306 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225307 base::StringPrintf(
5308 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
5309 histogram_suffix),
Adithya Srinivasan9d1b6fa2024-08-29 15:04:355310 true, 1);
5311}
5312
Hiroshige Hayashizakice728e4a2025-03-13 20:29:105313TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:485314 DISABLED_CHROMEOS(BlockUntilHeadTimedout)) {
Max Currane6679ca2023-06-06 19:01:395315 base::HistogramTester histogram_tester;
5316
5317 MakePrefetchService(
5318 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
5319
Taiyo Mizuhashi43066071f2025-04-25 23:40:225320 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:585321 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:225322 /*use_prefetch_proxy=*/true, GetEagernessParam());
5323
5324 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:545325 task_environment()->RunUntilIdle();
Max Currane6679ca2023-06-06 19:01:395326
Jeremy Roman4c9524212023-07-27 22:01:055327 VerifyCommonRequestState(
5328 GURL("https://example.com"),
5329 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:575330 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Max Currane6679ca2023-06-06 19:01:395331
5332 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:425333 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Currane6679ca2023-06-06 19:01:395334
5335 // Request the prefetch from the PrefetchService. The given callback should be
5336 // triggered once the timeout is exceeded.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575337 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizaki2df45292023-10-10 22:59:035338 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:335339 EXPECT_FALSE(future.IsReady());
Max Currane6679ca2023-06-06 19:01:395340
5341 task_environment()->FastForwardBy(base::Milliseconds(1000));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575342 PrefetchServingHandle serving_handle = future.Take();
5343 EXPECT_FALSE(serving_handle);
Max Currane6679ca2023-06-06 19:01:395344
5345 // If the prefetch is received after the block until head has timed out, it
5346 // will not be used.
5347 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
5348 /*use_prefetch_proxy=*/true,
5349 {{"X-Testing", "Hello World"}}, kHTMLBody);
5350
5351 // Check the metrics now that the prefetch is complete.
kenossbd9a3fc2025-03-28 05:15:575352 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
5353 GetEagernessParam(),
kenossbe8fbf22024-08-14 12:18:215354 /*is_accurate=*/true);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:465355 ExpectServingMetricsSuccess();
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575356 EXPECT_FALSE(serving_handle);
Max Currane6679ca2023-06-06 19:01:395357
Max Currane6679ca2023-06-06 19:01:395358 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:225359 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:405360 histogram_tester.ExpectTotalCount(
5361 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225362 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:405363 histogram_suffix),
5364 0);
Max Currane6679ca2023-06-06 19:01:395365 histogram_tester.ExpectUniqueTimeSample(
5366 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225367 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:405368 histogram_suffix),
Max Currane6679ca2023-06-06 19:01:395369 base::Milliseconds(1000), 1);
5370 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225371 base::StringPrintf(
5372 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
5373 histogram_suffix),
Max Currane6679ca2023-06-06 19:01:395374 true, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:545375
5376 histogram_tester.ExpectUniqueSample(
5377 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
5378 "PerMatchingCandidate.",
5379 histogram_suffix}),
5380 PrefetchPotentialCandidateServingResult::kNotServedBlockUntilHeadTimeout,
5381 1);
Max Currane6679ca2023-06-06 19:01:395382}
5383
Hiroshige Hayashizakice728e4a2025-03-13 20:29:105384TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:485385 DISABLED_CHROMEOS(HeadReceivedBeforeTimeout)) {
Max Currane6679ca2023-06-06 19:01:395386 base::HistogramTester histogram_tester;
5387
5388 MakePrefetchService(
5389 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
5390
Taiyo Mizuhashi43066071f2025-04-25 23:40:225391 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:585392 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:225393 /*use_prefetch_proxy=*/true, GetEagernessParam());
5394 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:545395 task_environment()->RunUntilIdle();
Max Currane6679ca2023-06-06 19:01:395396
Jeremy Roman4c9524212023-07-27 22:01:055397 VerifyCommonRequestState(
5398 GURL("https://example.com"),
5399 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:575400 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Max Currane6679ca2023-06-06 19:01:395401
5402 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:425403 NavigateInitiatedByRenderer(GURL("https://example.com"));
Max Currane6679ca2023-06-06 19:01:395404
5405 // Request the prefetch from the PrefetchService. The given callback should be
5406 // triggered once the timeout is exceeded.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575407 base::test::TestFuture<PrefetchServingHandle> future;
Hiroshige Hayashizaki2df45292023-10-10 22:59:035408 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
Hiroshige Hayashizakibc780622023-09-05 19:07:335409 EXPECT_FALSE(future.IsReady());
Max Currane6679ca2023-06-06 19:01:395410
5411 task_environment()->FastForwardBy(base::Milliseconds(1000));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575412 PrefetchServingHandle serving_handle = future.Take();
5413 EXPECT_FALSE(serving_handle);
Max Currane6679ca2023-06-06 19:01:395414
5415 // If the prefetch is received after the block until head has timed out, it
5416 // will not be used.
5417 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
5418 /*use_prefetch_proxy=*/true,
5419 {{"X-Testing", "Hello World"}}, kHTMLBody);
5420
5421 // Check the metrics now that the prefetch is complete.
kenossbd9a3fc2025-03-28 05:15:575422 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
5423 GetEagernessParam(),
kenossbe8fbf22024-08-14 12:18:215424 /*is_accurate=*/true);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:465425 ExpectServingMetricsSuccess();
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575426 EXPECT_FALSE(serving_handle);
Max Currane6679ca2023-06-06 19:01:395427
Max Currane6679ca2023-06-06 19:01:395428 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:225429 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:405430 histogram_tester.ExpectTotalCount(
5431 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225432 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:405433 histogram_suffix),
5434 0);
Max Currane6679ca2023-06-06 19:01:395435 histogram_tester.ExpectUniqueTimeSample(
5436 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225437 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:405438 histogram_suffix),
Max Currane6679ca2023-06-06 19:01:395439 base::Milliseconds(1000), 1);
5440 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225441 base::StringPrintf(
5442 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
5443 histogram_suffix),
Max Currane6679ca2023-06-06 19:01:395444 true, 1);
Taiyo Mizuhashi09f571f2025-08-04 16:05:545445
5446 histogram_tester.ExpectUniqueSample(
5447 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
5448 "PerMatchingCandidate.",
5449 histogram_suffix}),
5450 PrefetchPotentialCandidateServingResult::kNotServedBlockUntilHeadTimeout,
5451 1);
Max Currane6679ca2023-06-06 19:01:395452}
5453
Georg Neis0dce3332024-12-13 01:51:185454// TODO(crbug.com/40249481): Test flaky on trybots.
Hiroshige Hayashizakice728e4a2025-03-13 20:29:105455TEST_P(PrefetchServiceAlwaysBlockUntilHeadTest,
kenossda6656b2024-07-23 02:18:485456 DISABLED_CHROMEOS(MultipleGetPrefetchToServe)) {
Max Curranaefeb772023-08-01 18:03:105457 base::HistogramTester histogram_tester;
5458
5459 MakePrefetchService(
5460 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
5461
Taiyo Mizuhashi43066071f2025-04-25 23:40:225462 const PrefetchType prefetch_type =
Kouhei Uenoc5c2ea202023-11-14 08:58:585463 PrefetchType(PreloadingTriggerType::kSpeculationRule,
Taiyo Mizuhashi43066071f2025-04-25 23:40:225464 /*use_prefetch_proxy=*/true, GetEagernessParam());
5465 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:545466 task_environment()->RunUntilIdle();
Max Curranaefeb772023-08-01 18:03:105467
5468 VerifyCommonRequestState(
5469 GURL("https://example.com"),
5470 {.use_prefetch_proxy = true,
kenossbd9a3fc2025-03-28 05:15:575471 .expected_priority = ExpectedPriorityForEagerness(GetEagernessParam())});
Hiroshige Hayashizakifc691c52023-10-23 10:07:545472
5473 // Navigate to the URL before the head of the prefetch response is received
Taiyo Mizuhashi01d86af22024-03-27 14:27:425474 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizakifc691c52023-10-23 10:07:545475 // Request the prefetch from the PrefetchService. The same prefetch will be
5476 // requested again, so this callback will not be called.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575477 base::test::TestFuture<PrefetchServingHandle> first_future;
Taiyo Mizuhashi01d86af22024-03-27 14:27:425478 GetPrefetchToServe(first_future, GURL("https://example.com"),
5479 MainDocumentToken());
Hiroshige Hayashizakifc691c52023-10-23 10:07:545480
Taiyo Mizuhashi01d86af22024-03-27 14:27:425481 NavigateInitiatedByRenderer(GURL("https://example.com"));
Hiroshige Hayashizakifc691c52023-10-23 10:07:545482 // Request the prefetch from the PrefetchService a second time. This
5483 // callback should be triggered once the timeout is exceeded.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575484 base::test::TestFuture<PrefetchServingHandle> second_future;
Hiroshige Hayashizakifc691c52023-10-23 10:07:545485 GetPrefetchToServe(second_future, GURL("https://example.com"),
5486 MainDocumentToken());
5487 EXPECT_FALSE(second_future.IsReady());
5488 task_environment()->FastForwardBy(base::Milliseconds(1000));
kenoss968f5dd2025-03-10 06:27:405489 EXPECT_TRUE(first_future.IsReady());
5490 EXPECT_TRUE(second_future.IsReady());
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575491 PrefetchServingHandle serving_handle = second_future.Take();
5492 EXPECT_FALSE(serving_handle);
Hiroshige Hayashizakifc691c52023-10-23 10:07:545493
Max Curranaefeb772023-08-01 18:03:105494 // If the prefetch is received after the block until head has timed out, it
5495 // will not be used.
5496 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
5497 /*use_prefetch_proxy=*/true,
5498 {{"X-Testing", "Hello World"}}, kHTMLBody);
5499
5500 // Check the metrics now that the prefetch is complete.
kenossbd9a3fc2025-03-28 05:15:575501 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
5502 GetEagernessParam(),
kenossbe8fbf22024-08-14 12:18:215503 /*is_accurate=*/true);
Hiroshige Hayashizaki120cc23d2023-10-21 00:44:465504 ExpectServingMetricsSuccess();
Max Curranaefeb772023-08-01 18:03:105505
5506 std::string histogram_suffix =
Taiyo Mizuhashi43066071f2025-04-25 23:40:225507 GetMetricsSuffixTriggerTypeAndEagerness(prefetch_type, std::nullopt);
kenoss968f5dd2025-03-10 06:27:405508 histogram_tester.ExpectTotalCount(
5509 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225510 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
kenoss968f5dd2025-03-10 06:27:405511 histogram_suffix),
5512 0);
Max Curranaefeb772023-08-01 18:03:105513 histogram_tester.ExpectUniqueTimeSample(
5514 base::StringPrintf(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225515 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
kenoss968f5dd2025-03-10 06:27:405516 histogram_suffix),
5517 base::Milliseconds(1000), 2);
5518 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:225519 base::StringPrintf(
5520 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
5521 histogram_suffix),
kenoss968f5dd2025-03-10 06:27:405522 true, 2);
Max Curranaefeb772023-08-01 18:03:105523}
5524
Taiyo Mizuhashicd08a8f2025-06-10 17:27:325525class PrefetchServiceDisableBlockUntilHeadTimeoutTest
5526 : public PrefetchServiceTestBase,
5527 public WithPrefetchServiceRearchParam,
5528 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
5529 public:
5530 PrefetchServiceDisableBlockUntilHeadTimeoutTest()
5531 : WithPrefetchServiceRearchParam(GetParam()) {}
5532
5533 static constexpr int kBlockUntilHeadTimeout = 1000;
5534
5535 void InitScopedFeatureList() override {
5536 InitBaseParams();
5537 InitRearchFeatures();
5538 // Override `kPrefetchUseContentRefactor`.
5539 scoped_feature_list_.InitWithFeaturesAndParameters(
5540 {{features::kPrefetchUseContentRefactor,
5541 {
5542 {"ineligible_decoy_request_probability", "0"},
5543 {"prefetch_container_lifetime_s", "-1"},
5544 {"prefetch_timeout_ms", "10000"},
5545 // Initialize > 0ms timeouts for testing purposes.
5546 {"block_until_head_timeout_embedder_prefetch",
5547 base::NumberToString(kBlockUntilHeadTimeout)},
5548 }}},
5549 {});
5550 }
5551
5552 private:
5553 base::test::ScopedFeatureList scoped_feature_list_;
5554};
5555
5556INSTANTIATE_TEST_SUITE_P(
5557 ParametrizedTests,
5558 PrefetchServiceDisableBlockUntilHeadTimeoutTest,
5559 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
5560
5561// Tests that the default `BlockUntilHeadTimeout` is used if
5562// `should_disable_block_until_head_timeout` is false.
5563TEST_P(PrefetchServiceDisableBlockUntilHeadTimeoutTest,
5564 DISABLED_CHROMEOS(DisableBlockUntilHeadTimeoutFalse)) {
5565 base::HistogramTester histogram_tester;
5566 MakePrefetchService(
5567 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
5568 /*num_on_prefetch_likely_calls=*/std::nullopt));
5569
5570 // Set `should_disable_block_until_head_timeout` to false.
5571 std::unique_ptr<content::PrefetchHandle> handle =
5572 MakePrefetchFromBrowserContext(
5573 GURL("https://example.com"),
5574 /*no_vary_search_data=*/std::nullopt, /*additional_headers=*/{},
5575 /*request_status_listener=*/nullptr,
5576 base::Seconds(/* 10 minutes */ 60 * 10),
5577 /*should_disable_block_until_head_timeout=*/false);
5578 task_environment()->RunUntilIdle();
5579
Taiyo Mizuhashid13f8ca2025-06-23 13:29:025580 VerifyCommonRequestStateForBrowserContextPrefetch(
5581 GURL("https://example.com"), {.use_prefetch_proxy = false});
Taiyo Mizuhashicd08a8f2025-06-10 17:27:325582
5583 // Simulate a navigation. The prefetch should not be served after a timeout.
5584 std::unique_ptr<NavigationResult> navigation_result =
5585 SimulatePartOfNavigation(GURL("https://example.com"),
5586 /*is_renderer_initiated=*/false,
5587 /*is_nav_prerender=*/false);
5588 task_environment()->RunUntilIdle();
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575589 ASSERT_FALSE(navigation_result->serving_handle_future.IsReady());
Taiyo Mizuhashicd08a8f2025-06-10 17:27:325590 task_environment()->FastForwardBy(base::Milliseconds(kBlockUntilHeadTimeout));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575591 EXPECT_TRUE(navigation_result->serving_handle_future.IsReady());
5592 EXPECT_FALSE(navigation_result->serving_handle_future.Take());
Taiyo Mizuhashicd08a8f2025-06-10 17:27:325593
5594 auto metrics_suffix = GetMetricsSuffixTriggerTypeAndEagerness(
5595 PrefetchType(PreloadingTriggerType::kEmbedder,
5596 /*use_prefetch_proxy=*/false),
5597 test::kPreloadingEmbedderHistgramSuffixForTesting);
5598 histogram_tester.ExpectUniqueSample(
5599 base::StringPrintf(
5600 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
5601 metrics_suffix),
5602 true, 1);
5603 histogram_tester.ExpectUniqueTimeSample(
5604 base::StringPrintf(
5605 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
5606 metrics_suffix),
5607 base::Milliseconds(kBlockUntilHeadTimeout), 1);
5608 histogram_tester.ExpectTotalCount(
5609 base::StringPrintf(
5610 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
5611 metrics_suffix),
5612 0);
5613}
5614
5615// Tests that the default `BlockUntilHeadTimeout` is ignored if
5616// `should_disable_block_until_head_timeout` is true.
5617TEST_P(PrefetchServiceDisableBlockUntilHeadTimeoutTest,
5618 DISABLED_CHROMEOS(DisableBlockUntilHeadTimeoutTrue)) {
5619 base::HistogramTester histogram_tester;
5620 MakePrefetchService(
5621 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
5622 /*num_on_prefetch_likely_calls=*/std::nullopt));
5623
5624 // Set `should_disable_block_until_head_timeout` to true.
5625 std::unique_ptr<content::PrefetchHandle> handle =
5626 MakePrefetchFromBrowserContext(
5627 GURL("https://example.com"),
5628 /*no_vary_search_data=*/std::nullopt, /*additional_headers=*/{},
5629 /*request_status_listener=*/nullptr,
5630 base::Seconds(/* 10 minutes */ 60 * 10),
5631 /*should_disable_block_until_head_timeout=*/true);
5632 task_environment()->RunUntilIdle();
5633
Taiyo Mizuhashid13f8ca2025-06-23 13:29:025634 VerifyCommonRequestStateForBrowserContextPrefetch(
5635 GURL("https://example.com"), {.use_prefetch_proxy = false});
Taiyo Mizuhashicd08a8f2025-06-10 17:27:325636
5637 // Simulate a navigation. The prefetch still blocks the navigation, after the
5638 // default timeout/
5639 std::unique_ptr<NavigationResult> navigation_result =
5640 SimulatePartOfNavigation(GURL("https://example.com"),
5641 /*is_renderer_initiated=*/false,
5642 /*is_nav_prerender=*/false);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575643 ASSERT_FALSE(navigation_result->serving_handle_future.IsReady());
Taiyo Mizuhashicd08a8f2025-06-10 17:27:325644 task_environment()->FastForwardBy(base::Milliseconds(kBlockUntilHeadTimeout));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575645 EXPECT_FALSE(navigation_result->serving_handle_future.IsReady());
Taiyo Mizuhashicd08a8f2025-06-10 17:27:325646
5647 // It is eventually served after creating a response head.
5648 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
5649 /*use_prefetch_proxy=*/true,
5650 {{"X-Testing", "Hello World"}}, kHTMLBody);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575651 EXPECT_TRUE(navigation_result->serving_handle_future.IsReady());
5652 EXPECT_TRUE(navigation_result->serving_handle_future.Take());
Taiyo Mizuhashicd08a8f2025-06-10 17:27:325653
5654 auto metrics_suffix = GetMetricsSuffixTriggerTypeAndEagerness(
5655 PrefetchType(PreloadingTriggerType::kEmbedder,
5656 /*use_prefetch_proxy=*/false),
5657 test::kPreloadingEmbedderHistgramSuffixForTesting);
5658 histogram_tester.ExpectUniqueSample(
5659 base::StringPrintf(
5660 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.%s",
5661 metrics_suffix),
5662 true, 1);
5663 histogram_tester.ExpectUniqueTimeSample(
5664 base::StringPrintf(
5665 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served.%s",
5666 metrics_suffix),
5667 base::Milliseconds(kBlockUntilHeadTimeout), 1);
5668 histogram_tester.ExpectTotalCount(
5669 base::StringPrintf(
5670 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed.%s",
5671 metrics_suffix),
5672 0);
5673}
5674
Taiyo Mizuhashiaf23c432025-06-23 07:41:375675// Tests that browsing data removal for prefetch is performed per 1) its
5676// `referring_origin` 2) if that is std::nullopt, then prefetch url.
5677TEST_P(PrefetchServiceTest, PrefetchEviction) {
5678 base::HistogramTester histogram_tester;
5679
5680 struct TestCase {
5681 const std::optional<url::Origin> referring_origin;
5682 const GURL prefetch_url;
5683 };
5684 const std::vector<TestCase> test_cases = {
5685 {url::Origin::Create(GURL("https://a.test")), GURL("https://a.test/0")},
5686 {url::Origin::Create(GURL("https://a.test")), GURL("https://b.test/1")},
5687 {url::Origin::Create(GURL("https://b.test")), GURL("https://a.test/2")},
5688 {url::Origin::Create(GURL("https://b.test")), GURL("https://b.test/3")},
5689 {std::nullopt, GURL("https://a.test/4")},
5690 {std::nullopt, GURL("https://b.test/5")},
5691 };
5692
5693 MakePrefetchService(
5694 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
5695 /*num_on_prefetch_likely_calls=*/std::nullopt));
5696 PrefetchService* prefetch_service =
5697 BrowserContextImpl::From(browser_context())->GetPrefetchService();
5698
5699 std::vector<std::unique_ptr<PrefetchHandle>> handles;
5700 for (const auto& test_case : test_cases) {
5701 handles.push_back(
5702 MakePrefetchFromEmbedder(test_case.prefetch_url,
5703 PrefetchType(PreloadingTriggerType::kEmbedder,
5704 /*use_prefetch_proxy=*/false),
5705 /*referrer=*/{}, test_case.referring_origin));
5706 }
5707 task_environment()->RunUntilIdle();
5708
5709 // Evict prefetches from "a.test". The prefetch for "a.test" with no
5710 // `referring_origin` should also be removed.
5711 auto filter_builder = BrowsingDataFilterBuilder::Create(
5712 BrowsingDataFilterBuilder::Mode::kDelete);
5713 filter_builder->AddOrigin(url::Origin::Create(GURL("https://a.test")));
5714 auto filter = filter_builder->BuildStorageKeyFilter();
5715 prefetch_service->EvictPrefetchesForBrowsingDataRemoval(
5716 filter, PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved);
5717 task_environment()->RunUntilIdle();
5718 EXPECT_FALSE(handles[0]->IsAlive());
5719 EXPECT_FALSE(handles[1]->IsAlive());
5720 EXPECT_FALSE(handles[4]->IsAlive());
5721 EXPECT_TRUE(handles[2]->IsAlive());
5722 EXPECT_TRUE(handles[3]->IsAlive());
5723 EXPECT_TRUE(handles[5]->IsAlive());
5724 histogram_tester.ExpectUniqueSample(
5725 "Preloading.Prefetch.PrefetchStatus",
5726 PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 3);
5727
5728 // Attempt to clear all the cache. The remaining prefetches are also removed.
5729 prefetch_service->EvictPrefetchesForBrowsingDataRemoval(
5730 BrowsingDataFilterBuilder::Create(
5731 BrowsingDataFilterBuilder::Mode::kPreserve)
5732 ->BuildStorageKeyFilter(),
5733 PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved);
5734 task_environment()->RunUntilIdle();
5735 EXPECT_FALSE(handles[2]->IsAlive());
5736 EXPECT_FALSE(handles[3]->IsAlive());
5737 EXPECT_FALSE(handles[5]->IsAlive());
5738 histogram_tester.ExpectUniqueSample(
5739 "Preloading.Prefetch.PrefetchStatus",
5740 PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved, 6);
5741}
5742
Taiyo Mizuhashi070baec2025-03-28 15:37:005743// Tests that the prefetch eviction for eligible but not started triggers (i.e.
5744// `PreloadingAttempt`'s `PreloadingHoldbackStatus` is `kUnspecified`) causes no
5745// crash. This is a regression test of crbug.com/404703517.
5746TEST_P(PrefetchServiceTest, PrefetchEvictionForEligibleButNotStartedPrefetch) {
5747 NavigateAndCommit(GURL("https://example.com"));
5748 MakePrefetchService(
5749 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
5750 /*num_on_prefetch_likely_calls=*/2));
5751
5752 const auto url_1 = GURL("https://example.com/one");
5753 const auto url_2 = GURL("https://example.com/two");
5754 auto candidate_1 = blink::mojom::SpeculationCandidate::New();
5755 candidate_1->url = url_1;
5756 candidate_1->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:255757 candidate_1->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Taiyo Mizuhashi070baec2025-03-28 15:37:005758 candidate_1->referrer = blink::mojom::Referrer::New();
5759 auto candidate_2 = candidate_1.Clone();
5760 candidate_2->url = url_2;
5761
5762 // Send `candidate_1` and `candidate_2`.
5763 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
5764 candidates.push_back(candidate_1.Clone());
5765 candidates.push_back(candidate_2.Clone());
5766 auto* prefetch_document_manager =
5767 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
5768 prefetch_document_manager->ProcessCandidates(candidates);
5769 task_environment()->RunUntilIdle();
5770
5771 PrefetchService* prefetch_service =
5772 BrowserContextImpl::From(browser_context())->GetPrefetchService();
5773 base::WeakPtr<PrefetchContainer> prefetch_container1, prefetch_container2;
5774 std::tie(std::ignore, prefetch_container1) =
5775 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
5776 PrefetchContainer::Key(MainDocumentToken(), url_1))[0];
5777 std::tie(std::ignore, prefetch_container2) =
5778 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
5779 PrefetchContainer::Key(MainDocumentToken(), url_2))[0];
5780
5781 // `candidate_1` should be started, while `candidate_2` stays in a queue.
5782 ASSERT_EQ(prefetch_container1->GetLoadState(),
5783 PrefetchContainer::LoadState::kStarted);
5784 ASSERT_EQ(prefetch_container2->GetLoadState(),
5785 PrefetchContainer::LoadState::kEligible);
5786
5787 // Try to evict.
5788 prefetch_service->EvictPrefetchesForBrowsingDataRemoval(
5789 BrowsingDataFilterBuilder::Create(
5790 BrowsingDataFilterBuilder::Mode::kPreserve)
5791 ->BuildStorageKeyFilter(),
5792 PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved);
5793
5794 // - `candidate_1` should have `PreloadingEligibility::kEligible`,
5795 // `PreloadingHoldbackStatus::kAllowed` and
5796 // `PreloadingTriggeringOutcome::kFailure` as
5797 // `kPrefetchEvictedAfterBrowsingDataRemoved`.
5798 // - `candidate_2` should have `PreloadingEligibility::kEligible` but
5799 // `PreloadingHoldbackStatus::kUnspecified` (as the holdback status will be
5800 // determined when the prefetch is actually started) and
5801 // `PreloadingTriggeringOutcome::kUnspecified`.
5802 {
5803 const auto source_id = ForceLogsUploadAndGetUkmId();
5804 auto actual_attempts = test_ukm_recorder()->GetEntries(
5805 ukm::builders::Preloading_Attempt::kEntryName,
5806 test::kPreloadingAttemptUkmMetrics);
5807 ASSERT_EQ(actual_attempts.size(), 2u);
5808 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> expected_attempts =
5809 {attempt_entry_builder()->BuildEntry(
5810 source_id, PreloadingType::kPrefetch,
5811 PreloadingEligibility::kEligible,
5812 PreloadingHoldbackStatus::kAllowed,
5813 PreloadingTriggeringOutcome::kFailure,
5814 ToPreloadingFailureReason(
5815 PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved),
5816 /*accurate=*/false,
5817 /*ready_time=*/std::nullopt,
Takashi Nakayama978f0a152025-06-17 08:26:255818 blink::mojom::SpeculationEagerness::kImmediate),
Taiyo Mizuhashi070baec2025-03-28 15:37:005819 attempt_entry_builder()->BuildEntry(
5820 source_id, PreloadingType::kPrefetch,
5821 PreloadingEligibility::kEligible,
5822 PreloadingHoldbackStatus::kUnspecified,
5823 PreloadingTriggeringOutcome::kUnspecified,
5824 PreloadingFailureReason::kUnspecified,
5825 /*accurate=*/false,
5826 /*ready_time=*/std::nullopt,
Takashi Nakayama978f0a152025-06-17 08:26:255827 blink::mojom::SpeculationEagerness::kImmediate)};
Taiyo Mizuhashi070baec2025-03-28 15:37:005828 ASSERT_THAT(actual_attempts,
5829 testing::UnorderedElementsAreArray(expected_attempts))
5830 << test::ActualVsExpectedUkmEntriesToString(actual_attempts,
5831 expected_attempts);
5832 }
5833}
5834
5835// Tests that the prefetch eviction during eligiblity check (i.e.
5836// `PreloadingAttempt`'s `PreloadingEligibility` is `kUnspecified`) causes no
5837// crash. This is a regression test of crbug.com/404703517.
5838TEST_P(PrefetchServiceTest, PrefetchEvictionDuringEligiblityCheck) {
5839 NavigateAndCommit(GURL("https://example.com"));
5840 MakePrefetchService(
5841 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
5842 /*num_on_prefetch_likely_calls=*/1));
5843 PrefetchService* prefetch_service =
5844 BrowserContextImpl::From(browser_context())->GetPrefetchService();
5845
5846 // Pause the elibility check.
5847 base::test::TestFuture<base::OnceClosure> eligibility_check_callback_future;
5848 prefetch_service->SetDelayEligibilityCheckForTesting(base::BindRepeating(
5849 [](base::test::TestFuture<base::OnceClosure>*
5850 eligibility_check_callback_future,
5851 base::OnceClosure callback) {
5852 eligibility_check_callback_future->SetValue(std::move(callback));
5853 },
5854 base::Unretained(&eligibility_check_callback_future)));
5855
5856 const auto url_1 = GURL("https://example.com/one");
5857 auto candidate_1 = blink::mojom::SpeculationCandidate::New();
5858 candidate_1->url = url_1;
5859 candidate_1->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:255860 candidate_1->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Taiyo Mizuhashi070baec2025-03-28 15:37:005861 candidate_1->referrer = blink::mojom::Referrer::New();
5862
5863 // Send `candidate_1`;
5864 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
5865 candidates.push_back(candidate_1.Clone());
5866 auto* prefetch_document_manager =
5867 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
5868 prefetch_document_manager->ProcessCandidates(candidates);
5869 task_environment()->RunUntilIdle();
5870
5871 base::WeakPtr<PrefetchContainer> prefetch_container1;
5872 std::tie(std::ignore, prefetch_container1) =
5873 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
5874 PrefetchContainer::Key(MainDocumentToken(), url_1))[0];
5875
5876 // `candidate_1` should be on a way of eligibility check.
5877 ASSERT_EQ(prefetch_container1->GetLoadState(),
5878 PrefetchContainer::LoadState::kNotStarted);
5879
5880 // Try to evict.
5881 prefetch_service->EvictPrefetchesForBrowsingDataRemoval(
5882 BrowsingDataFilterBuilder::Create(
5883 BrowsingDataFilterBuilder::Mode::kPreserve)
5884 ->BuildStorageKeyFilter(),
5885 PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved);
5886 {
5887 const auto source_id = ForceLogsUploadAndGetUkmId();
5888 auto actual_attempts = test_ukm_recorder()->GetEntries(
5889 ukm::builders::Preloading_Attempt::kEntryName,
5890 test::kPreloadingAttemptUkmMetrics);
5891 ASSERT_EQ(actual_attempts.size(), 1u);
5892 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> expected_attempts =
5893 {attempt_entry_builder()->BuildEntry(
5894 source_id, PreloadingType::kPrefetch,
5895 PreloadingEligibility::kUnspecified,
5896 PreloadingHoldbackStatus::kUnspecified,
5897 PreloadingTriggeringOutcome::kUnspecified,
5898 PreloadingFailureReason::kUnspecified,
5899 /*accurate=*/false,
5900 /*ready_time=*/std::nullopt,
Takashi Nakayama978f0a152025-06-17 08:26:255901 blink::mojom::SpeculationEagerness::kImmediate)};
Taiyo Mizuhashi070baec2025-03-28 15:37:005902 ASSERT_THAT(actual_attempts,
5903 testing::UnorderedElementsAreArray(expected_attempts))
5904 << test::ActualVsExpectedUkmEntriesToString(actual_attempts,
5905 expected_attempts);
5906 }
5907
Hiroshige Hayashizakif4fc22602025-07-23 01:01:025908 // Resume the elibility check (currently this is just to satisfy the preferred
5909 // invariant that `PrefetchService::OnGotEligibility*()` is always called for
5910 // each eligibility check.
5911 eligibility_check_callback_future.Take().Run();
5912
Taiyo Mizuhashi070baec2025-03-28 15:37:005913 prefetch_service->SetDelayEligibilityCheckForTesting(base::NullCallback());
5914}
5915
5916// Tests that the prefetch eviction for heldback triggers causes no crash. This
5917// is a regression test of crbug.com/404703517.
5918TEST_P(PrefetchServiceTest, PrefetchEvictionWhenHoldback) {
5919 content::test::PreloadingConfigOverride preloading_config_override;
5920 preloading_config_override.SetHoldback(
5921 PreloadingType::kPrefetch,
5922 content_preloading_predictor::kSpeculationRules, true);
5923
5924 NavigateAndCommit(GURL("https://example.com"));
5925 MakePrefetchService(
5926 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
5927 /*num_on_prefetch_likely_calls=*/1));
5928 PrefetchService* prefetch_service =
5929 BrowserContextImpl::From(browser_context())->GetPrefetchService();
5930
5931 const auto url_1 = GURL("https://example.com/one");
5932 auto candidate_1 = blink::mojom::SpeculationCandidate::New();
5933 candidate_1->url = url_1;
5934 candidate_1->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:255935 candidate_1->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Taiyo Mizuhashi070baec2025-03-28 15:37:005936 candidate_1->referrer = blink::mojom::Referrer::New();
5937
5938 // Send `candidate_1`;
5939 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
5940 candidates.push_back(candidate_1.Clone());
5941 auto* prefetch_document_manager =
5942 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
5943 prefetch_document_manager->ProcessCandidates(candidates);
5944 task_environment()->RunUntilIdle();
5945
5946 base::WeakPtr<PrefetchContainer> prefetch_container1;
5947 std::tie(std::ignore, prefetch_container1) =
5948 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
5949 PrefetchContainer::Key(MainDocumentToken(), url_1))[0];
5950 task_environment()->RunUntilIdle();
5951
5952 // `candidate_1` should be failed as heldback
5953 ASSERT_EQ(prefetch_container1->GetLoadState(),
5954 PrefetchContainer::LoadState::kFailedHeldback);
5955
5956 // Try to evict.
5957 prefetch_service->EvictPrefetchesForBrowsingDataRemoval(
5958 BrowsingDataFilterBuilder::Create(
5959 BrowsingDataFilterBuilder::Mode::kPreserve)
5960 ->BuildStorageKeyFilter(),
5961 PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved);
5962 {
5963 const auto source_id = ForceLogsUploadAndGetUkmId();
5964 auto actual_attempts = test_ukm_recorder()->GetEntries(
5965 ukm::builders::Preloading_Attempt::kEntryName,
5966 test::kPreloadingAttemptUkmMetrics);
5967 ASSERT_EQ(actual_attempts.size(), 1u);
5968 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> expected_attempts =
5969 {attempt_entry_builder()->BuildEntry(
5970 source_id, PreloadingType::kPrefetch,
5971 PreloadingEligibility::kEligible,
5972 PreloadingHoldbackStatus::kHoldback,
5973 PreloadingTriggeringOutcome::kUnspecified,
5974 PreloadingFailureReason::kUnspecified,
5975 /*accurate=*/false,
5976 /*ready_time=*/std::nullopt,
Takashi Nakayama978f0a152025-06-17 08:26:255977 blink::mojom::SpeculationEagerness::kImmediate)};
Taiyo Mizuhashi070baec2025-03-28 15:37:005978 ASSERT_THAT(actual_attempts,
5979 testing::UnorderedElementsAreArray(expected_attempts))
5980 << test::ActualVsExpectedUkmEntriesToString(actual_attempts,
5981 expected_attempts);
5982 }
5983}
5984
Domenic Denicola7eaf450e2025-06-17 02:00:045985class PrefetchServiceLimitsTest
kenossbd9a3fc2025-03-28 05:15:575986 : public PrefetchServiceTestBase,
5987 public WithPrefetchServiceRearchParam,
5988 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
Adithya Srinivasanf393dd02023-05-16 20:26:165989 public:
Domenic Denicola7eaf450e2025-06-17 02:00:045990 PrefetchServiceLimitsTest() : WithPrefetchServiceRearchParam(GetParam()) {}
kenossbd9a3fc2025-03-28 05:15:575991
Adithya Srinivasanf393dd02023-05-16 20:26:165992 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:155993 InitBaseParams();
5994 InitRearchFeatures();
Adithya Srinivasanf393dd02023-05-16 20:26:165995 }
Adithya Srinivasan6054e132023-05-18 17:38:395996
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:575997 PrefetchServingHandle CompletePrefetch(
Adithya Srinivasan6054e132023-05-18 17:38:395998 GURL url,
5999 blink::mojom::SpeculationEagerness eagerness) {
6000 MakePrefetchOnMainFrame(
Kouhei Uenoc5c2ea202023-11-14 08:58:586001 url, PrefetchType(PreloadingTriggerType::kSpeculationRule,
6002 /*use_prefetch_proxy=*/false, eagerness));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546003 task_environment()->RunUntilIdle();
Jeremy Roman4c9524212023-07-27 22:01:056004 return CompleteExistingPrefetch(
6005 url, {.expected_priority = ExpectedPriorityForEagerness(eagerness)});
Adithya Srinivasanb60483492023-06-14 21:22:146006 }
6007
6008 // Unlike the above method, this expects the prefetch for |url| to have
6009 // already been triggered.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:576010 PrefetchServingHandle CompleteExistingPrefetch(
Jeremy Roman4c9524212023-07-27 22:01:056011 GURL url,
6012 const VerifyCommonRequestStateOptions& common_options = {}) {
6013 VerifyCommonRequestState(url, common_options);
Adithya Srinivasan6054e132023-05-18 17:38:396014 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
6015 /*use_prefetch_proxy=*/false,
6016 {{"X-Testing", "Hello World"}}, kHTMLBody);
Taiyo Mizuhashi01d86af22024-03-27 14:27:426017 NavigateInitiatedByRenderer(url);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486018 return GetPrefetchToServe(url);
Adithya Srinivasan6054e132023-05-18 17:38:396019 }
Adithya Srinivasanf393dd02023-05-16 20:26:166020};
6021
kenossbd9a3fc2025-03-28 05:15:576022INSTANTIATE_TEST_SUITE_P(
6023 ParametrizedTests,
Domenic Denicola7eaf450e2025-06-17 02:00:046024 PrefetchServiceLimitsTest,
kenossbd9a3fc2025-03-28 05:15:576025 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
6026
Domenic Denicola7eaf450e2025-06-17 02:00:046027TEST_P(PrefetchServiceLimitsTest,
Takashi Nakayama978f0a152025-06-17 08:26:256028 NonImmediatePrefetchAllowedWhenImmediateLimitIsReached) {
Domenic Denicola7eaf450e2025-06-17 02:00:046029 const GURL url_over_limit = GURL("https://example.com/over_limit");
6030 const GURL url_conservative = GURL("https://example.com/conservative");
Adithya Srinivasanf393dd02023-05-16 20:26:166031
6032 NavigateAndCommit(GURL("https://example.com"));
6033
Takashi Nakayama978f0a152025-06-17 08:26:256034 MakePrefetchService(std::make_unique<
6035 testing::NiceMock<MockPrefetchServiceDelegate>>(
6036 /*num_on_prefetch_likely_calls=*/kMaxNumberOfImmediatePrefetchesPerPage +
6037 2));
Adithya Srinivasanf393dd02023-05-16 20:26:166038
Takashi Nakayama978f0a152025-06-17 08:26:256039 for (int i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046040 const GURL url("https://example.com/" + base::NumberToString(i));
6041 ASSERT_TRUE(
Takashi Nakayama978f0a152025-06-17 08:26:256042 CompletePrefetch(url, blink::mojom::SpeculationEagerness::kImmediate));
Domenic Denicola7eaf450e2025-06-17 02:00:046043 }
Adithya Srinivasan6054e132023-05-18 17:38:396044
Takashi Nakayama978f0a152025-06-17 08:26:256045 // Note: |url_over_limit| is not prefetched as the limit for immediate
6046 // prefetches has been reached.
Adithya Srinivasanf393dd02023-05-16 20:26:166047 MakePrefetchOnMainFrame(
Takashi Nakayama978f0a152025-06-17 08:26:256048 url_over_limit,
6049 PrefetchType(PreloadingTriggerType::kSpeculationRule,
6050 /*use_prefetch_proxy=*/false,
6051 blink::mojom::SpeculationEagerness::kImmediate));
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546052 task_environment()->RunUntilIdle();
Adithya Srinivasanf393dd02023-05-16 20:26:166053 EXPECT_EQ(RequestCount(), 0);
Domenic Denicola7eaf450e2025-06-17 02:00:046054 NavigateInitiatedByRenderer(url_over_limit);
6055 ASSERT_FALSE(GetPrefetchToServe(url_over_limit));
Adithya Srinivasanf393dd02023-05-16 20:26:166056
Domenic Denicola7eaf450e2025-06-17 02:00:046057 // We can still prefetch |url_conservative| as it is a conservative prefetch.
Takashi Nakayama978f0a152025-06-17 08:26:256058 auto non_immediate_prefetch = CompletePrefetch(
Domenic Denicola7eaf450e2025-06-17 02:00:046059 url_conservative, blink::mojom::SpeculationEagerness::kConservative);
Takashi Nakayama978f0a152025-06-17 08:26:256060 ASSERT_TRUE(non_immediate_prefetch);
6061 EXPECT_EQ(non_immediate_prefetch.GetPrefetchStatus(),
Adithya Srinivasanf393dd02023-05-16 20:26:166062 PrefetchStatus::kPrefetchSuccessful);
6063
Arthur Sonzognic686e8f2024-01-11 08:36:376064 std::optional<PrefetchReferringPageMetrics> referring_page_metrics =
Adithya Srinivasan6054e132023-05-18 17:38:396065 PrefetchReferringPageMetrics::GetForCurrentDocument(main_rfh());
Domenic Denicola7eaf450e2025-06-17 02:00:046066 EXPECT_EQ(referring_page_metrics->prefetch_attempted_count,
Takashi Nakayama978f0a152025-06-17 08:26:256067 kMaxNumberOfImmediatePrefetchesPerPage + 2);
Domenic Denicola7eaf450e2025-06-17 02:00:046068 EXPECT_EQ(referring_page_metrics->prefetch_eligible_count,
Takashi Nakayama978f0a152025-06-17 08:26:256069 kMaxNumberOfImmediatePrefetchesPerPage + 2);
Domenic Denicola7eaf450e2025-06-17 02:00:046070 EXPECT_EQ(referring_page_metrics->prefetch_successful_count,
Takashi Nakayama978f0a152025-06-17 08:26:256071 kMaxNumberOfImmediatePrefetchesPerPage + 1);
Adithya Srinivasan6054e132023-05-18 17:38:396072}
6073
Takashi Nakayama978f0a152025-06-17 08:26:256074TEST_P(PrefetchServiceLimitsTest, NonImmediatePrefetchEvictedAtLimit) {
Adithya Srinivasan6054e132023-05-18 17:38:396075 const GURL url_1 = GURL("https://example.com/one");
6076 const GURL url_2 = GURL("https://example.com/two");
6077 const GURL url_3 = GURL("https://example.com/three");
6078 const GURL url_4 = GURL("https://example.com/four");
6079
6080 NavigateAndCommit(GURL("https://example.com"));
6081
Domenic Denicola7eaf450e2025-06-17 02:00:046082 // This test is written to assume this specific limit and will need
6083 // modification if it changes.
Takashi Nakayama978f0a152025-06-17 08:26:256084 ASSERT_EQ(kMaxNumberOfNonImmediatePrefetchesPerPage, 2);
Domenic Denicola7eaf450e2025-06-17 02:00:046085
Adithya Srinivasan6054e132023-05-18 17:38:396086 MakePrefetchService(
6087 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
6088 /*num_on_prefetch_likely_calls=*/4));
6089
Adithya Srinivasand476f4f82023-06-20 15:55:136090 base::MockRepeatingCallback<void(const GURL& url)> mock_destruction_callback;
6091 EXPECT_CALL(mock_destruction_callback, Run(url_1)).Times(1);
6092 EXPECT_CALL(mock_destruction_callback, Run(url_2)).Times(1);
Adithya Srinivasand1c68ba2023-05-30 19:54:256093 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh())
Adithya Srinivasand476f4f82023-06-20 15:55:136094 ->SetPrefetchDestructionCallback(mock_destruction_callback.Get());
Adithya Srinivasand1c68ba2023-05-30 19:54:256095
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486096 auto prefetch_1 =
Adithya Srinivasan6054e132023-05-18 17:38:396097 CompletePrefetch(url_1, blink::mojom::SpeculationEagerness::kModerate);
6098 ASSERT_TRUE(prefetch_1);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486099 EXPECT_EQ(prefetch_1.GetPrefetchStatus(),
Adithya Srinivasan6054e132023-05-18 17:38:396100 PrefetchStatus::kPrefetchSuccessful);
6101
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486102 auto prefetch_2 =
Adithya Srinivasan6054e132023-05-18 17:38:396103 CompletePrefetch(url_2, blink::mojom::SpeculationEagerness::kModerate);
6104 ASSERT_TRUE(prefetch_2);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486105 EXPECT_EQ(prefetch_2.GetPrefetchStatus(),
Adithya Srinivasan6054e132023-05-18 17:38:396106 PrefetchStatus::kPrefetchSuccessful);
6107 ASSERT_TRUE(prefetch_1);
6108
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486109 auto prefetch_3 =
Adithya Srinivasan6054e132023-05-18 17:38:396110 CompletePrefetch(url_3, blink::mojom::SpeculationEagerness::kModerate);
6111 ASSERT_TRUE(prefetch_3);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486112 EXPECT_EQ(prefetch_3.GetPrefetchStatus(),
Adithya Srinivasan6054e132023-05-18 17:38:396113 PrefetchStatus::kPrefetchSuccessful);
6114 // Prefetch for |url_1| should have been evicted to allow a prefetch of
6115 // |url_3|.
6116 ASSERT_FALSE(prefetch_1);
6117 ASSERT_TRUE(prefetch_2);
6118
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486119 auto prefetch_4 =
Adithya Srinivasan6054e132023-05-18 17:38:396120 CompletePrefetch(url_4, blink::mojom::SpeculationEagerness::kModerate);
6121 ASSERT_TRUE(prefetch_4);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486122 EXPECT_EQ(prefetch_4.GetPrefetchStatus(),
Adithya Srinivasan6054e132023-05-18 17:38:396123 PrefetchStatus::kPrefetchSuccessful);
6124 // Prefetch for |url_2| should have been evicted to allow a prefetch of
6125 // |url_4|.
6126 ASSERT_FALSE(prefetch_2);
6127 ASSERT_TRUE(prefetch_3);
6128
6129 // The first and second prefetches should have failure reason set to
6130 // 'kPrefetchEvicted'.
6131 {
6132 const auto source_id = ForceLogsUploadAndGetUkmId();
6133 auto actual_attempts = test_ukm_recorder()->GetEntries(
6134 ukm::builders::Preloading_Attempt::kEntryName,
6135 test::kPreloadingAttemptUkmMetrics);
6136 EXPECT_EQ(actual_attempts.size(), 4u);
6137
6138 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> expected_attempts =
6139 {attempt_entry_builder()->BuildEntry(
6140 source_id, PreloadingType::kPrefetch,
6141 PreloadingEligibility::kEligible,
6142 PreloadingHoldbackStatus::kAllowed,
6143 PreloadingTriggeringOutcome::kFailure,
6144 ToPreloadingFailureReason(
Adithya Srinivasanb7f08b42023-12-05 15:49:366145 content::PrefetchStatus::kPrefetchEvictedForNewerPrefetch),
kenossbe8fbf22024-08-14 12:18:216146 /*accurate=*/true,
Adithya Srinivasan6054e132023-05-18 17:38:396147 /*ready_time=*/
Adithya Srinivasan38e0c402023-07-19 16:12:586148 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
6149 blink::mojom::SpeculationEagerness::kModerate),
Adithya Srinivasan6054e132023-05-18 17:38:396150 attempt_entry_builder()->BuildEntry(
6151 source_id, PreloadingType::kPrefetch,
6152 PreloadingEligibility::kEligible,
6153 PreloadingHoldbackStatus::kAllowed,
6154 PreloadingTriggeringOutcome::kFailure,
6155 ToPreloadingFailureReason(
Adithya Srinivasanb7f08b42023-12-05 15:49:366156 content::PrefetchStatus::kPrefetchEvictedForNewerPrefetch),
kenossbe8fbf22024-08-14 12:18:216157 /*accurate=*/true,
Adithya Srinivasan6054e132023-05-18 17:38:396158 /*ready_time=*/
Adithya Srinivasan38e0c402023-07-19 16:12:586159 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
6160 blink::mojom::SpeculationEagerness::kModerate),
Adithya Srinivasan6054e132023-05-18 17:38:396161 attempt_entry_builder()->BuildEntry(
6162 source_id, PreloadingType::kPrefetch,
6163 PreloadingEligibility::kEligible,
6164 PreloadingHoldbackStatus::kAllowed,
6165 PreloadingTriggeringOutcome::kReady,
6166 PreloadingFailureReason::kUnspecified,
kenossbe8fbf22024-08-14 12:18:216167 /*accurate=*/true,
Adithya Srinivasan6054e132023-05-18 17:38:396168 /*ready_time=*/
Adithya Srinivasan38e0c402023-07-19 16:12:586169 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
6170 blink::mojom::SpeculationEagerness::kModerate),
Adithya Srinivasan6054e132023-05-18 17:38:396171 attempt_entry_builder()->BuildEntry(
6172 source_id, PreloadingType::kPrefetch,
6173 PreloadingEligibility::kEligible,
6174 PreloadingHoldbackStatus::kAllowed,
6175 PreloadingTriggeringOutcome::kReady,
6176 PreloadingFailureReason::kUnspecified,
kenossbe8fbf22024-08-14 12:18:216177 /*accurate=*/true,
Adithya Srinivasan6054e132023-05-18 17:38:396178 /*ready_time=*/
Adithya Srinivasan38e0c402023-07-19 16:12:586179 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
6180 blink::mojom::SpeculationEagerness::kModerate)};
Adithya Srinivasan6054e132023-05-18 17:38:396181 EXPECT_THAT(actual_attempts,
6182 testing::UnorderedElementsAreArray(expected_attempts))
6183 << test::ActualVsExpectedUkmEntriesToString(actual_attempts,
6184 expected_attempts);
6185 }
Adithya Srinivasanf393dd02023-05-16 20:26:166186}
6187
Domenic Denicola7eaf450e2025-06-17 02:00:046188TEST_P(PrefetchServiceLimitsTest, PrefetchWithNoCandidateIsNotStarted) {
Adithya Srinivasanb60483492023-06-14 21:22:146189 const GURL url_1 = GURL("https://example.com/one");
6190 const GURL url_2 = GURL("https://example.com/two");
6191 const GURL url_3 = GURL("https://example.com/three");
6192
6193 NavigateAndCommit(GURL("https://example.com"));
6194
6195 MakePrefetchService(
6196 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
6197 /*num_on_prefetch_likely_calls=*/3));
6198
6199 auto candidate_1 = blink::mojom::SpeculationCandidate::New();
6200 candidate_1->url = url_1;
6201 candidate_1->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:256202 candidate_1->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Adithya Srinivasanb60483492023-06-14 21:22:146203 candidate_1->referrer = blink::mojom::Referrer::New();
6204 auto candidate_2 = candidate_1.Clone();
6205 candidate_2->url = url_2;
6206 auto candidate_3 = candidate_1.Clone();
6207 candidate_3->url = url_3;
6208
6209 auto* prefetch_document_manager =
6210 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
6211 ASSERT_TRUE(prefetch_document_manager);
6212
Adithya Srinivasand476f4f82023-06-20 15:55:136213 base::MockRepeatingCallback<void(const GURL& url)> mock_destruction_callback;
6214 EXPECT_CALL(mock_destruction_callback, Run(url_2)).Times(1);
6215 prefetch_document_manager->SetPrefetchDestructionCallback(
6216 mock_destruction_callback.Get());
Adithya Srinivasanb60483492023-06-14 21:22:146217
6218 // Send 3 candidates to PrefetchDocumentManager.
6219 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
6220 candidates.push_back(candidate_1.Clone());
6221 candidates.push_back(candidate_2.Clone());
6222 candidates.push_back(candidate_3.Clone());
kenoss1ce36df592024-12-03 01:04:356223 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546224 task_environment()->RunUntilIdle();
Jeremy Roman4c9524212023-07-27 22:01:056225 VerifyCommonRequestState(url_1);
Adithya Srinivasanb60483492023-06-14 21:22:146226
6227 // Remove |url_2| from the list of candidates while a prefetch for |url_1| is
6228 // in progress.
6229 candidates.clear();
6230 candidates.push_back(candidate_1.Clone());
6231 candidates.push_back(candidate_3.Clone());
kenoss1ce36df592024-12-03 01:04:356232 prefetch_document_manager->ProcessCandidates(candidates);
Adithya Srinivasanb60483492023-06-14 21:22:146233
6234 // Finish prefetch of |url_1|.
6235 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
6236 /*use_prefetch_proxy=*/false,
6237 {{"X-Testing", "Hello World"}}, kHTMLBody);
6238 // PrefetchService skips |url_2| because its candidate was removed, and starts
6239 // prefetching |url_3| instead.
Jeremy Roman4c9524212023-07-27 22:01:056240 VerifyCommonRequestState(url_3);
Adithya Srinivasanb60483492023-06-14 21:22:146241 // Finish prefetch of |url_2|.
6242 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
6243 /*use_prefetch_proxy=*/false,
6244 {{"X-Testing", "Hello World"}}, kHTMLBody);
6245 // There should be no pending prefetch requests.
6246 EXPECT_EQ(RequestCount(), 0);
6247}
6248
Domenic Denicola7eaf450e2025-06-17 02:00:046249TEST_P(PrefetchServiceLimitsTest,
Adithya Srinivasanb60483492023-06-14 21:22:146250 InProgressPrefetchWithNoCandidateIsCancelled) {
6251 const GURL url_1 = GURL("https://example.com/one");
6252 const GURL url_2 = GURL("https://example.com/two");
6253
6254 NavigateAndCommit(GURL("https://example.com"));
6255
6256 MakePrefetchService(
6257 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
6258 /*num_on_prefetch_likely_calls=*/2));
6259
6260 auto candidate_1 = blink::mojom::SpeculationCandidate::New();
6261 candidate_1->url = url_1;
6262 candidate_1->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:256263 candidate_1->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Adithya Srinivasanb60483492023-06-14 21:22:146264 candidate_1->referrer = blink::mojom::Referrer::New();
6265 auto candidate_2 = candidate_1.Clone();
6266 candidate_2->url = url_2;
6267
6268 auto* prefetch_document_manager =
6269 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
6270 ASSERT_TRUE(prefetch_document_manager);
6271
Adithya Srinivasand476f4f82023-06-20 15:55:136272 base::MockRepeatingCallback<void(const GURL& url)> mock_destruction_callback;
6273 EXPECT_CALL(mock_destruction_callback, Run(url_1)).Times(1);
6274 prefetch_document_manager->SetPrefetchDestructionCallback(
6275 mock_destruction_callback.Get());
Adithya Srinivasanb60483492023-06-14 21:22:146276
6277 // Send 2 candidates to PrefetchDocumentManager.
6278 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
6279 candidates.push_back(candidate_1.Clone());
6280 candidates.push_back(candidate_2.Clone());
kenoss1ce36df592024-12-03 01:04:356281 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546282 task_environment()->RunUntilIdle();
Adithya Srinivasanb60483492023-06-14 21:22:146283
6284 // Prefetch for |url_1| should have started.
Jeremy Roman4c9524212023-07-27 22:01:056285 VerifyCommonRequestState(url_1);
Adithya Srinivasanb60483492023-06-14 21:22:146286
6287 // Remove |candidate_1|.
6288 candidates.clear();
6289 candidates.push_back(candidate_2.Clone());
kenoss1ce36df592024-12-03 01:04:356290 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546291 task_environment()->RunUntilIdle();
Adithya Srinivasanb60483492023-06-14 21:22:146292
6293 // The prefetch for |url_1| should be cancelled, and prefetch for |url_2|
6294 // should have started.
6295
6296 EXPECT_EQ(test_url_loader_factory_.pending_requests()->size(), 2u);
6297 // The client for the first request should be disconnected.
6298 EXPECT_FALSE(
6299 test_url_loader_factory_.GetPendingRequest(0)->client.is_connected());
6300 // Clears out first request.
6301 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
6302 /*use_prefetch_proxy=*/false,
6303 {{"X-Testing", "Hello World"}}, kHTMLBody);
Jeremy Roman4c9524212023-07-27 22:01:056304 VerifyCommonRequestState(url_2);
Taiyo Mizuhashi01d86af22024-03-27 14:27:426305 NavigateInitiatedByRenderer(url_1);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:576306 PrefetchServingHandle serving_handle = GetPrefetchToServe(url_1);
6307 EXPECT_FALSE(serving_handle);
Adithya Srinivasanb60483492023-06-14 21:22:146308}
6309
Domenic Denicola7eaf450e2025-06-17 02:00:046310TEST_P(PrefetchServiceLimitsTest, CompletedPrefetchWithNoCandidateIsEvicted) {
Adithya Srinivasanb60483492023-06-14 21:22:146311 const GURL url_1 = GURL("https://example.com/one");
6312 const GURL url_2 = GURL("https://example.com/two");
6313 const GURL url_3 = GURL("https://example.com/three");
6314
6315 NavigateAndCommit(GURL("https://example.com"));
6316
6317 MakePrefetchService(
6318 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
6319 /*num_on_prefetch_likely_calls=*/2));
6320
6321 auto candidate_1 = blink::mojom::SpeculationCandidate::New();
6322 candidate_1->url = url_1;
6323 candidate_1->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:256324 candidate_1->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Adithya Srinivasanb60483492023-06-14 21:22:146325 candidate_1->referrer = blink::mojom::Referrer::New();
6326 auto candidate_2 = candidate_1.Clone();
6327 candidate_2->url = url_2;
6328
6329 auto* prefetch_document_manager =
6330 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
6331 ASSERT_TRUE(prefetch_document_manager);
6332
Adithya Srinivasand476f4f82023-06-20 15:55:136333 base::MockRepeatingCallback<void(const GURL& url)> mock_destruction_callback;
6334 EXPECT_CALL(mock_destruction_callback, Run(url_1)).Times(1);
6335 prefetch_document_manager->SetPrefetchDestructionCallback(
6336 mock_destruction_callback.Get());
Adithya Srinivasanb60483492023-06-14 21:22:146337
6338 // Send 2 candidates to PrefetchDocumentManager.
6339 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
6340 candidates.push_back(candidate_1.Clone());
6341 candidates.push_back(candidate_2.Clone());
kenoss1ce36df592024-12-03 01:04:356342 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546343 task_environment()->RunUntilIdle();
Adithya Srinivasanb60483492023-06-14 21:22:146344
6345 // Complete prefetches for |url_1| and |url_2|.
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486346 auto prefetch_1 = CompleteExistingPrefetch(url_1);
Adithya Srinivasanb60483492023-06-14 21:22:146347 ASSERT_TRUE(prefetch_1);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486348 auto prefetch_2 = CompleteExistingPrefetch(url_2);
Adithya Srinivasanb60483492023-06-14 21:22:146349 ASSERT_TRUE(prefetch_2);
6350
6351 // Remove |candidate_1|.
6352 candidates.clear();
6353 candidates.push_back(candidate_2.Clone());
kenoss1ce36df592024-12-03 01:04:356354 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546355 task_environment()->RunUntilIdle();
Adithya Srinivasanb60483492023-06-14 21:22:146356 // |prefetch_1| should have been removed.
6357 EXPECT_FALSE(prefetch_1);
6358 EXPECT_TRUE(prefetch_2);
6359}
6360
Adithya Srinivasand476f4f82023-06-20 15:55:136361// Test to see if we can re-prefetch a url whose previous prefetch expired.
Domenic Denicola7eaf450e2025-06-17 02:00:046362TEST_P(PrefetchServiceLimitsTest, PrefetchReset) {
Adithya Srinivasand476f4f82023-06-20 15:55:136363 base::test::ScopedFeatureList scoped_feature_list;
kenoss557d4092025-04-01 05:01:156364 // Override `kPrefetchUseContentRefactor`.
Adithya Srinivasand476f4f82023-06-20 15:55:136365 scoped_feature_list.InitWithFeaturesAndParameters(
6366 {{features::kPrefetchUseContentRefactor,
6367 {{"ineligible_decoy_request_probability", "0"},
6368 {"prefetch_container_lifetime_s", "1"}}}},
6369 {});
6370
6371 NavigateAndCommit(GURL("https://example.com"));
6372 MakePrefetchService(
6373 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
6374 /*num_on_prefetch_likely_calls=*/2));
6375
6376 auto* prefetch_document_manager =
6377 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
6378
6379 const auto url = GURL("https://example.com/one");
6380 base::MockRepeatingCallback<void(const GURL& url)> mock_destruction_callback;
6381 EXPECT_CALL(mock_destruction_callback, Run(url)).Times(1);
6382 prefetch_document_manager->SetPrefetchDestructionCallback(
6383 mock_destruction_callback.Get());
6384
6385 auto candidate = blink::mojom::SpeculationCandidate::New();
6386 candidate->url = url;
6387 candidate->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:256388 candidate->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Adithya Srinivasand476f4f82023-06-20 15:55:136389 candidate->referrer = blink::mojom::Referrer::New();
6390
6391 // Start and complete prefetch of |url|.
6392 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
6393 candidates.push_back(candidate.Clone());
kenoss1ce36df592024-12-03 01:04:356394 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486395 auto prefetch = CompleteExistingPrefetch(url);
Adithya Srinivasand476f4f82023-06-20 15:55:136396 ASSERT_TRUE(prefetch);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:486397 EXPECT_EQ(prefetch.GetPrefetchStatus(), PrefetchStatus::kPrefetchSuccessful);
Adithya Srinivasand476f4f82023-06-20 15:55:136398
6399 // Fast forward by a second and expire |prefetch|.
6400 task_environment()->FastForwardBy(base::Seconds(1));
6401 EXPECT_FALSE(prefetch);
6402
6403 // Try reprefetching |url|.
Alison Gale770f3fc2024-04-27 00:39:586404 // TODO(crbug.com/40064525): Ideally this prefetch would be requeued
Adithya Srinivasand476f4f82023-06-20 15:55:136405 // automatically.
6406 candidates.clear();
6407 candidates.push_back(candidate.Clone());
kenoss1ce36df592024-12-03 01:04:356408 prefetch_document_manager->ProcessCandidates(candidates);
Adithya Srinivasand476f4f82023-06-20 15:55:136409 // Prefetch for |url| should have started again.
Jeremy Roman4c9524212023-07-27 22:01:056410 VerifyCommonRequestState(url);
Adithya Srinivasan006b3652023-12-14 14:52:126411
6412 {
6413 const auto source_id = ForceLogsUploadAndGetUkmId();
6414 auto actual_attempts = test_ukm_recorder()->GetEntries(
6415 ukm::builders::Preloading_Attempt::kEntryName,
6416 test::kPreloadingAttemptUkmMetrics);
6417 EXPECT_EQ(actual_attempts.size(), 2u);
6418 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> expected_attempts =
6419 {attempt_entry_builder()->BuildEntry(
6420 source_id, PreloadingType::kPrefetch,
6421 PreloadingEligibility::kEligible,
6422 PreloadingHoldbackStatus::kAllowed,
6423 PreloadingTriggeringOutcome::kFailure,
6424 ToPreloadingFailureReason(PrefetchStatus::kPrefetchIsStale),
kenossbe8fbf22024-08-14 12:18:216425 /*accurate=*/true,
Adithya Srinivasan006b3652023-12-14 14:52:126426 /*ready_time=*/
6427 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
Takashi Nakayama978f0a152025-06-17 08:26:256428 blink::mojom::SpeculationEagerness::kImmediate),
Adithya Srinivasan006b3652023-12-14 14:52:126429 attempt_entry_builder()->BuildEntry(
6430 source_id, PreloadingType::kPrefetch,
6431 PreloadingEligibility::kEligible,
6432 PreloadingHoldbackStatus::kAllowed,
6433 PreloadingTriggeringOutcome::kRunning,
6434 PreloadingFailureReason::kUnspecified,
6435 /*accurate=*/false,
6436 /*ready_time=*/std::nullopt,
Takashi Nakayama978f0a152025-06-17 08:26:256437 blink::mojom::SpeculationEagerness::kImmediate)};
Adithya Srinivasan006b3652023-12-14 14:52:126438 EXPECT_THAT(actual_attempts,
6439 testing::UnorderedElementsAreArray(expected_attempts))
6440 << test::ActualVsExpectedUkmEntriesToString(actual_attempts,
6441 expected_attempts);
6442 }
Adithya Srinivasand476f4f82023-06-20 15:55:136443}
6444
Domenic Denicola7eaf450e2025-06-17 02:00:046445TEST_P(PrefetchServiceLimitsTest, NextPrefetchQueuedImmediatelyAfterReset) {
Adithya Srinivasand5e37cb2023-07-05 14:45:236446 base::test::ScopedFeatureList scoped_feature_list;
kenoss557d4092025-04-01 05:01:156447 // Override `kPrefetchUseContentRefactor`.
Adithya Srinivasand5e37cb2023-07-05 14:45:236448 scoped_feature_list.InitWithFeaturesAndParameters(
Domenic Denicola7eaf450e2025-06-17 02:00:046449 {
6450 {features::kPrefetchUseContentRefactor,
6451 {{"ineligible_decoy_request_probability", "0"},
6452 {"prefetch_container_lifetime_s", "1"}}},
6453 },
Adithya Srinivasand5e37cb2023-07-05 14:45:236454 {});
6455
6456 NavigateAndCommit(GURL("https://example.com"));
Domenic Denicola7eaf450e2025-06-17 02:00:046457
Takashi Nakayama978f0a152025-06-17 08:26:256458 MakePrefetchService(std::make_unique<
6459 testing::NiceMock<MockPrefetchServiceDelegate>>(
6460 /*num_on_prefetch_likely_calls=*/kMaxNumberOfImmediatePrefetchesPerPage +
6461 1));
Adithya Srinivasand5e37cb2023-07-05 14:45:236462
6463 auto* prefetch_document_manager =
6464 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
Domenic Denicola7eaf450e2025-06-17 02:00:046465
Takashi Nakayama978f0a152025-06-17 08:26:256466 // Create kMaxNumberOfImmediatePrefetchesPerPage + 1 URLs to exceed the limit
Domenic Denicola7eaf450e2025-06-17 02:00:046467 std::vector<GURL> urls;
Takashi Nakayama978f0a152025-06-17 08:26:256468 for (size_t i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage + 1; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046469 urls.emplace_back("https://example.com/" + base::NumberToString(i));
6470 }
Adithya Srinivasand5e37cb2023-07-05 14:45:236471
6472 base::MockRepeatingCallback<void(const GURL& url)> mock_destruction_callback;
Domenic Denicola7eaf450e2025-06-17 02:00:046473 // All URLs but the last will expire and be destroyed
Takashi Nakayama978f0a152025-06-17 08:26:256474 for (size_t i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046475 EXPECT_CALL(mock_destruction_callback, Run(urls[i])).Times(1);
6476 }
Adithya Srinivasand5e37cb2023-07-05 14:45:236477 prefetch_document_manager->SetPrefetchDestructionCallback(
6478 mock_destruction_callback.Get());
6479
Domenic Denicola7eaf450e2025-06-17 02:00:046480 auto base_candidate = blink::mojom::SpeculationCandidate::New();
6481 base_candidate->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:256482 base_candidate->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Domenic Denicola7eaf450e2025-06-17 02:00:046483 base_candidate->referrer = blink::mojom::Referrer::New();
Adithya Srinivasand5e37cb2023-07-05 14:45:236484
Domenic Denicola7eaf450e2025-06-17 02:00:046485 // Add all candidates (one more than the limit)
Adithya Srinivasand5e37cb2023-07-05 14:45:236486 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
Domenic Denicola7eaf450e2025-06-17 02:00:046487 for (const auto& url : urls) {
6488 auto candidate = base_candidate.Clone();
6489 candidate->url = url;
6490 candidates.push_back(std::move(candidate));
6491 }
kenoss1ce36df592024-12-03 01:04:356492 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546493 task_environment()->RunUntilIdle();
Adithya Srinivasand5e37cb2023-07-05 14:45:236494
Domenic Denicola7eaf450e2025-06-17 02:00:046495 // Complete prefetches up to the limit
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:576496 std::vector<PrefetchServingHandle> prefetches;
Takashi Nakayama978f0a152025-06-17 08:26:256497 for (size_t i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046498 auto prefetch = CompleteExistingPrefetch(urls[i]);
6499 ASSERT_TRUE(prefetch);
6500 EXPECT_EQ(prefetch.GetPrefetchStatus(),
6501 PrefetchStatus::kPrefetchSuccessful);
6502 prefetches.push_back(std::move(prefetch));
6503 }
Adithya Srinivasand5e37cb2023-07-05 14:45:236504
Domenic Denicola7eaf450e2025-06-17 02:00:046505 // The last URL should not be queued because we are at the limit
Adithya Srinivasand5e37cb2023-07-05 14:45:236506 EXPECT_EQ(RequestCount(), 0);
6507
Takashi Nakayama978f0a152025-06-17 08:26:256508 // Fast forward by a second to expire the
6509 // kMaxNumberOfImmediatePrefetchesPerPage prefetches
Adithya Srinivasand5e37cb2023-07-05 14:45:236510 task_environment()->FastForwardBy(base::Seconds(1));
Domenic Denicola7eaf450e2025-06-17 02:00:046511 for (auto& prefetch : prefetches) {
6512 EXPECT_FALSE(prefetch);
6513 }
Adithya Srinivasand5e37cb2023-07-05 14:45:236514
Domenic Denicola7eaf450e2025-06-17 02:00:046515 // The last URL should now be queued and prefetched
Takashi Nakayama978f0a152025-06-17 08:26:256516 VerifyCommonRequestState(urls[kMaxNumberOfImmediatePrefetchesPerPage]);
Adithya Srinivasand5e37cb2023-07-05 14:45:236517}
6518
Taiyo Mizuhashi4e624a22025-05-15 11:28:546519// Tests that the prefetch queue is not stuck when resetting the running
6520// prefetch during waiting its response. Regression test for
6521// crbug.com/400233773.
6522TEST_P(PrefetchServiceTest, PrefetchQueueNotStuckWhenResettingRunningPrefetch) {
Taiyo Mizuhashi9aa95a92025-03-05 19:35:046523 NavigateAndCommit(GURL("https://example.com"));
6524 MakePrefetchService(
6525 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
6526 /*num_on_prefetch_likely_calls=*/0));
Taiyo Mizuhashi4e624a22025-05-15 11:28:546527 PrefetchService* prefetch_service =
6528 BrowserContextImpl::From(browser_context())->GetPrefetchService();
Taiyo Mizuhashi9aa95a92025-03-05 19:35:046529
6530 const auto url_1 = GURL("https://example.com/one");
6531 const auto url_2 = GURL("https://example.com/two");
Taiyo Mizuhashi9aa95a92025-03-05 19:35:046532 auto handle_1 =
6533 MakePrefetchFromBrowserContext(url_1, std::nullopt, {}, nullptr);
6534 auto handle_2 =
6535 MakePrefetchFromBrowserContext(url_2, std::nullopt, {}, nullptr);
Taiyo Mizuhashi4e624a22025-05-15 11:28:546536 task_environment()->RunUntilIdle();
Taiyo Mizuhashi9aa95a92025-03-05 19:35:046537
Taiyo Mizuhashi9aa95a92025-03-05 19:35:046538 base::WeakPtr<PrefetchContainer> prefetch_container1, prefetch_container2;
6539 std::tie(std::ignore, prefetch_container1) =
6540 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
6541 PrefetchContainer::Key(std::nullopt, url_1))[0];
6542 std::tie(std::ignore, prefetch_container2) =
6543 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
6544 PrefetchContainer::Key(std::nullopt, url_2))[0];
6545
6546 ASSERT_EQ(prefetch_container1->GetLoadState(),
6547 PrefetchContainer::LoadState::kStarted);
6548 ASSERT_EQ(prefetch_container2->GetLoadState(),
6549 PrefetchContainer::LoadState::kEligible);
6550
6551 // Reset the first prefetch during waiting its response. Note that it will
6552 // eventually destruct the loader.
6553 handle_1.reset();
6554 task_environment()->RunUntilIdle();
Taiyo Mizuhashi4e624a22025-05-15 11:28:546555
Taiyo Mizuhashi9aa95a92025-03-05 19:35:046556 EXPECT_FALSE(prefetch_container1);
Taiyo Mizuhashi4e624a22025-05-15 11:28:546557 // https://crbug.com/400233773 is fixed.
Taiyo Mizuhashi9aa95a92025-03-05 19:35:046558 EXPECT_EQ(prefetch_container2->GetLoadState(),
6559 PrefetchContainer::LoadState::kStarted);
6560}
6561
Domenic Denicola7eaf450e2025-06-17 02:00:046562TEST_P(PrefetchServiceLimitsTest, PrefetchFailsAndIsReset) {
Adithya Srinivasan4d3dad02024-10-17 18:06:596563 base::HistogramTester histogram_tester;
Adithya Srinivasan006b3652023-12-14 14:52:126564 base::test::ScopedFeatureList scoped_feature_list;
kenoss557d4092025-04-01 05:01:156565 // Override `kPrefetchUseContentRefactor`.
Adithya Srinivasan006b3652023-12-14 14:52:126566 scoped_feature_list.InitWithFeaturesAndParameters(
6567 {{features::kPrefetchUseContentRefactor,
6568 {{"ineligible_decoy_request_probability", "0"},
6569 {"prefetch_container_lifetime_s", "1"}}}},
6570 {});
6571
6572 NavigateAndCommit(GURL("https://example.com"));
6573 MakePrefetchService(
6574 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
6575 /*num_on_prefetch_likely_calls=*/1));
6576
6577 auto* prefetch_document_manager =
6578 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
6579
6580 const auto url = GURL("https://example.com/one");
6581 base::MockRepeatingCallback<void(const GURL& url)> mock_destruction_callback;
6582 EXPECT_CALL(mock_destruction_callback, Run(url)).Times(1);
6583 prefetch_document_manager->SetPrefetchDestructionCallback(
6584 mock_destruction_callback.Get());
6585
6586 auto candidate = blink::mojom::SpeculationCandidate::New();
6587 candidate->url = url;
6588 candidate->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:256589 candidate->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Adithya Srinivasan006b3652023-12-14 14:52:126590 candidate->referrer = blink::mojom::Referrer::New();
6591
6592 // Start prefetch of |url|.
6593 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
6594 candidates.push_back(candidate.Clone());
kenoss1ce36df592024-12-03 01:04:356595 prefetch_document_manager->ProcessCandidates(candidates);
Adithya Srinivasan006b3652023-12-14 14:52:126596 base::RunLoop().RunUntilIdle();
6597 VerifyCommonRequestState(url);
6598 MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED, kHTMLMimeType,
6599 /*use_prefetch_proxy=*/true,
6600 {{"X-Testing", "Hello World"}}, kHTMLBody);
6601
6602 // Fast forward by a second and expire existing PrefetchContainer.
6603 task_environment()->FastForwardBy(base::Seconds(1));
6604 // The failure reason recorded (failed due to net error) should not be
6605 // overwritten when the prefetch is timed out (marked as stale).
6606 ExpectCorrectUkmLogs({.outcome = PreloadingTriggeringOutcome::kFailure,
6607 .failure = ToPreloadingFailureReason(
6608 PrefetchStatus::kPrefetchFailedNetError)});
Adithya Srinivasan4d3dad02024-10-17 18:06:596609 histogram_tester.ExpectUniqueSample("Preloading.Prefetch.PrefetchStatus",
6610 PrefetchStatus::kPrefetchFailedNetError,
6611 1);
Adithya Srinivasan006b3652023-12-14 14:52:126612}
6613
Takashi Nakayama978f0a152025-06-17 08:26:256614TEST_P(PrefetchServiceLimitsTest, ImmediatePrefetchLimitIsDynamic) {
Domenic Denicola7eaf450e2025-06-17 02:00:046615 // The test only makes sense with limits >=2
Takashi Nakayama978f0a152025-06-17 08:26:256616 ASSERT_GE(kMaxNumberOfImmediatePrefetchesPerPage, 2u);
Domenic Denicola7eaf450e2025-06-17 02:00:046617
6618 std::vector<GURL> urls;
Takashi Nakayama978f0a152025-06-17 08:26:256619 for (size_t i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage + 1; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046620 urls.emplace_back("https://example.com/" + base::NumberToString(i));
6621 }
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466622
6623 NavigateAndCommit(GURL("https://example.com"));
6624
Takashi Nakayama978f0a152025-06-17 08:26:256625 MakePrefetchService(std::make_unique<
6626 testing::NiceMock<MockPrefetchServiceDelegate>>(
6627 /*num_on_prefetch_likely_calls=*/kMaxNumberOfImmediatePrefetchesPerPage +
6628 2));
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466629
6630 auto* prefetch_document_manager =
6631 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
6632 ASSERT_TRUE(prefetch_document_manager);
6633
Domenic Denicola7eaf450e2025-06-17 02:00:046634 base::MockRepeatingCallback<void(const GURL&)> destruction_cb;
6635 EXPECT_CALL(destruction_cb, Run(urls[0])).Times(1);
6636 EXPECT_CALL(destruction_cb, Run(urls[1])).Times(1);
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466637 prefetch_document_manager->SetPrefetchDestructionCallback(
Domenic Denicola7eaf450e2025-06-17 02:00:046638 destruction_cb.Get());
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466639
Domenic Denicola7eaf450e2025-06-17 02:00:046640 auto base_candidate = blink::mojom::SpeculationCandidate::New();
6641 base_candidate->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:256642 base_candidate->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Domenic Denicola7eaf450e2025-06-17 02:00:046643 base_candidate->referrer = blink::mojom::Referrer::New();
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466644
Domenic Denicola7eaf450e2025-06-17 02:00:046645 // Send candidates up to the limit
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466646 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
Takashi Nakayama978f0a152025-06-17 08:26:256647 for (size_t i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046648 auto candidate = base_candidate.Clone();
6649 candidate->url = urls[i];
6650 candidates.push_back(std::move(candidate));
6651 }
kenoss1ce36df592024-12-03 01:04:356652 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546653 task_environment()->RunUntilIdle();
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466654
Domenic Denicola7eaf450e2025-06-17 02:00:046655 // Complete all prefetches up to the limit
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:576656 std::vector<PrefetchServingHandle> prefetches;
Takashi Nakayama978f0a152025-06-17 08:26:256657 for (size_t i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046658 auto prefetch = CompleteExistingPrefetch(urls[i]);
6659 ASSERT_TRUE(prefetch);
6660 EXPECT_EQ(prefetch.GetPrefetchStatus(),
6661 PrefetchStatus::kPrefetchSuccessful);
6662 prefetches.push_back(std::move(prefetch));
6663 }
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466664
Domenic Denicola7eaf450e2025-06-17 02:00:046665 // Process candidates for urls[1] through
Takashi Nakayama978f0a152025-06-17 08:26:256666 // urls[kMaxNumberOfImmediatePrefetchesPerPage]
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466667 candidates.clear();
Takashi Nakayama978f0a152025-06-17 08:26:256668 for (size_t i = 1; i < kMaxNumberOfImmediatePrefetchesPerPage + 1; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046669 auto candidate = base_candidate.Clone();
6670 candidate->url = urls[i];
6671 candidates.push_back(std::move(candidate));
6672 }
kenoss1ce36df592024-12-03 01:04:356673 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546674 task_environment()->RunUntilIdle();
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466675
Domenic Denicola7eaf450e2025-06-17 02:00:046676 // The new prefetch should succeed, and the 0th should be evicted
6677 auto prefetch_extra =
Takashi Nakayama978f0a152025-06-17 08:26:256678 CompleteExistingPrefetch(urls[kMaxNumberOfImmediatePrefetchesPerPage]);
Domenic Denicola7eaf450e2025-06-17 02:00:046679 ASSERT_TRUE(prefetch_extra);
6680 EXPECT_EQ(prefetch_extra.GetPrefetchStatus(),
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466681 PrefetchStatus::kPrefetchSuccessful);
Domenic Denicola7eaf450e2025-06-17 02:00:046682 EXPECT_FALSE(prefetches[0]); // 0th prefetch should be evicted
Takashi Nakayama978f0a152025-06-17 08:26:256683 for (size_t i = 1; i < kMaxNumberOfImmediatePrefetchesPerPage; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046684 EXPECT_TRUE(prefetches[i]); // Others should still be valid
6685 }
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466686
Domenic Denicola7eaf450e2025-06-17 02:00:046687 // Now process candidates for urls[0] through
Takashi Nakayama978f0a152025-06-17 08:26:256688 // urls[kMaxNumberOfImmediatePrefetchesPerPage] (which will exceed the limit)
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466689 candidates.clear();
Takashi Nakayama978f0a152025-06-17 08:26:256690 for (size_t i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage + 1; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046691 auto candidate = base_candidate.Clone();
6692 candidate->url = urls[i];
6693 candidates.push_back(std::move(candidate));
6694 }
kenoss1ce36df592024-12-03 01:04:356695 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546696 task_environment()->RunUntilIdle();
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466697
Domenic Denicola7eaf450e2025-06-17 02:00:046698 // Should not reprefetch urls[0] because we are at the limit
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466699 EXPECT_EQ(RequestCount(), 0);
6700
Domenic Denicola7eaf450e2025-06-17 02:00:046701 // Now process candidates for urls[0] plus urls[2] through
Takashi Nakayama978f0a152025-06-17 08:26:256702 // urls[kMaxNumberOfImmediatePrefetchesPerPage] (back under the limit)
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466703 candidates.clear();
Takashi Nakayama978f0a152025-06-17 08:26:256704 for (size_t i = 0; i < kMaxNumberOfImmediatePrefetchesPerPage + 1; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046705 if (i != 1) {
6706 auto candidate = base_candidate.Clone();
6707 candidate->url = urls[i];
6708 candidates.push_back(std::move(candidate));
6709 }
6710 }
kenoss1ce36df592024-12-03 01:04:356711 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546712 task_environment()->RunUntilIdle();
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466713
Domenic Denicola7eaf450e2025-06-17 02:00:046714 // Now urls[0] should be prefetched successfully, and urls[1] evicted
6715 auto prefetch_0_new = CompleteExistingPrefetch(urls[0]);
6716 ASSERT_TRUE(prefetch_0_new);
6717 EXPECT_EQ(prefetch_0_new.GetPrefetchStatus(),
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466718 PrefetchStatus::kPrefetchSuccessful);
Domenic Denicola7eaf450e2025-06-17 02:00:046719 EXPECT_FALSE(prefetches[1]);
Takashi Nakayama978f0a152025-06-17 08:26:256720 for (size_t i = 2; i < kMaxNumberOfImmediatePrefetchesPerPage; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046721 EXPECT_TRUE(prefetches[i]); // Others should still be valid
6722 }
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466723
Domenic Denicola7eaf450e2025-06-17 02:00:046724 // Verify UKM entries
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466725 {
6726 const auto source_id = ForceLogsUploadAndGetUkmId();
6727 auto actual_attempts = test_ukm_recorder()->GetEntries(
6728 ukm::builders::Preloading_Attempt::kEntryName,
6729 test::kPreloadingAttemptUkmMetrics);
Takashi Nakayama978f0a152025-06-17 08:26:256730 EXPECT_EQ(actual_attempts.size(),
6731 kMaxNumberOfImmediatePrefetchesPerPage + 2);
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466732
Domenic Denicola7eaf450e2025-06-17 02:00:046733 std::vector<ukm::TestUkmRecorder::HumanReadableUkmEntry> expected_attempts;
6734
6735 // urls[0], attempt #1 (evicted)
6736 expected_attempts.push_back(attempt_entry_builder()->BuildEntry(
6737 source_id, PreloadingType::kPrefetch, PreloadingEligibility::kEligible,
6738 PreloadingHoldbackStatus::kAllowed,
6739 PreloadingTriggeringOutcome::kFailure,
6740 ToPreloadingFailureReason(
6741 content::PrefetchStatus::kPrefetchEvictedAfterCandidateRemoved),
6742 /*accurate=*/true,
6743 /*ready_time=*/
6744 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
Takashi Nakayama978f0a152025-06-17 08:26:256745 blink::mojom::SpeculationEagerness::kImmediate));
Domenic Denicola7eaf450e2025-06-17 02:00:046746
6747 // urls[1] (evicted)
6748 expected_attempts.push_back(attempt_entry_builder()->BuildEntry(
6749 source_id, PreloadingType::kPrefetch, PreloadingEligibility::kEligible,
6750 PreloadingHoldbackStatus::kAllowed,
6751 PreloadingTriggeringOutcome::kFailure,
6752 ToPreloadingFailureReason(
6753 content::PrefetchStatus::kPrefetchEvictedAfterCandidateRemoved),
6754 /*accurate=*/true,
6755 /*ready_time=*/
6756 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
Takashi Nakayama978f0a152025-06-17 08:26:256757 blink::mojom::SpeculationEagerness::kImmediate));
Domenic Denicola7eaf450e2025-06-17 02:00:046758
Takashi Nakayama978f0a152025-06-17 08:26:256759 // urls[2] through urls[kMaxNumberOfImmediatePrefetchesPerPage-1] (ready)
6760 for (size_t i = 2; i < kMaxNumberOfImmediatePrefetchesPerPage; ++i) {
Domenic Denicola7eaf450e2025-06-17 02:00:046761 expected_attempts.push_back(attempt_entry_builder()->BuildEntry(
6762 source_id, PreloadingType::kPrefetch,
6763 PreloadingEligibility::kEligible, PreloadingHoldbackStatus::kAllowed,
6764 PreloadingTriggeringOutcome::kReady,
6765 PreloadingFailureReason::kUnspecified,
6766 /*accurate=*/true,
6767 /*ready_time=*/
6768 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
Takashi Nakayama978f0a152025-06-17 08:26:256769 blink::mojom::SpeculationEagerness::kImmediate));
Domenic Denicola7eaf450e2025-06-17 02:00:046770 }
6771
Takashi Nakayama978f0a152025-06-17 08:26:256772 // urls[kMaxNumberOfImmediatePrefetchesPerPage] (ready)
Domenic Denicola7eaf450e2025-06-17 02:00:046773 expected_attempts.push_back(attempt_entry_builder()->BuildEntry(
6774 source_id, PreloadingType::kPrefetch, PreloadingEligibility::kEligible,
6775 PreloadingHoldbackStatus::kAllowed, PreloadingTriggeringOutcome::kReady,
6776 PreloadingFailureReason::kUnspecified,
6777 /*accurate=*/true,
6778 /*ready_time=*/
6779 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
Takashi Nakayama978f0a152025-06-17 08:26:256780 blink::mojom::SpeculationEagerness::kImmediate));
Domenic Denicola7eaf450e2025-06-17 02:00:046781
6782 // urls[0], attempt #2 (ready)
6783 expected_attempts.push_back(attempt_entry_builder()->BuildEntry(
6784 source_id, PreloadingType::kPrefetch, PreloadingEligibility::kEligible,
6785 PreloadingHoldbackStatus::kAllowed, PreloadingTriggeringOutcome::kReady,
6786 PreloadingFailureReason::kUnspecified,
6787 /*accurate=*/true,
6788 /*ready_time=*/
6789 base::ScopedMockElapsedTimersForTest::kMockElapsedTime,
Takashi Nakayama978f0a152025-06-17 08:26:256790 blink::mojom::SpeculationEagerness::kImmediate));
Domenic Denicola7eaf450e2025-06-17 02:00:046791
Adithya Srinivasan6bdb9bcbf2023-06-23 21:28:466792 EXPECT_THAT(actual_attempts,
6793 testing::UnorderedElementsAreArray(expected_attempts))
6794 << test::ActualVsExpectedUkmEntriesToString(actual_attempts,
6795 expected_attempts);
6796 }
6797}
6798
Domenic Denicola7eaf450e2025-06-17 02:00:046799TEST_P(PrefetchServiceLimitsTest, RemoveCandidateForFailedPrefetch) {
Adithya Srinivasanbedbca612023-08-31 21:51:386800 const GURL url = GURL("https://example.com/one");
6801
6802 NavigateAndCommit(GURL("https://example.com"));
6803
6804 MakePrefetchService(
6805 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
6806 /*num_on_prefetch_likely_calls=*/1));
6807
6808 auto candidate = blink::mojom::SpeculationCandidate::New();
6809 candidate->url = url;
6810 candidate->action = blink::mojom::SpeculationAction::kPrefetch;
Takashi Nakayama978f0a152025-06-17 08:26:256811 candidate->eagerness = blink::mojom::SpeculationEagerness::kImmediate;
Adithya Srinivasanbedbca612023-08-31 21:51:386812 candidate->referrer = blink::mojom::Referrer::New();
6813
6814 auto* prefetch_document_manager =
6815 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
6816 ASSERT_TRUE(prefetch_document_manager);
6817
6818 base::MockRepeatingCallback<void(const GURL& url)> mock_destruction_callback;
6819 EXPECT_CALL(mock_destruction_callback, Run(url)).Times(1);
6820 prefetch_document_manager->SetPrefetchDestructionCallback(
6821 mock_destruction_callback.Get());
6822
6823 // Send canadidate to PrefetchDocumentManager.
6824 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
6825 candidates.push_back(candidate.Clone());
kenoss1ce36df592024-12-03 01:04:356826 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546827 task_environment()->RunUntilIdle();
Adithya Srinivasanbedbca612023-08-31 21:51:386828
6829 // Prefetch for |url| should have started.
6830 VerifyCommonRequestState(url);
6831 // Send error response for prefetch of |url|
6832 MakeResponseAndWait(net::HTTP_OK, net::ERR_FAILED, kHTMLMimeType,
6833 /*use_prefetch_proxy=*/false,
6834 {{"X-Testing", "Hello World"}}, kHTMLBody);
6835
6836 // Remove |candidate|.
6837 candidates.clear();
kenoss1ce36df592024-12-03 01:04:356838 prefetch_document_manager->ProcessCandidates(candidates);
Hiroshige Hayashizakib6a84d992023-10-02 17:57:546839 task_environment()->RunUntilIdle();
Adithya Srinivasanbedbca612023-08-31 21:51:386840
6841 ExpectCorrectUkmLogs({.outcome = PreloadingTriggeringOutcome::kFailure,
6842 .failure = ToPreloadingFailureReason(
6843 PrefetchStatus::kPrefetchFailedNetError)});
6844}
6845
Jeremy Roman586b5ec2024-02-13 15:14:406846blink::UserAgentMetadata GetFakeUserAgentMetadata() {
6847 blink::UserAgentMetadata metadata;
6848 metadata.brand_version_list.emplace_back("fake", "42");
6849 metadata.brand_full_version_list.emplace_back("fake", "42.0.1701.99");
6850 metadata.full_version = "42.0.1701.99";
6851 return metadata;
6852}
6853
kenossbd9a3fc2025-03-28 05:15:576854class PrefetchServiceClientHintsTest
6855 : public PrefetchServiceTestBase,
6856 public WithPrefetchServiceRearchParam,
6857 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
6858 public:
6859 PrefetchServiceClientHintsTest()
6860 : WithPrefetchServiceRearchParam(GetParam()) {}
6861
6862 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:156863 InitBaseParams();
kenossbd9a3fc2025-03-28 05:15:576864 InitRearchFeatures();
6865 }
6866
Jeremy Roman586b5ec2024-02-13 15:14:406867 protected:
6868 std::unique_ptr<BrowserContext> CreateBrowserContext() override {
kenoss972e8682024-09-05 00:20:146869 auto browser_context = PrefetchServiceTestBase::CreateBrowserContext();
Jeremy Roman586b5ec2024-02-13 15:14:406870 TestBrowserContext::FromBrowserContext(browser_context.get())
6871 ->SetClientHintsControllerDelegate(&client_hints_controller_delegate_);
6872 return browser_context;
6873 }
6874
6875 MockClientHintsControllerDelegate& client_hints_controller_delegate() {
6876 return client_hints_controller_delegate_;
6877 }
6878
6879 private:
6880 MockClientHintsControllerDelegate client_hints_controller_delegate_{
6881 GetFakeUserAgentMetadata()};
6882};
6883
kenossbd9a3fc2025-03-28 05:15:576884INSTANTIATE_TEST_SUITE_P(
6885 ParametrizedTests,
6886 PrefetchServiceClientHintsTest,
6887 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
6888
6889TEST_P(PrefetchServiceClientHintsTest, NoClientHintsWhenDisabled) {
Jeremy Roman586b5ec2024-02-13 15:14:406890 base::test::ScopedFeatureList disable_prefetch_ch;
6891 disable_prefetch_ch.InitAndDisableFeature(features::kPrefetchClientHints);
6892
6893 MakePrefetchService(
6894 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
6895 NavigateAndCommit(GURL("https://example.com/"));
6896
6897 MakePrefetchOnMainFrame(
6898 GURL("https://example.com/two"),
6899 PrefetchType(PreloadingTriggerType::kSpeculationRule,
6900 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:256901 blink::mojom::SpeculationEagerness::kImmediate),
Jeremy Roman586b5ec2024-02-13 15:14:406902 blink::mojom::Referrer(GURL("https://example.com/"),
6903 network::mojom::ReferrerPolicy::kStrictOrigin));
6904 task_environment()->RunUntilIdle();
6905
6906 auto* pending = test_url_loader_factory_.GetPendingRequest(0);
6907 ASSERT_TRUE(pending);
6908 EXPECT_FALSE(pending->request.headers.HasHeader("Sec-CH-UA"));
6909}
6910
kenossbd9a3fc2025-03-28 05:15:576911TEST_P(PrefetchServiceClientHintsTest, LowEntropyClientHints) {
Jeremy Roman586b5ec2024-02-13 15:14:406912 base::test::ScopedFeatureList enable_prefetch_ch;
6913 enable_prefetch_ch.InitAndEnableFeature(features::kPrefetchClientHints);
6914
6915 MakePrefetchService(
6916 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
6917 NavigateAndCommit(GURL("https://example.com/"));
6918
6919 MakePrefetchOnMainFrame(
6920 GURL("https://example.com/two"),
6921 PrefetchType(PreloadingTriggerType::kSpeculationRule,
6922 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:256923 blink::mojom::SpeculationEagerness::kImmediate),
Jeremy Roman586b5ec2024-02-13 15:14:406924 blink::mojom::Referrer(GURL("https://example.com/"),
6925 network::mojom::ReferrerPolicy::kStrictOrigin));
6926 task_environment()->RunUntilIdle();
6927
6928 auto* pending = test_url_loader_factory_.GetPendingRequest(0);
6929 ASSERT_TRUE(pending);
6930 EXPECT_TRUE(pending->request.headers.HasHeader("Sec-CH-UA"));
6931}
6932
kenossbd9a3fc2025-03-28 05:15:576933TEST_P(PrefetchServiceClientHintsTest, HighEntropyClientHints) {
Jeremy Roman586b5ec2024-02-13 15:14:406934 base::test::ScopedFeatureList enable_prefetch_ch;
6935 enable_prefetch_ch.InitAndEnableFeature(features::kPrefetchClientHints);
6936
6937 MakePrefetchService(
6938 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
6939 NavigateAndCommit(GURL("https://example.com/"));
6940 client_hints_controller_delegate().SetMostRecentMainFrameViewportSize(
6941 gfx::Size(800, 600));
6942 client_hints_controller_delegate().PersistClientHints(
6943 url::Origin::Create(GURL("https://example.com/")),
6944 /*parent_rfh=*/nullptr,
6945 {network::mojom::WebClientHintsType::kViewportWidth});
6946
6947 MakePrefetchOnMainFrame(
6948 GURL("https://example.com/two"),
6949 PrefetchType(PreloadingTriggerType::kSpeculationRule,
6950 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:256951 blink::mojom::SpeculationEagerness::kImmediate),
Jeremy Roman586b5ec2024-02-13 15:14:406952 blink::mojom::Referrer(GURL("https://example.com/"),
6953 network::mojom::ReferrerPolicy::kStrictOrigin));
6954 task_environment()->RunUntilIdle();
6955
6956 auto* pending = test_url_loader_factory_.GetPendingRequest(0);
6957 ASSERT_TRUE(pending);
6958 EXPECT_TRUE(pending->request.headers.HasHeader("Sec-CH-UA"));
Chris Fredricksond923c682024-07-30 18:19:346959 std::optional<std::string> viewport_width =
6960 pending->request.headers.GetHeader("Sec-CH-Viewport-Width");
6961 EXPECT_TRUE(viewport_width.has_value());
Jeremy Roman586b5ec2024-02-13 15:14:406962
6963 // Even though we hinted it above, if the actual RenderWidgetHostView reports
6964 // its size that gets used above. So accept any positive integer. (This
6965 // notably affects the results on Android.)
6966 int viewport_width_int;
Chris Fredricksond923c682024-07-30 18:19:346967 EXPECT_TRUE(base::StringToInt(*viewport_width, &viewport_width_int));
Jeremy Roman586b5ec2024-02-13 15:14:406968 EXPECT_GT(viewport_width_int, 0);
6969}
6970
kenossbd9a3fc2025-03-28 05:15:576971TEST_P(PrefetchServiceClientHintsTest, CrossSiteNone) {
Jeremy Roman586b5ec2024-02-13 15:14:406972 base::test::ScopedFeatureList enable_prefetch_ch;
6973 enable_prefetch_ch.InitAndEnableFeatureWithParameters(
6974 features::kPrefetchClientHints, {{"cross_site_behavior", "none"}});
6975
6976 MakePrefetchService(
6977 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
6978 NavigateAndCommit(GURL("https://a.test/"));
6979 client_hints_controller_delegate().SetMostRecentMainFrameViewportSize(
6980 gfx::Size(800, 600));
6981 client_hints_controller_delegate().PersistClientHints(
6982 url::Origin::Create(GURL("https://b.test/")),
6983 /*parent_rfh=*/nullptr,
6984 {network::mojom::WebClientHintsType::kViewportWidth});
6985
6986 MakePrefetchOnMainFrame(
6987 GURL("https://b.test/"),
6988 PrefetchType(PreloadingTriggerType::kSpeculationRule,
6989 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:256990 blink::mojom::SpeculationEagerness::kImmediate),
Jeremy Roman586b5ec2024-02-13 15:14:406991 blink::mojom::Referrer(GURL("https://a.test/"),
6992 network::mojom::ReferrerPolicy::kStrictOrigin));
6993 task_environment()->RunUntilIdle();
6994
6995 auto* pending = test_url_loader_factory_.GetPendingRequest(0);
6996 ASSERT_TRUE(pending);
6997 EXPECT_FALSE(pending->request.headers.HasHeader("Sec-CH-UA"));
6998 EXPECT_FALSE(pending->request.headers.HasHeader("Sec-CH-Viewport-Width"));
6999}
7000
kenossbd9a3fc2025-03-28 05:15:577001TEST_P(PrefetchServiceClientHintsTest, CrossSiteLowEntropy) {
Jeremy Roman586b5ec2024-02-13 15:14:407002 base::test::ScopedFeatureList enable_prefetch_ch;
7003 enable_prefetch_ch.InitAndEnableFeatureWithParameters(
7004 features::kPrefetchClientHints, {{"cross_site_behavior", "low_entropy"}});
7005
7006 MakePrefetchService(
7007 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7008 NavigateAndCommit(GURL("https://a.test/"));
7009 client_hints_controller_delegate().SetMostRecentMainFrameViewportSize(
7010 gfx::Size(800, 600));
7011 client_hints_controller_delegate().PersistClientHints(
7012 url::Origin::Create(GURL("https://b.test/")),
7013 /*parent_rfh=*/nullptr,
7014 {network::mojom::WebClientHintsType::kViewportWidth});
7015
7016 MakePrefetchOnMainFrame(
7017 GURL("https://b.test/"),
7018 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7019 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:257020 blink::mojom::SpeculationEagerness::kImmediate),
Jeremy Roman586b5ec2024-02-13 15:14:407021 blink::mojom::Referrer(GURL("https://a.test/"),
7022 network::mojom::ReferrerPolicy::kStrictOrigin));
7023 task_environment()->RunUntilIdle();
7024
7025 auto* pending = test_url_loader_factory_.GetPendingRequest(0);
7026 ASSERT_TRUE(pending);
7027 EXPECT_TRUE(pending->request.headers.HasHeader("Sec-CH-UA"));
7028 EXPECT_FALSE(pending->request.headers.HasHeader("Sec-CH-Viewport-Width"));
7029}
7030
kenossbd9a3fc2025-03-28 05:15:577031TEST_P(PrefetchServiceClientHintsTest, CrossSiteAll) {
Jeremy Roman586b5ec2024-02-13 15:14:407032 base::test::ScopedFeatureList enable_prefetch_ch;
7033 enable_prefetch_ch.InitAndEnableFeatureWithParameters(
7034 features::kPrefetchClientHints, {{"cross_site_behavior", "all"}});
7035
7036 MakePrefetchService(
7037 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7038 NavigateAndCommit(GURL("https://a.test/"));
7039 client_hints_controller_delegate().SetMostRecentMainFrameViewportSize(
7040 gfx::Size(800, 600));
7041 client_hints_controller_delegate().PersistClientHints(
7042 url::Origin::Create(GURL("https://b.test/")),
7043 /*parent_rfh=*/nullptr,
7044 {network::mojom::WebClientHintsType::kViewportWidth});
7045
7046 MakePrefetchOnMainFrame(
7047 GURL("https://b.test/"),
7048 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7049 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:257050 blink::mojom::SpeculationEagerness::kImmediate),
Jeremy Roman586b5ec2024-02-13 15:14:407051 blink::mojom::Referrer(GURL("https://a.test/"),
7052 network::mojom::ReferrerPolicy::kStrictOrigin));
7053 task_environment()->RunUntilIdle();
7054
7055 auto* pending = test_url_loader_factory_.GetPendingRequest(0);
7056 ASSERT_TRUE(pending);
7057 EXPECT_TRUE(pending->request.headers.HasHeader("Sec-CH-UA"));
7058 EXPECT_TRUE(pending->request.headers.HasHeader("Sec-CH-Viewport-Width"));
7059}
7060
kenossbd9a3fc2025-03-28 05:15:577061TEST_P(PrefetchServiceTest, CancelWhileBlockedOnHead) {
Jeremy Roman796d9ec2024-05-28 18:33:297062 MakePrefetchService(
7063 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7064 NavigateAndCommit(GURL("https://example.com/"));
7065
7066 GURL next_url("https://example.com/two");
7067 MakePrefetchOnMainFrame(
7068 next_url,
7069 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7070 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:257071 blink::mojom::SpeculationEagerness::kImmediate),
Jeremy Roman796d9ec2024-05-28 18:33:297072 blink::mojom::Referrer(GURL("https://example.com/"),
7073 network::mojom::ReferrerPolicy::kStrictOrigin));
7074 task_environment()->RunUntilIdle();
7075
7076 // Start a navigation to the URL (one must be running for GetPrefetchToServe
7077 // to work, at present).
7078 NavigateInitiatedByRenderer(next_url);
7079
7080 // Try to access the outcome of the prefetch, like the serving path does.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577081 base::test::TestFuture<PrefetchServingHandle> future;
Jeremy Roman796d9ec2024-05-28 18:33:297082 GetPrefetchToServe(future, next_url, MainDocumentToken());
7083 EXPECT_FALSE(future.IsReady());
7084
7085 // Cancel the prefetch.
7086 auto* prefetch_document_manager =
7087 PrefetchDocumentManager::GetOrCreateForCurrentDocument(main_rfh());
7088 std::vector<blink::mojom::SpeculationCandidatePtr> candidates;
kenoss1ce36df592024-12-03 01:04:357089 prefetch_document_manager->ProcessCandidates(candidates);
Jeremy Roman796d9ec2024-05-28 18:33:297090 task_environment()->RunUntilIdle();
7091
7092 // If all goes well, the service has reported that the prefetch cannot be
7093 // served. This could be changed in the future to instead wait for the
7094 // prefetch rather than cancelling it, but what's key here is that it doesn't
7095 // hang.
7096 ASSERT_TRUE(future.IsReady());
7097 EXPECT_FALSE(future.Get());
7098}
7099
Taiyo Mizuhashi08c47d12025-03-05 23:46:077100// Tests that the `PrefetchStreamingURLLoader` disconnection during
7101// `PrefetchService`'s redirection handling (starts from
7102// `PrefetchStreamingURLLoader::OnReceiveRedirect`, and ends until
7103// `PrefetchService` calls `PrefetchStreamingURLLoader::HandleRedirect`) causes
7104// no crash, and the corresponding prefetch should not be served.
7105// A regression test for crbug.com/396133768.
kenossbd9a3fc2025-03-28 05:15:577106TEST_P(
Taiyo Mizuhashi08c47d12025-03-05 23:46:077107 PrefetchServiceTest,
7108 DISABLED_CHROMEOS(URLLoaderDisconnectedWhileHandlingRedirectEligibilty)) {
Taiyo Mizuhashi08c47d12025-03-05 23:46:077109 MakePrefetchService(
7110 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7111 PrefetchService* prefetch_service =
7112 BrowserContextImpl::From(browser_context())->GetPrefetchService();
7113
7114 MakePrefetchOnMainFrame(
7115 GURL("https://example.com"),
7116 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7117 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:257118 blink::mojom::SpeculationEagerness::kImmediate));
Taiyo Mizuhashi08c47d12025-03-05 23:46:077119 task_environment()->RunUntilIdle();
7120 VerifyCommonRequestState(GURL("https://example.com"),
7121 {.use_prefetch_proxy = true});
7122 VerifyFollowRedirectParams(0);
7123
7124 // Set a handler so that the eligibility check sequences invocked after
7125 // `PrefetchService::OnPrefetchRedirect` will be paused.
7126 base::test::TestFuture<base::OnceClosure>
7127 redirect_eligibility_check_callback_future;
7128 prefetch_service->SetDelayEligibilityCheckForTesting(base::BindRepeating(
7129 [](base::test::TestFuture<base::OnceClosure>*
7130 redirect_eligibility_check_callback_future,
7131 base::OnceClosure callback) {
7132 redirect_eligibility_check_callback_future->SetValue(
7133 std::move(callback));
7134 },
7135 base::Unretained(&redirect_eligibility_check_callback_future)));
7136
7137 net::RedirectInfo redirect_info;
7138 redirect_info.new_method = "GET";
7139 redirect_info.new_referrer_policy =
7140 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
7141 redirect_info.new_url = GURL("https://redirect.com");
7142 network::TestURLLoaderFactory::PendingRequest* request =
7143 test_url_loader_factory_.GetPendingRequest(0);
7144 ASSERT_TRUE(request);
7145 ASSERT_TRUE(request->client);
7146 auto redirect_head = CreateURLResponseHeadForPrefetch(
7147 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
7148 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"));
7149
7150 request->client->OnReceiveRedirect(redirect_info, redirect_head.Clone());
7151 task_environment()->RunUntilIdle();
7152
7153 // Now the redirect handling is paused right before
7154 // `PrefetchService::OnPrefetchRedirect.`
7155
7156 // Disconnect URLLoader.
7157 base::test::TestFuture<void> disconnect_future;
7158 std::vector<std::pair<GURL, base::WeakPtr<PrefetchContainer>>> prefetches =
7159 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
7160 PrefetchContainer::Key(MainDocumentToken(),
7161 GURL("https://example.com")));
7162 ASSERT_EQ(1u, prefetches.size());
7163 base::WeakPtr<PrefetchContainer> prefetch_container = prefetches[0].second;
7164 prefetch_container->GetStreamingURLLoader()->SetOnDeletionScheduledForTests(
7165 disconnect_future.GetCallback());
7166 request->client.reset();
7167 ASSERT_TRUE(disconnect_future.Wait());
7168
7169 // Resume the eligibility check.
7170 redirect_eligibility_check_callback_future.Take().Run();
7171 task_environment()->RunUntilIdle();
7172
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:087173 // Now `PrefetchServableState` should be `kNotServable` since we don't
7174 // have a non-redirect response but `PrefetchStreamingURLLoader` is gone.
Taiyo Mizuhashi08c47d12025-03-05 23:46:077175 EXPECT_EQ(prefetch_container->GetServableState(base::TimeDelta::Max()),
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:087176 PrefetchServableState::kNotServable);
Taiyo Mizuhashi08c47d12025-03-05 23:46:077177
7178 // Start a navigation. The prefetch should not be served.
7179 std::unique_ptr<NavigationResult> navigation_result =
7180 SimulatePartOfNavigation(GURL("https://example.com"),
7181 /*is_renderer_initiated=*/true,
7182 /*is_nav_prerender=*/true);
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577183 ASSERT_TRUE(navigation_result->serving_handle_future.IsReady());
7184 EXPECT_FALSE(navigation_result->serving_handle_future.Take());
Taiyo Mizuhashi08c47d12025-03-05 23:46:077185
7186 prefetch_service->SetDelayEligibilityCheckForTesting(base::NullCallback());
7187}
7188
7189// Tests that the `PrefetchStreamingURLLoader` disconnection during
7190// `PrefetchService`'s redirection handling causes no crash, and successfully
7191// unblocks the navigation that potentially matches the corresponding
7192// prefetch and thus was blocked in the match resolver (BlockUntilHead).
7193// A regression test for crbug.com/396133768.√
kenossbd9a3fc2025-03-28 05:15:577194TEST_P(
Taiyo Mizuhashi08c47d12025-03-05 23:46:077195 PrefetchServiceTest,
7196 DISABLED_CHROMEOS(
7197 URLLoaderDisconnectedWhileHandlingRedirectEligibilty_BlockUntilHead)) {
Taiyo Mizuhashi08c47d12025-03-05 23:46:077198 MakePrefetchService(
7199 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7200 PrefetchService* prefetch_service =
7201 BrowserContextImpl::From(browser_context())->GetPrefetchService();
7202
7203 MakePrefetchOnMainFrame(
7204 GURL("https://example.com"),
7205 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7206 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:257207 blink::mojom::SpeculationEagerness::kImmediate));
Taiyo Mizuhashi08c47d12025-03-05 23:46:077208 task_environment()->RunUntilIdle();
7209 VerifyCommonRequestState(GURL("https://example.com"),
7210 {.use_prefetch_proxy = true});
7211 VerifyFollowRedirectParams(0);
7212
7213 // Start a navigation and invoke FindPrefetch to start a wait loop.
7214 std::unique_ptr<NavigationResult> navigation_result =
7215 SimulatePartOfNavigation(GURL("https://example.com"),
7216 /*is_renderer_initiated=*/true,
7217 /*is_nav_prerender=*/true);
7218 task_environment()->RunUntilIdle();
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577219 ASSERT_FALSE(navigation_result->serving_handle_future.IsReady());
Taiyo Mizuhashi08c47d12025-03-05 23:46:077220
7221 // Set a handler so that the eligibility check sequences invocked after
7222 // `PrefetchService::OnPrefetchRedirect` will be paused.
7223 base::test::TestFuture<base::OnceClosure>
7224 redirect_eligibility_check_callback_future;
7225 prefetch_service->SetDelayEligibilityCheckForTesting(base::BindRepeating(
7226 [](base::test::TestFuture<base::OnceClosure>*
7227 redirect_eligibility_check_callback_future,
7228 base::OnceClosure callback) {
7229 redirect_eligibility_check_callback_future->SetValue(
7230 std::move(callback));
7231 },
7232 base::Unretained(&redirect_eligibility_check_callback_future)));
7233
7234 // Start redirecting.
7235 net::RedirectInfo redirect_info;
7236 redirect_info.new_method = "GET";
7237 redirect_info.new_referrer_policy =
7238 net::ReferrerPolicy::REDUCE_GRANULARITY_ON_TRANSITION_CROSS_ORIGIN;
7239 redirect_info.new_url = GURL("https://redirect.com");
7240 network::TestURLLoaderFactory::PendingRequest* request =
7241 test_url_loader_factory_.GetPendingRequest(0);
7242 ASSERT_TRUE(request);
7243 ASSERT_TRUE(request->client);
7244 auto redirect_head = CreateURLResponseHeadForPrefetch(
7245 net::HTTP_PERMANENT_REDIRECT, kHTMLMimeType,
7246 /*use_prefetch_proxy=*/true, {}, GURL("https://redirect.com"));
7247
7248 request->client->OnReceiveRedirect(redirect_info, redirect_head.Clone());
7249 task_environment()->RunUntilIdle();
7250
7251 // Now the redirect handling is paused right before
7252 // `PrefetchService::OnPrefetchRedirect.`
7253
7254 // Disconnect URLLoader.
7255 base::test::TestFuture<void> disconnect_future;
7256 std::vector<std::pair<GURL, base::WeakPtr<PrefetchContainer>>> prefetches =
7257 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
7258 PrefetchContainer::Key(MainDocumentToken(),
7259 GURL("https://example.com")));
7260 ASSERT_EQ(1u, prefetches.size());
7261 base::WeakPtr<PrefetchContainer> prefetch_container = prefetches[0].second;
7262 prefetch_container->GetStreamingURLLoader()->SetOnDeletionScheduledForTests(
7263 disconnect_future.GetCallback());
7264 request->client.reset();
7265 ASSERT_TRUE(disconnect_future.Wait());
7266 // `PrefetchStreamingURLLoader::DisconnectPrefetchURLLoaderMojo` will directly
kenoss3dfd90792025-03-11 01:13:467267 // invoke `PrefetchMatchResolver::OnHeadDetermine`. At this point the
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:087268 // container's `PrefetchServableState` should be
7269 // `kShouldBlockUntilHeadReceived` (same with a normal case of an ineligible
7270 // redirect), so it should be unblocked for unmatched.
Taiyo Mizuhashi08c47d12025-03-05 23:46:077271 // TODO(crbug.com/396133768): Explicitly check more detailed Servable/Load
7272 // states.
7273 redirect_eligibility_check_callback_future.Take().Run();
7274 task_environment()->RunUntilIdle();
7275 // The prefetch should not be served.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577276 EXPECT_FALSE(navigation_result->serving_handle_future.Take());
Taiyo Mizuhashi08c47d12025-03-05 23:46:077277
7278 prefetch_service->SetDelayEligibilityCheckForTesting(base::NullCallback());
7279}
7280
kenoss8fb82852025-03-11 02:20:427281// Test that multiple concurrent navigations are handled correctly.
kenossf6fbd5652024-09-04 00:40:287282//
7283// Scenario:
7284//
7285// - Prefetch A started.
7286// - A received non-redirect header.
7287// - Navigation X started, which matches to A. Unblocked synchronously as
7288// success.
7289// - Navigation Y started, which matches to A. Unblocked synchronously as
7290// success.
kenossbd9a3fc2025-03-28 05:15:577291TEST_P(
kenossf6fbd5652024-09-04 00:40:287292 PrefetchServiceTest,
7293 DISABLED_CHROMEOS(MultipleConcurrentNavigationSuccessBeforeNavigations)) {
kenossf6fbd5652024-09-04 00:40:287294 base::HistogramTester histogram_tester;
7295
7296 MakePrefetchService(
7297 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7298
7299 MakePrefetchOnMainFrame(
7300 GURL("https://example.com"),
7301 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7302 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:257303 blink::mojom::SpeculationEagerness::kImmediate));
kenossf6fbd5652024-09-04 00:40:287304 task_environment()->RunUntilIdle();
7305
7306 VerifyCommonRequestState(GURL("https://example.com"),
7307 {.use_prefetch_proxy = true});
7308 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
7309 /*use_prefetch_proxy=*/true,
7310 {{"X-Testing", "Hello World"}}, kHTMLBody);
7311
7312 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody));
7313
7314 std::unique_ptr<NavigationResult> nav_res1 = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497315 GURL("https://example.com"), /*is_renderer_initiated=*/true,
7316 /*is_nav_prerender=*/false);
kenossf6fbd5652024-09-04 00:40:287317 std::unique_ptr<NavigationResult> nav_res2 = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497318 GURL("https://example.com"), /*is_renderer_initiated=*/true,
7319 /*is_nav_prerender=*/false);
kenossf6fbd5652024-09-04 00:40:287320 task_environment()->RunUntilIdle();
7321
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577322 ExpectServingReaderSuccess(FROM_HERE, nav_res1->serving_handle_future.Take());
kenossf6fbd5652024-09-04 00:40:287323 ExpectServingMetrics(
7324 FROM_HERE, nav_res1,
7325 {.prefetch_status = PrefetchStatus::kPrefetchSuccessful,
7326 .prefetch_header_latency = base::Milliseconds(kHeaderLatency),
7327 .required_private_prefetch_proxy = true});
7328
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577329 ExpectServingReaderSuccess(FROM_HERE, nav_res2->serving_handle_future.Take());
kenossf6fbd5652024-09-04 00:40:287330 ExpectServingMetrics(
7331 FROM_HERE, nav_res2,
7332 {.prefetch_status = PrefetchStatus::kPrefetchSuccessful,
7333 .prefetch_header_latency = base::Milliseconds(kHeaderLatency),
7334 .required_private_prefetch_proxy = true});
7335
7336 histogram_tester.ExpectUniqueSample(
7337 "PrefetchProxy.AfterClick.RedirectChainSize", 1, 2);
7338 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:227339 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
7340 "SpeculationRule_"
Takashi Nakayamac8183502025-08-04 14:18:507341 "Immediate2",
kenossf6fbd5652024-09-04 00:40:287342 false, 2);
Taiyo Mizuhashia009caf2025-08-05 07:34:227343 // Call `PrefetchContainer` dtor to record the UMA.
7344 PrefetchDocumentManager::DeleteForCurrentDocument(main_rfh());
7345 histogram_tester.ExpectUniqueSample(
7346 "Prefetch.PrefetchContainer.ServedCount.SpeculationRule_Immediate2", 2,
7347 1);
kenossf6fbd5652024-09-04 00:40:287348}
7349
7350// Scenario:
7351//
7352// - Prefetch A started.
7353// - Navigation X started, which matches to A. Blocked by A.
7354// - Navigation Y started, which matches to A. Blocked by A.
7355// - A received non-redirect header. Unblocks them as success.
kenossbd9a3fc2025-03-28 05:15:577356TEST_P(
kenossf6fbd5652024-09-04 00:40:287357 PrefetchServiceTest,
7358 DISABLED_CHROMEOS(MultipleConcurrentNavigationBlockUntilHeadThenSuccess)) {
kenossf6fbd5652024-09-04 00:40:287359 base::HistogramTester histogram_tester;
7360
7361 MakePrefetchService(
7362 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7363
7364 MakePrefetchOnMainFrame(
7365 GURL("https://example.com"),
7366 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7367 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:257368 blink::mojom::SpeculationEagerness::kImmediate));
kenossf6fbd5652024-09-04 00:40:287369 task_environment()->RunUntilIdle();
7370
7371 VerifyCommonRequestState(GURL("https://example.com"),
7372 {.use_prefetch_proxy = true});
7373
7374 std::unique_ptr<NavigationResult> nav_res1 = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497375 GURL("https://example.com"), /*is_renderer_initiated=*/true,
7376 /*is_nav_prerender=*/false);
kenossf6fbd5652024-09-04 00:40:287377 std::unique_ptr<NavigationResult> nav_res2 = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497378 GURL("https://example.com"), /*is_renderer_initiated=*/true,
7379 /*is_nav_prerender=*/false);
kenossf6fbd5652024-09-04 00:40:287380 task_environment()->RunUntilIdle();
7381
7382 SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
7383 /*use_prefetch_proxy=*/true,
7384 {{"X-Testing", "Hello World"}},
7385 std::size(kHTMLBody));
7386 SendBodyContentOfResponseAndWait(kHTMLBody);
7387 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
7388
7389 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
Takashi Nakayama978f0a152025-06-17 08:26:257390 blink::mojom::SpeculationEagerness::kImmediate,
kenossf6fbd5652024-09-04 00:40:287391 /*is_accurate=*/true);
7392
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577393 ExpectServingReaderSuccess(FROM_HERE, nav_res1->serving_handle_future.Take());
kenossf6fbd5652024-09-04 00:40:287394 // TODO(crbug.com/356540465): See the bug. Make PrefetchServingMetrics
7395 // available for multiple concurrent navigations.
7396 ExpectServingMetrics(
7397 FROM_HERE, nav_res1,
7398 {.prefetch_status = PrefetchStatus::kPrefetchNotFinishedInTime,
7399 .prefetch_header_latency = std::nullopt,
7400 .required_private_prefetch_proxy = true});
7401
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577402 ExpectServingReaderSuccess(FROM_HERE, nav_res2->serving_handle_future.Take());
kenossf6fbd5652024-09-04 00:40:287403 ExpectServingMetrics(
7404 FROM_HERE, nav_res2,
7405 {.prefetch_status = PrefetchStatus::kPrefetchSuccessful,
7406 .prefetch_header_latency = base::Milliseconds(kHeaderLatency),
7407 .required_private_prefetch_proxy = true});
7408
7409 histogram_tester.ExpectUniqueSample(
7410 "PrefetchProxy.AfterClick.RedirectChainSize", 1, 2);
7411 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:227412 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
7413 "SpeculationRule_"
Takashi Nakayamac8183502025-08-04 14:18:507414 "Immediate2",
kenossf6fbd5652024-09-04 00:40:287415 true, 2);
Taiyo Mizuhashia009caf2025-08-05 07:34:227416 // Call `PrefetchContainer` dtor to record the UMA.
7417 PrefetchDocumentManager::DeleteForCurrentDocument(main_rfh());
7418 histogram_tester.ExpectUniqueSample(
7419 "Prefetch.PrefetchContainer.ServedCount.SpeculationRule_Immediate2", 2,
7420 1);
kenossf6fbd5652024-09-04 00:40:287421}
7422
7423// Scenario:
7424//
7425// - Prefetch A started with NVS hint.
7426// - Navigation X started, which is potentially matches and eventually matches
7427// to A. Blocked by A.
7428// - Navigation Y started, which is potentially matches and not eventually
7429// matches to A. Blocked by A.
7430// - A received non-redirect header. Unblocks them as success/fail.
kenossbd9a3fc2025-03-28 05:15:577431TEST_P(PrefetchServiceTest,
kenossf6fbd5652024-09-04 00:40:287432 DISABLED_CHROMEOS(
7433 MultipleConcurrentNavigationBlockUntilHeadThenSuccessFail)) {
kenossf6fbd5652024-09-04 00:40:287434 base::HistogramTester histogram_tester;
7435
7436 MakePrefetchService(
7437 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7438
7439 network::mojom::NoVarySearchPtr no_vary_search_hint =
7440 network::mojom::NoVarySearch::New();
7441 no_vary_search_hint->vary_on_key_order = true;
7442 no_vary_search_hint->search_variance =
7443 network::mojom::SearchParamsVariance::NewNoVaryParams(
7444 std::vector<std::string>({"match", "notEventuallyMatch"}));
7445
7446 MakePrefetchOnMainFrame(
7447 GURL("https://example.com/?match=0"),
7448 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7449 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:257450 blink::mojom::SpeculationEagerness::kImmediate),
kenossf6fbd5652024-09-04 00:40:287451 /* referrer */ blink::mojom::Referrer(), std::move(no_vary_search_hint));
7452 task_environment()->RunUntilIdle();
7453
7454 VerifyCommonRequestState(GURL("https://example.com/?match=0"),
7455 {.use_prefetch_proxy = true});
7456
7457 std::unique_ptr<NavigationResult> nav_res1 = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497458 GURL("https://example.com/?match=1"), /*is_renderer_initiated=*/true,
7459 /*is_nav_prerender=*/false);
kenossf6fbd5652024-09-04 00:40:287460 std::unique_ptr<NavigationResult> nav_res2 = SimulatePartOfNavigation(
7461 GURL("https://example.com/?notEventuallyMatch=1"),
kenoss804a72dc2024-10-11 12:43:497462 /*is_renderer_initiated=*/true, /*is_nav_prerender=*/false);
kenossf6fbd5652024-09-04 00:40:287463 task_environment()->RunUntilIdle();
7464
7465 SendHeadOfResponseAndWait(
7466 net::HTTP_OK, kHTMLMimeType,
7467 /*use_prefetch_proxy=*/true,
7468 {{"X-Testing", "Hello World"}, {"No-Vary-Search", "params=(\"match\")"}},
7469 std::size(kHTMLBody));
7470 SendBodyContentOfResponseAndWait(kHTMLBody);
7471 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
7472
7473 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
Takashi Nakayama978f0a152025-06-17 08:26:257474 blink::mojom::SpeculationEagerness::kImmediate,
kenossf6fbd5652024-09-04 00:40:287475 /*is_accurate=*/true);
7476
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577477 ExpectServingReaderSuccess(FROM_HERE, nav_res1->serving_handle_future.Take());
kenossf6fbd5652024-09-04 00:40:287478 // TODO(crbug.com/356540465): See the bug. Make PrefetchServingMetrics
7479 // available for multiple concurrent navigations.
7480 ExpectServingMetrics(
7481 FROM_HERE, nav_res1,
7482 {.prefetch_status = PrefetchStatus::kPrefetchNotFinishedInTime,
7483 .prefetch_header_latency = std::nullopt,
7484 .required_private_prefetch_proxy = true});
7485
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577486 EXPECT_FALSE(nav_res2->serving_handle_future.Take());
kenossf6fbd5652024-09-04 00:40:287487
7488 histogram_tester.ExpectUniqueSample(
7489 "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
7490 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:227491 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
7492 "SpeculationRule_"
Takashi Nakayamac8183502025-08-04 14:18:507493 "Immediate2",
kenossf6fbd5652024-09-04 00:40:287494 true, 2);
Taiyo Mizuhashia009caf2025-08-05 07:34:227495 // Call `PrefetchContainer` dtor to record the UMA.
7496 PrefetchDocumentManager::DeleteForCurrentDocument(main_rfh());
7497 histogram_tester.ExpectUniqueSample(
7498 "Prefetch.PrefetchContainer.ServedCount.SpeculationRule_Immediate2", 1,
7499 1);
kenossf6fbd5652024-09-04 00:40:287500}
7501
7502// Scenario:
7503//
7504// - Prefetch A started.
7505// - Navigation X started, which matches to A. Blocked by A.
7506// - Navigation Y started, which matches to A. Blocked by A.
7507// - Cookies of domain of A changed.
7508// - A received non-redirect header. Unblocks them as fail.
7509//
7510// This test checks that it is safe to call
kenoss8fb82852025-03-11 02:20:427511// `PrefetchContainer::OnDetectedCookiesChange()` multiple times.
kenossbd9a3fc2025-03-28 05:15:577512TEST_P(PrefetchServiceTest,
kenossf6fbd5652024-09-04 00:40:287513 DISABLED_CHROMEOS(
7514 MultipleConcurrentNavigationBlockUntilHeadThenCookiesChanged)) {
kenossf6fbd5652024-09-04 00:40:287515 base::HistogramTester histogram_tester;
7516
7517 MakePrefetchService(
7518 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7519
7520 MakePrefetchOnMainFrame(
7521 GURL("https://example.com"),
7522 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7523 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:257524 blink::mojom::SpeculationEagerness::kImmediate));
kenossf6fbd5652024-09-04 00:40:287525 task_environment()->RunUntilIdle();
7526
7527 VerifyCommonRequestState(GURL("https://example.com"),
7528 {.use_prefetch_proxy = true});
7529
7530 std::unique_ptr<NavigationResult> nav_res1 = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497531 GURL("https://example.com"), /*is_renderer_initiated=*/true,
7532 /*is_nav_prerender=*/false);
kenossf6fbd5652024-09-04 00:40:287533 std::unique_ptr<NavigationResult> nav_res2 = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497534 GURL("https://example.com"), /*is_renderer_initiated=*/true,
7535 /*is_nav_prerender=*/false);
kenossf6fbd5652024-09-04 00:40:287536 task_environment()->RunUntilIdle();
7537
7538 ASSERT_TRUE(SetCookie(GURL("https://example.com"), "testing"));
7539
7540 SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
7541 /*use_prefetch_proxy=*/true,
7542 {{"X-Testing", "Hello World"}},
7543 std::size(kHTMLBody));
7544 SendBodyContentOfResponseAndWait(kHTMLBody);
7545 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
7546
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577547 EXPECT_FALSE(nav_res1->serving_handle_future.Take());
kenossf6fbd5652024-09-04 00:40:287548
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577549 EXPECT_FALSE(nav_res2->serving_handle_future.Take());
kenossf6fbd5652024-09-04 00:40:287550
7551 histogram_tester.ExpectTotalCount(
7552 "PrefetchProxy.AfterClick.RedirectChainSize", 0);
7553 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:227554 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
7555 "SpeculationRule_"
Takashi Nakayamac8183502025-08-04 14:18:507556 "Immediate2",
kenossf6fbd5652024-09-04 00:40:287557 true, 2);
Taiyo Mizuhashia009caf2025-08-05 07:34:227558 // Call `PrefetchContainer` dtor to record the UMA.
7559 PrefetchDocumentManager::DeleteForCurrentDocument(main_rfh());
7560 histogram_tester.ExpectUniqueSample(
7561 "Prefetch.PrefetchContainer.ServedCount.SpeculationRule_Immediate2", 0,
7562 1);
kenossf6fbd5652024-09-04 00:40:287563}
7564
kenossb68c9e82024-10-10 10:11:157565// Scenario:
7566//
7567// - Prefetch ahead of prerender A started. The eligibility check is not done
7568// yet.
7569// - Navigation X started, which is potentially matches and eventually matches
7570// to A. Blocked by A. (Regard X as prerender, while we don't assume that in
7571// this test actually.)
7572// - The eligibility check of A scceeds. Matching process proceeds and ends as
7573// success.
kenossbd9a3fc2025-03-28 05:15:577574TEST_P(PrefetchServiceTest,
kenossb68c9e82024-10-10 10:11:157575 DISABLED_CHROMEOS(PrefetchAheadOfPrerenderSuccess)) {
7576 base::test::ScopedFeatureList scoped_feature_list;
7577 scoped_feature_list.InitWithFeatures(
7578 {features::kPrerender2FallbackPrefetchSpecRules}, {});
7579
7580 base::HistogramTester histogram_tester;
7581
7582 MakePrefetchService(
7583 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7584
7585 base::test::TestFuture<base::OnceClosure> eligibility_check_callback_future;
7586 auto& prefetch_service = *PrefetchService::GetFromFrameTreeNodeId(
7587 web_contents()->GetPrimaryMainFrame()->GetFrameTreeNodeId());
7588 prefetch_service.SetDelayEligibilityCheckForTesting(base::BindRepeating(
7589 [](base::test::TestFuture<base::OnceClosure>*
7590 eligibility_check_callback_future,
7591 base::OnceClosure callback) {
7592 eligibility_check_callback_future->SetValue(std::move(callback));
7593 },
7594 base::Unretained(&eligibility_check_callback_future)));
7595
7596 MakePrefetchOnMainFrame(
7597 GURL("https://example.com"),
7598 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7599 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:257600 blink::mojom::SpeculationEagerness::kImmediate),
kenossb68c9e82024-10-10 10:11:157601 blink::mojom::Referrer(), network::mojom::NoVarySearchPtr(),
7602 /*planned_max_preloading_type=*/PreloadingType::kPrerender);
7603 task_environment()->RunUntilIdle();
7604
7605 std::unique_ptr<NavigationResult> nav_res = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497606 GURL("https://example.com"), /*is_renderer_initiated=*/true,
7607 /*is_nav_prerender=*/true);
kenossb68c9e82024-10-10 10:11:157608 task_environment()->RunUntilIdle();
7609
7610 // The prefetch is a match candidate, but eligibility check is not done yet.
7611 // Matching process is in progress.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577612 ASSERT_FALSE(nav_res->serving_handle_future.IsReady());
kenossb68c9e82024-10-10 10:11:157613
7614 // Proceed to the eligibility check.
7615 eligibility_check_callback_future.Take().Run();
7616
David Risneya1dd2bf2025-03-06 03:40:427617 VerifyCommonRequestState(
7618 GURL("https://example.com"),
7619 {
7620 .use_prefetch_proxy = false,
7621 .sec_purpose_header_value =
7622 blink::kSecPurposePrefetchPrerenderHeaderValue,
7623 });
kenossb68c9e82024-10-10 10:11:157624
7625 SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
7626 /*use_prefetch_proxy=*/true,
7627 {{"X-Testing", "Hello World"}},
7628 std::size(kHTMLBody));
7629 SendBodyContentOfResponseAndWait(kHTMLBody);
7630 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
7631
7632 ExpectPrefetchSuccess(histogram_tester, std::size(kHTMLBody),
Takashi Nakayama978f0a152025-06-17 08:26:257633 blink::mojom::SpeculationEagerness::kImmediate,
kenossb68c9e82024-10-10 10:11:157634 /*is_accurate=*/true);
7635
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577636 ExpectServingReaderSuccess(FROM_HERE, nav_res->serving_handle_future.Take());
kenossb68c9e82024-10-10 10:11:157637 // TODO(crbug.com/356540465): See the bug. Make PrefetchServingMetrics
7638 // available for multiple concurrent navigations.
7639 ExpectServingMetrics(
7640 FROM_HERE, nav_res,
7641 {.prefetch_status = PrefetchStatus::kPrefetchSuccessful,
7642 .prefetch_header_latency = base::Milliseconds(kHeaderLatency),
7643 .required_private_prefetch_proxy = false});
7644
7645 histogram_tester.ExpectUniqueSample(
7646 "PrefetchProxy.AfterClick.RedirectChainSize", 1, 1);
7647 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi43066071f2025-04-25 23:40:227648 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
7649 "SpeculationRule_"
Takashi Nakayamac8183502025-08-04 14:18:507650 "Immediate2",
kenossb68c9e82024-10-10 10:11:157651 true, 1);
kenoss14462ae2025-03-21 02:39:517652
7653 prefetch_service.SetDelayEligibilityCheckForTesting(base::NullCallback());
kenossb68c9e82024-10-10 10:11:157654}
7655
7656// Scenario:
7657//
7658// - Prefetch ahead of prerender A started. The eligibility check is not done
7659// yet.
7660// - Navigation X started, which is potentially matches to A. Blocked by A.
7661// (Regard X as prerender, while we don't assume that in this test actually.)
7662// - The eligibility check of A failed (due to non https). Matching process ends
7663// with no prefetch.
kenossbd9a3fc2025-03-28 05:15:577664TEST_P(PrefetchServiceTest,
kenossb68c9e82024-10-10 10:11:157665 DISABLED_CHROMEOS(PrefetchAheadOfPrerenderIneligible)) {
7666 base::test::ScopedFeatureList scoped_feature_list;
7667 scoped_feature_list.InitWithFeatures(
7668 {features::kPrerender2FallbackPrefetchSpecRules}, {});
7669
7670 base::HistogramTester histogram_tester;
7671
7672 MakePrefetchService(
7673 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
7674
7675 base::test::TestFuture<base::OnceClosure> eligibility_check_callback_future;
7676 auto& prefetch_service = *PrefetchService::GetFromFrameTreeNodeId(
7677 web_contents()->GetPrimaryMainFrame()->GetFrameTreeNodeId());
7678 prefetch_service.SetDelayEligibilityCheckForTesting(base::BindRepeating(
7679 [](base::test::TestFuture<base::OnceClosure>*
7680 eligibility_check_callback_future,
7681 base::OnceClosure callback) {
7682 eligibility_check_callback_future->SetValue(std::move(callback));
7683 },
7684 base::Unretained(&eligibility_check_callback_future)));
7685
7686 MakePrefetchOnMainFrame(
7687 GURL("http://example.com"),
7688 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7689 /*use_prefetch_proxy=*/false,
Takashi Nakayama978f0a152025-06-17 08:26:257690 blink::mojom::SpeculationEagerness::kImmediate),
kenossb68c9e82024-10-10 10:11:157691 blink::mojom::Referrer(), network::mojom::NoVarySearchPtr(),
7692 /*planned_max_preloading_type=*/PreloadingType::kPrerender);
7693 task_environment()->RunUntilIdle();
7694
7695 std::unique_ptr<NavigationResult> nav_res = SimulatePartOfNavigation(
kenoss804a72dc2024-10-11 12:43:497696 GURL("http://example.com"), /*is_renderer_initiated=*/true,
7697 /*is_nav_prerender=*/true);
kenossb68c9e82024-10-10 10:11:157698 task_environment()->RunUntilIdle();
7699
7700 // The prefetch is a match candidate, but eligibility check is not done yet.
7701 // Matching process is in progress.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577702 ASSERT_FALSE(nav_res->serving_handle_future.IsReady());
kenossb68c9e82024-10-10 10:11:157703
7704 // Proceed to the eligibility check.
7705 eligibility_check_callback_future.Take().Run();
7706
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:577707 ASSERT_FALSE(nav_res->serving_handle_future.Take());
kenossb68c9e82024-10-10 10:11:157708
7709 EXPECT_EQ(RequestCount(), 0);
7710 ExpectPrefetchNotEligible(histogram_tester,
7711 PreloadingEligibility::kSchemeIsNotHttps,
7712 /*is_accurate=*/true);
7713
7714 // Note that serving metrics is not recorded for the prefetch because
7715 // `HasPrefetchStatus()` doesn't hold in
7716 // `PrefetchContainer::UpdateServingPageMetrics()`.
kenoss14462ae2025-03-21 02:39:517717
7718 prefetch_service.SetDelayEligibilityCheckForTesting(base::NullCallback());
kenossb68c9e82024-10-10 10:11:157719}
7720
kenossbd9a3fc2025-03-28 05:15:577721TEST_P(PrefetchServiceTest,
Wayne Jackson Jr.0118d2a2025-02-21 10:53:467722 DISABLED_CHROMEOS(IsPrefetchDuplicateSameNoVarySearchHint)) {
Wayne Jackson Jr.0118d2a2025-02-21 10:53:467723 base::HistogramTester histogram_tester;
7724 MakePrefetchService(
7725 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
7726 /*num_on_prefetch_likely_calls=*/std::nullopt));
7727
7728 std::unique_ptr<ProbePrefetchRequestStatusListener> probe_listener =
7729 std::make_unique<ProbePrefetchRequestStatusListener>();
7730
7731 std::unique_ptr<content::PrefetchRequestStatusListener>
7732 request_status_listener =
7733 std::make_unique<TestablePrefetchRequestStatusListener>(
7734 probe_listener->GetWeakPtr());
7735
7736 std::vector<std::string> no_vary_params = {"ts"};
7737 net::HttpNoVarySearchData nvs_hint =
7738 net::HttpNoVarySearchData::CreateFromNoVaryParams(no_vary_params, false);
7739 GURL pf_one_url("https://example.com/search?q=ai&ts=1000");
7740 std::unique_ptr<content::PrefetchHandle> handle =
7741 MakePrefetchFromBrowserContext(pf_one_url, nvs_hint, {},
7742 std::move(request_status_listener));
7743 task_environment()->RunUntilIdle();
7744
7745 // Test with the "no-vary" param value changed.
7746 GURL pf_two_url("https://example.com/search?q=ai&ts=1001");
7747 EXPECT_TRUE(browser_context()->IsPrefetchDuplicate(pf_two_url, nvs_hint));
7748
7749 // Test with an additional query parameter.
7750 GURL pf_three_url("https://example.com/search?q=ai&ts=1000&qsubts=1000");
7751 EXPECT_FALSE(browser_context()->IsPrefetchDuplicate(pf_three_url, nvs_hint));
7752
7753 // Test with the same params with different values.
7754 GURL pf_four_url("https://example.com/search?q=dogs&ts=1002");
7755 EXPECT_FALSE(browser_context()->IsPrefetchDuplicate(pf_four_url, nvs_hint));
7756}
7757
kenossb68c9e82024-10-10 10:11:157758// These tests check the behavior of
7759// `PrefetchService::AddPrefetchContainerWithoutStartingPrefetch()` if an old
7760// prefetch is registered and yet another new prefetch for the same key is
7761// added.
kenossbd9a3fc2025-03-28 05:15:577762class PrefetchServiceAddPrefetchContainerTest
7763 : public PrefetchServiceTestBase,
7764 public WithPrefetchServiceRearchParam,
7765 public ::testing::WithParamInterface<PrefetchServiceRearchParam::Arg> {
kenossb68c9e82024-10-10 10:11:157766 public:
kenossbd9a3fc2025-03-28 05:15:577767 PrefetchServiceAddPrefetchContainerTest()
7768 : WithPrefetchServiceRearchParam(GetParam()) {}
7769
kenossb68c9e82024-10-10 10:11:157770 void InitScopedFeatureList() override {
kenoss557d4092025-04-01 05:01:157771 InitBaseParams();
kenossbd9a3fc2025-03-28 05:15:577772 InitRearchFeatures();
kenossb68c9e82024-10-10 10:11:157773 scoped_feature_list_for_prerender2_fallback_.InitWithFeatures(
7774 {features::kPrerender2FallbackPrefetchSpecRules}, {});
7775 }
7776
7777 PrefetchService& GetPrefetchService() {
7778 return *PrefetchService::GetFromFrameTreeNodeId(
7779 web_contents()->GetPrimaryMainFrame()->GetFrameTreeNodeId());
7780 }
7781
7782 std::unique_ptr<PrefetchContainer> CreateSpeculationRulesPrefetchContainer(
7783 const blink::DocumentToken& document_token,
7784 const GURL& prefetch_url,
7785 PreloadingType planned_max_preloading_type) {
7786 auto prefetch_type =
7787 PrefetchType(PreloadingTriggerType::kSpeculationRule,
7788 /*use_prefetch_proxy=*/true,
Takashi Nakayama978f0a152025-06-17 08:26:257789 blink::mojom::SpeculationEagerness::kImmediate);
kenossb68c9e82024-10-10 10:11:157790
7791 auto* preloading_data =
7792 PreloadingData::GetOrCreateForWebContents(web_contents());
7793 PreloadingURLMatchCallback matcher =
7794 PreloadingDataImpl::GetPrefetchServiceMatcher(
7795 GetPrefetchService(),
7796 PrefetchContainer::Key(document_token, prefetch_url));
7797
7798 auto* attempt = static_cast<PreloadingAttemptImpl*>(
7799 preloading_data->AddPreloadingAttempt(
7800 GetPredictorForPreloadingTriggerType(prefetch_type.trigger_type()),
7801 PreloadingType::kPrefetch, std::move(matcher),
kenossb68c9e82024-10-10 10:11:157802 web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId()));
7803
7804 attempt->SetSpeculationEagerness(prefetch_type.GetEagerness());
7805
7806 return std::make_unique<PrefetchContainer>(
7807 static_cast<content::RenderFrameHostImpl&>(*main_rfh()), document_token,
7808 prefetch_url, std::move(prefetch_type), blink::mojom::Referrer(),
HuanPo Lin740620b2025-03-21 12:37:267809 std::make_optional(SpeculationRulesTags()),
Rulong Chen(陈汝龙)bf12169c2024-12-16 05:38:167810 /*no_vary_search_hint=*/std::nullopt,
Taiyo Mizuhashid7c856992025-06-23 08:56:027811 /*priority=*/std::nullopt,
kenoss3bd73b82024-10-10 20:33:497812 /*prefetch_document_manager=*/nullptr,
kenossc3f9a2c2025-03-06 15:35:517813 PreloadPipelineInfo::Create(planned_max_preloading_type),
kenoss8644f6bf2025-02-10 05:51:447814 attempt->GetWeakPtr());
kenossb68c9e82024-10-10 10:11:157815 }
7816
7817 void AddPrefetchContainerWithoutStartingPrefetchForTesting(
7818 std::unique_ptr<PrefetchContainer> prefetch_container) {
7819 // GetPrefetchService().AddPrefetchContainerWithoutStartingPrefetch(std::move(prefetch_container));
7820 GetPrefetchService().AddPrefetchContainerWithoutStartingPrefetchForTesting(
7821 std::move(prefetch_container));
7822 }
7823
7824 private:
7825 base::test::ScopedFeatureList scoped_feature_list_for_prerender2_fallback_;
7826};
7827
kenossbd9a3fc2025-03-28 05:15:577828INSTANTIATE_TEST_SUITE_P(
7829 ParametrizedTests,
7830 PrefetchServiceAddPrefetchContainerTest,
7831 testing::ValuesIn(PrefetchServiceRearchParam::Params()));
7832
7833TEST_P(PrefetchServiceAddPrefetchContainerTest, ReplacesOldWithNewByDefault) {
kenossb68c9e82024-10-10 10:11:157834 blink::DocumentToken document_token;
7835
7836 std::unique_ptr<PrefetchContainer> prefetch_container1 =
7837 CreateSpeculationRulesPrefetchContainer(document_token,
7838 GURL("https://example.com"),
7839 PreloadingType::kPrefetch);
Hiroshige Hayashizaki64802cf12025-01-16 23:18:467840 prefetch_container1->SimulatePrefetchEligibleForTest();
7841 prefetch_container1->SimulatePrefetchStartedForTest();
kenossb68c9e82024-10-10 10:11:157842 base::WeakPtr<PreloadingAttempt> attempt1 =
7843 prefetch_container1->preloading_attempt();
7844 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7845 std::move(prefetch_container1));
7846
7847 std::unique_ptr<PrefetchContainer> prefetch_container2 =
7848 CreateSpeculationRulesPrefetchContainer(document_token,
7849 GURL("https://example.com"),
7850 PreloadingType::kPrefetch);
7851 base::WeakPtr<PreloadingAttempt> attempt2 =
7852 prefetch_container2->preloading_attempt();
7853 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7854 std::move(prefetch_container2));
7855
7856 std::vector<std::pair<GURL, base::WeakPtr<PrefetchContainer>>> prefetches =
7857 GetPrefetchService().GetAllForUrlWithoutRefAndQueryForTesting(
7858 PrefetchContainer::Key(document_token, GURL("https://example.com")));
7859 ASSERT_EQ(1u, prefetches.size());
7860 base::WeakPtr<PrefetchContainer> prefetch_container = prefetches[0].second;
7861
7862 ASSERT_EQ(attempt2.get(), prefetch_container->preloading_attempt().get());
7863}
7864
kenossbd9a3fc2025-03-28 05:15:577865TEST_P(PrefetchServiceAddPrefetchContainerTest,
kenossb68c9e82024-10-10 10:11:157866 PreservesOldIfOldIsAheadOfPrerender) {
7867 blink::DocumentToken document_token;
7868
7869 std::unique_ptr<PrefetchContainer> prefetch_container1 =
7870 CreateSpeculationRulesPrefetchContainer(document_token,
7871 GURL("https://example.com"),
7872 PreloadingType::kPrerender);
7873 base::WeakPtr<PreloadingAttempt> attempt1 =
7874 prefetch_container1->preloading_attempt();
7875 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7876 std::move(prefetch_container1));
7877
7878 std::unique_ptr<PrefetchContainer> prefetch_container2 =
7879 CreateSpeculationRulesPrefetchContainer(document_token,
7880 GURL("https://example.com"),
7881 PreloadingType::kPrefetch);
7882 base::WeakPtr<PreloadingAttempt> attempt2 =
7883 prefetch_container2->preloading_attempt();
7884 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7885 std::move(prefetch_container2));
7886
7887 std::vector<std::pair<GURL, base::WeakPtr<PrefetchContainer>>> prefetches =
7888 GetPrefetchService().GetAllForUrlWithoutRefAndQueryForTesting(
7889 PrefetchContainer::Key(document_token, GURL("https://example.com")));
7890 ASSERT_EQ(1u, prefetches.size());
7891 base::WeakPtr<PrefetchContainer> prefetch_container = prefetches[0].second;
7892
7893 ASSERT_EQ(attempt1.get(), prefetch_container->preloading_attempt().get());
7894}
7895
kenossbd9a3fc2025-03-28 05:15:577896TEST_P(PrefetchServiceAddPrefetchContainerTest,
kenossb68c9e82024-10-10 10:11:157897 ReplacesOldWithNewIfOldIsAheadOfPrerenderAndNotServable) {
7898 blink::DocumentToken document_token;
7899
7900 std::unique_ptr<PrefetchContainer> prefetch_container1 =
7901 CreateSpeculationRulesPrefetchContainer(document_token,
7902 GURL("https://example.com"),
7903 PreloadingType::kPrerender);
Hiroshige Hayashizaki64802cf12025-01-16 23:18:467904 prefetch_container1->SimulatePrefetchFailedIneligibleForTest(
kenossb68c9e82024-10-10 10:11:157905 PreloadingEligibility::kDataSaverEnabled);
7906 base::WeakPtr<PreloadingAttempt> attempt1 =
7907 prefetch_container1->preloading_attempt();
7908 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7909 std::move(prefetch_container1));
7910
7911 std::unique_ptr<PrefetchContainer> prefetch_container2 =
7912 CreateSpeculationRulesPrefetchContainer(document_token,
7913 GURL("https://example.com"),
7914 PreloadingType::kPrefetch);
7915 base::WeakPtr<PreloadingAttempt> attempt2 =
7916 prefetch_container2->preloading_attempt();
7917 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7918 std::move(prefetch_container2));
7919
7920 std::vector<std::pair<GURL, base::WeakPtr<PrefetchContainer>>> prefetches =
7921 GetPrefetchService().GetAllForUrlWithoutRefAndQueryForTesting(
7922 PrefetchContainer::Key(document_token, GURL("https://example.com")));
7923 ASSERT_EQ(1u, prefetches.size());
7924 base::WeakPtr<PrefetchContainer> prefetch_container = prefetches[0].second;
7925
7926 ASSERT_EQ(attempt2.get(), prefetch_container->preloading_attempt().get());
7927}
7928
kenossbd9a3fc2025-03-28 05:15:577929TEST_P(PrefetchServiceAddPrefetchContainerTest,
kenossb68c9e82024-10-10 10:11:157930 TakesOldWithAttributeMigrationIfNewIsAheadOfPrerender) {
7931 blink::DocumentToken document_token;
7932
7933 std::unique_ptr<PrefetchContainer> prefetch_container1 =
7934 CreateSpeculationRulesPrefetchContainer(document_token,
7935 GURL("https://example.com"),
7936 PreloadingType::kPrefetch);
7937 base::WeakPtr<PreloadingAttempt> attempt1 =
7938 prefetch_container1->preloading_attempt();
7939 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7940 std::move(prefetch_container1));
7941
7942 std::unique_ptr<PrefetchContainer> prefetch_container2 =
7943 CreateSpeculationRulesPrefetchContainer(document_token,
7944 GURL("https://example.com"),
7945 PreloadingType::kPrerender);
7946 base::WeakPtr<PreloadingAttempt> attempt2 =
7947 prefetch_container2->preloading_attempt();
7948 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7949 std::move(prefetch_container2));
7950
7951 {
7952 std::vector<std::pair<GURL, base::WeakPtr<PrefetchContainer>>> prefetches =
7953 GetPrefetchService().GetAllForUrlWithoutRefAndQueryForTesting(
7954 PrefetchContainer::Key(document_token,
7955 GURL("https://example.com")));
7956 ASSERT_EQ(1u, prefetches.size());
7957 base::WeakPtr<PrefetchContainer> prefetch_container = prefetches[0].second;
7958
7959 ASSERT_EQ(attempt1.get(), prefetch_container->preloading_attempt().get());
7960 ASSERT_TRUE(prefetch_container->IsLikelyAheadOfPrerender());
7961 }
7962
7963 // `prefetch_container1` now inherits a property `IsLikelyAheadOfPrerender()`.
7964 // So, it wins when yet another one is about to be added.
7965
7966 std::unique_ptr<PrefetchContainer> prefetch_container3 =
7967 CreateSpeculationRulesPrefetchContainer(document_token,
7968 GURL("https://example.com"),
7969 PreloadingType::kPrefetch);
7970 AddPrefetchContainerWithoutStartingPrefetchForTesting(
7971 std::move(prefetch_container3));
7972
7973 {
7974 std::vector<std::pair<GURL, base::WeakPtr<PrefetchContainer>>> prefetches =
7975 GetPrefetchService().GetAllForUrlWithoutRefAndQueryForTesting(
7976 PrefetchContainer::Key(document_token,
7977 GURL("https://example.com")));
7978 ASSERT_EQ(1u, prefetches.size());
7979 base::WeakPtr<PrefetchContainer> prefetch_container = prefetches[0].second;
7980
7981 ASSERT_EQ(attempt1.get(), prefetch_container->preloading_attempt().get());
7982 }
7983}
kenosseab3c422025-04-03 12:50:197984
kenoss1b8ebc22025-04-04 00:11:357985// Tests behavior of concurrent prefetches.
7986//
7987// Scenario:
7988//
7989// - Three prefetch are triggered.
7990// - `PrefetchScheduler` starts the two of them.
7991// - The second one ended.
7992// - `PrefetchScheduler` starts the last one.
7993TEST_P(PrefetchServiceTest, PrefetchScheduler_RunsTwoConcurrentPrefetches) {
7994 if (!UsePrefetchScheduler()) {
7995 GTEST_SKIP() << "Assume PrefetchScheduler";
7996 }
7997
7998 base::test::ScopedFeatureList scoped_feature_list;
7999 scoped_feature_list.InitWithFeaturesAndParameters(
8000 {{features::kPrefetchSchedulerTesting,
8001 {{"kPrefetchSchedulerTestingActiveSetSizeLimitForBase", "2"},
8002 {"kPrefetchSchedulerTestingActiveSetSizeLimitForBurst", "2"}}}},
8003 {});
8004
8005 NavigateAndCommit(GURL("https://example.com"));
8006 MakePrefetchService(
8007 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
8008 /*num_on_prefetch_likely_calls=*/0));
8009 PrefetchService* prefetch_service =
8010 BrowserContextImpl::From(browser_context())->GetPrefetchService();
8011
8012 const auto url_1 = GURL("https://example.com/one");
8013 const auto url_2 = GURL("https://example.com/two");
8014 const auto url_3 = GURL("https://example.com/three");
8015 auto handle_1 =
8016 MakePrefetchFromBrowserContext(url_1, std::nullopt, {}, nullptr);
8017 auto handle_2 =
8018 MakePrefetchFromBrowserContext(url_2, std::nullopt, {}, nullptr);
8019 auto handle_3 =
8020 MakePrefetchFromBrowserContext(url_3, std::nullopt, {}, nullptr);
8021 task_environment()->RunUntilIdle();
8022
8023 base::WeakPtr<PrefetchContainer> prefetch_container1, prefetch_container2,
8024 prefetch_container3;
8025 std::tie(std::ignore, prefetch_container1) =
8026 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8027 PrefetchContainer::Key(std::nullopt, url_1))[0];
8028 std::tie(std::ignore, prefetch_container2) =
8029 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8030 PrefetchContainer::Key(std::nullopt, url_2))[0];
8031 std::tie(std::ignore, prefetch_container3) =
8032 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8033 PrefetchContainer::Key(std::nullopt, url_3))[0];
8034
8035 ASSERT_EQ(prefetch_container1->GetLoadState(),
8036 PrefetchContainer::LoadState::kStarted);
8037 ASSERT_EQ(prefetch_container2->GetLoadState(),
8038 PrefetchContainer::LoadState::kStarted);
8039 ASSERT_EQ(prefetch_container3->GetLoadState(),
8040 PrefetchContainer::LoadState::kEligible);
8041
8042 handle_2.reset();
8043 EXPECT_FALSE(prefetch_container2);
8044 // Resolve `PrefetchScheduler::ProgressAsync()`.
8045 task_environment()->RunUntilIdle();
8046
8047 ASSERT_EQ(prefetch_container1->GetLoadState(),
8048 PrefetchContainer::LoadState::kStarted);
8049 ASSERT_EQ(prefetch_container3->GetLoadState(),
8050 PrefetchContainer::LoadState::kStarted);
8051}
8052
8053// Tests prioritizing behavior.
8054//
8055// Scenario:
8056//
kenoss0a567362025-05-16 11:18:558057// - Two prefetches are triggered.
kenoss1b8ebc22025-04-04 00:11:358058// - A prefetch is triggered with high priority.
8059// - `PrefetchScheduler` starts the later one.
8060TEST_P(PrefetchServiceTest, PrefetchScheduler_Prioritize) {
kenoss0a567362025-05-16 11:18:558061 if (!(UsePrefetchScheduler() &&
8062 features::kPrefetchSchedulerProgressSyncBestEffort.Get())) {
8063 GTEST_SKIP() << "Assume PrefetchScheduler and "
8064 "PrefetchSchedulerProgressSyncBestEffort";
8065 }
8066
8067 base::test::ScopedFeatureList scoped_feature_list;
8068 scoped_feature_list.InitWithFeaturesAndParameters(
8069 {{features::kPrefetchSchedulerTesting,
8070 {{"kPrefetchSchedulerTestingActiveSetSizeLimitForBase", "1"},
8071 {"kPrefetchSchedulerTestingActiveSetSizeLimitForBurst", "1"}}}},
8072 {});
8073
8074 NavigateAndCommit(GURL("https://example.com"));
8075 MakePrefetchService(
8076 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
8077 /*num_on_prefetch_likely_calls=*/0));
8078 PrefetchService* prefetch_service =
8079 BrowserContextImpl::From(browser_context())->GetPrefetchService();
8080
8081 prefetch_service->GetPrefetchSchedulerForTesting()
8082 .SetCalculatePriorityForTesting(
8083 base::BindRepeating([](const PrefetchContainer& prefetch_container) {
8084 if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
8085 "?prioritize=1")) {
Taiyo Mizuhashid7c856992025-06-23 08:56:028086 return PrefetchSchedulerPriority::kHighTest;
kenoss0a567362025-05-16 11:18:558087 }
8088
Taiyo Mizuhashid7c856992025-06-23 08:56:028089 return PrefetchSchedulerPriority::kBase;
kenoss0a567362025-05-16 11:18:558090 }));
8091
8092 const auto url_1 = GURL("https://example.com/one");
8093 const auto url_2 = GURL("https://example.com/two");
8094 const auto url_3 = GURL("https://example.com/two?prioritize=1");
8095 auto handle_1 =
8096 MakePrefetchFromBrowserContext(url_1, std::nullopt, {}, nullptr);
8097 auto handle_2 =
8098 MakePrefetchFromBrowserContext(url_2, std::nullopt, {}, nullptr);
8099 auto handle_3 =
8100 MakePrefetchFromBrowserContext(url_3, std::nullopt, {}, nullptr);
8101 task_environment()->RunUntilIdle();
8102
8103 base::WeakPtr<PrefetchContainer> prefetch_container1, prefetch_container2,
8104 prefetch_container3;
8105 std::tie(std::ignore, prefetch_container1) =
8106 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8107 PrefetchContainer::Key(std::nullopt, url_1))[0];
8108 std::tie(std::ignore, prefetch_container2) =
8109 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8110 PrefetchContainer::Key(std::nullopt, url_2))[0];
8111 std::tie(std::ignore, prefetch_container3) =
8112 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8113 PrefetchContainer::Key(std::nullopt, url_3))[0];
8114
8115 ASSERT_EQ(prefetch_container1->GetLoadState(),
8116 PrefetchContainer::LoadState::kStarted);
8117 ASSERT_EQ(prefetch_container2->GetLoadState(),
8118 PrefetchContainer::LoadState::kEligible);
8119 ASSERT_EQ(prefetch_container3->GetLoadState(),
8120 PrefetchContainer::LoadState::kEligible);
8121
8122 handle_1.reset();
8123 EXPECT_FALSE(prefetch_container1);
8124 // Resolve `PrefetchScheduler::ProgressAsync()`.
8125 task_environment()->RunUntilIdle();
8126
8127 ASSERT_EQ(prefetch_container2->GetLoadState(),
8128 PrefetchContainer::LoadState::kEligible);
8129 ASSERT_EQ(prefetch_container3->GetLoadState(),
8130 PrefetchContainer::LoadState::kStarted);
8131}
8132
8133// Tests prioritizing behavior.
8134//
8135// Scenario:
8136//
8137// - A prefetch is triggered.
8138// - A prefetch is triggered with high priority.
8139// - `PrefetchScheduler` starts the later one.
8140TEST_P(PrefetchServiceTest, PrefetchScheduler_Prioritize_Async) {
8141 if (!(UsePrefetchScheduler() &&
8142 !features::kPrefetchSchedulerProgressSyncBestEffort.Get())) {
8143 GTEST_SKIP() << "Assume PrefetchScheduler and not "
8144 "PrefetchSchedulerProgressSyncBestEffort";
kenoss1b8ebc22025-04-04 00:11:358145 }
8146
8147 base::test::ScopedFeatureList scoped_feature_list;
8148 scoped_feature_list.InitWithFeaturesAndParameters(
8149 {{features::kPrefetchSchedulerTesting,
8150 {{"kPrefetchSchedulerTestingActiveSetSizeLimitForBase", "1"},
8151 {"kPrefetchSchedulerTestingActiveSetSizeLimitForBurst", "1"}}}},
8152 {});
8153
8154 NavigateAndCommit(GURL("https://example.com"));
8155 MakePrefetchService(
8156 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
8157 /*num_on_prefetch_likely_calls=*/0));
8158 PrefetchService* prefetch_service =
8159 BrowserContextImpl::From(browser_context())->GetPrefetchService();
8160
8161 prefetch_service->GetPrefetchSchedulerForTesting()
8162 .SetCalculatePriorityForTesting(
8163 base::BindRepeating([](const PrefetchContainer& prefetch_container) {
8164 if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
8165 "?prioritize=1")) {
Taiyo Mizuhashid7c856992025-06-23 08:56:028166 return PrefetchSchedulerPriority::kHighTest;
kenoss1b8ebc22025-04-04 00:11:358167 }
8168
Taiyo Mizuhashid7c856992025-06-23 08:56:028169 return PrefetchSchedulerPriority::kBase;
kenoss1b8ebc22025-04-04 00:11:358170 }));
8171
8172 const auto url_1 = GURL("https://example.com/one");
8173 const auto url_2 = GURL("https://example.com/two?prioritize=1");
8174 auto handle_1 =
8175 MakePrefetchFromBrowserContext(url_1, std::nullopt, {}, nullptr);
8176 auto handle_2 =
8177 MakePrefetchFromBrowserContext(url_2, std::nullopt, {}, nullptr);
8178 task_environment()->RunUntilIdle();
8179
8180 base::WeakPtr<PrefetchContainer> prefetch_container1, prefetch_container2;
8181 std::tie(std::ignore, prefetch_container1) =
8182 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8183 PrefetchContainer::Key(std::nullopt, url_1))[0];
8184 std::tie(std::ignore, prefetch_container2) =
8185 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8186 PrefetchContainer::Key(std::nullopt, url_2))[0];
8187
8188 ASSERT_EQ(prefetch_container1->GetLoadState(),
8189 PrefetchContainer::LoadState::kEligible);
8190 ASSERT_EQ(prefetch_container2->GetLoadState(),
8191 PrefetchContainer::LoadState::kStarted);
8192}
8193
8194// Tests bursting behavior.
8195//
8196// Scenario:
8197//
8198// - Two prefetches are triggered.
8199// - `PrefetchScheduler` starts the first one.
8200// - Two prefetches are triggered with burst.
8201// - `PrefetchScheduler` starts the third one.
8202// - The third one ended.
8203// - `PrefetchScheduler` starts the fourth one.
8204TEST_P(PrefetchServiceTest, PrefetchScheduler_Burst) {
8205 if (!UsePrefetchScheduler()) {
8206 GTEST_SKIP() << "Assume PrefetchScheduler";
8207 }
8208
8209 base::test::ScopedFeatureList scoped_feature_list;
8210 scoped_feature_list.InitWithFeaturesAndParameters(
8211 {
8212 {features::kPrefetchSchedulerTesting,
8213 {{"kPrefetchSchedulerTestingActiveSetSizeLimitForBase", "1"},
8214 {"kPrefetchSchedulerTestingActiveSetSizeLimitForBurst", "2"}}},
8215 },
8216 {});
8217
8218 NavigateAndCommit(GURL("https://example.com"));
8219 MakePrefetchService(
8220 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
8221 /*num_on_prefetch_likely_calls=*/0));
8222 PrefetchService* prefetch_service =
8223 BrowserContextImpl::From(browser_context())->GetPrefetchService();
8224
8225 prefetch_service->GetPrefetchSchedulerForTesting()
8226 .SetCalculatePriorityForTesting(
8227 base::BindRepeating([](const PrefetchContainer& prefetch_container) {
8228 if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
8229 "?burst=1")) {
Taiyo Mizuhashid7c856992025-06-23 08:56:028230 return PrefetchSchedulerPriority::kBurstTest;
kenoss1b8ebc22025-04-04 00:11:358231 }
8232
Taiyo Mizuhashid7c856992025-06-23 08:56:028233 return PrefetchSchedulerPriority::kBase;
kenoss1b8ebc22025-04-04 00:11:358234 }));
8235
8236 const auto url_1 = GURL("https://example.com/one");
8237 const auto url_2 = GURL("https://example.com/two");
8238 const auto url_3 = GURL("https://example.com/three?burst=1");
8239 const auto url_4 = GURL("https://example.com/four?burst=1");
8240 auto handle_1 =
8241 MakePrefetchFromBrowserContext(url_1, std::nullopt, {}, nullptr);
8242 auto handle_2 =
8243 MakePrefetchFromBrowserContext(url_2, std::nullopt, {}, nullptr);
8244 task_environment()->RunUntilIdle();
8245
8246 base::WeakPtr<PrefetchContainer> prefetch_container1, prefetch_container2,
8247 prefetch_container3, prefetch_container4;
8248 std::tie(std::ignore, prefetch_container1) =
8249 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8250 PrefetchContainer::Key(std::nullopt, url_1))[0];
8251 std::tie(std::ignore, prefetch_container2) =
8252 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8253 PrefetchContainer::Key(std::nullopt, url_2))[0];
8254
8255 ASSERT_EQ(prefetch_container1->GetLoadState(),
8256 PrefetchContainer::LoadState::kStarted);
8257 ASSERT_EQ(prefetch_container2->GetLoadState(),
8258 PrefetchContainer::LoadState::kEligible);
8259
8260 auto handle_3 =
8261 MakePrefetchFromBrowserContext(url_3, std::nullopt, {}, nullptr);
8262 auto handle_4 =
8263 MakePrefetchFromBrowserContext(url_4, std::nullopt, {}, nullptr);
8264 task_environment()->RunUntilIdle();
8265
8266 std::tie(std::ignore, prefetch_container3) =
8267 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8268 PrefetchContainer::Key(std::nullopt, url_3))[0];
8269 std::tie(std::ignore, prefetch_container4) =
8270 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8271 PrefetchContainer::Key(std::nullopt, url_4))[0];
8272
8273 ASSERT_EQ(prefetch_container1->GetLoadState(),
8274 PrefetchContainer::LoadState::kStarted);
8275 ASSERT_EQ(prefetch_container2->GetLoadState(),
8276 PrefetchContainer::LoadState::kEligible);
8277 ASSERT_EQ(prefetch_container3->GetLoadState(),
8278 PrefetchContainer::LoadState::kStarted);
8279 ASSERT_EQ(prefetch_container4->GetLoadState(),
8280 PrefetchContainer::LoadState::kEligible);
8281
8282 handle_3.reset();
8283 EXPECT_FALSE(prefetch_container3);
8284 // Resolve `PrefetchScheduler::ProgressAsync()`.
8285 task_environment()->RunUntilIdle();
8286
8287 ASSERT_EQ(prefetch_container1->GetLoadState(),
8288 PrefetchContainer::LoadState::kStarted);
8289 ASSERT_EQ(prefetch_container2->GetLoadState(),
8290 PrefetchContainer::LoadState::kEligible);
8291 ASSERT_EQ(prefetch_container4->GetLoadState(),
8292 PrefetchContainer::LoadState::kStarted);
8293}
8294
8295// Tests bursting behavior.
8296//
8297// Scenario:
8298//
8299// - Two prefetches are triggered.
8300// - Two prefetches are triggered with burst.
kenoss0a567362025-05-16 11:18:558301// - `PrefetchScheduler` starts the first and third one.
8302// - The third one ended.
8303// - `PrefetchScheduler` starts the forth one.
8304// - The first one ended.
8305// - `PrefetchScheduler` doesn't start the second one as
8306// `ActiveSetSizeLimitForBase` is 1.
8307TEST_P(PrefetchServiceTest, PrefetchScheduler_BurstTakesPriority) {
8308 if (!(UsePrefetchScheduler() &&
8309 features::kPrefetchSchedulerProgressSyncBestEffort.Get())) {
8310 GTEST_SKIP() << "Assume PrefetchScheduler and "
8311 "PrefetchSchedulerProgressSyncBestEffort";
8312 }
8313
8314 base::test::ScopedFeatureList scoped_feature_list;
8315 scoped_feature_list.InitWithFeaturesAndParameters(
8316 {
8317 {features::kPrefetchSchedulerTesting,
8318 {{"kPrefetchSchedulerTestingActiveSetSizeLimitForBase", "1"},
8319 {"kPrefetchSchedulerTestingActiveSetSizeLimitForBurst", "2"}}},
8320 },
8321 {});
8322
8323 NavigateAndCommit(GURL("https://example.com"));
8324 MakePrefetchService(
8325 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
8326 /*num_on_prefetch_likely_calls=*/0));
8327 PrefetchService* prefetch_service =
8328 BrowserContextImpl::From(browser_context())->GetPrefetchService();
8329
8330 prefetch_service->GetPrefetchSchedulerForTesting()
8331 .SetCalculatePriorityForTesting(
8332 base::BindRepeating([](const PrefetchContainer& prefetch_container) {
8333 if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
8334 "?burst=1")) {
Taiyo Mizuhashid7c856992025-06-23 08:56:028335 return PrefetchSchedulerPriority::kBurstTest;
kenoss0a567362025-05-16 11:18:558336 }
8337
Taiyo Mizuhashid7c856992025-06-23 08:56:028338 return PrefetchSchedulerPriority::kBase;
kenoss0a567362025-05-16 11:18:558339 }));
8340
8341 const auto url_1 = GURL("https://example.com/one");
8342 const auto url_2 = GURL("https://example.com/two");
8343 const auto url_3 = GURL("https://example.com/three?burst=1");
8344 const auto url_4 = GURL("https://example.com/four?burst=1");
8345 auto handle_1 =
8346 MakePrefetchFromBrowserContext(url_1, std::nullopt, {}, nullptr);
8347 auto handle_2 =
8348 MakePrefetchFromBrowserContext(url_2, std::nullopt, {}, nullptr);
8349 auto handle_3 =
8350 MakePrefetchFromBrowserContext(url_3, std::nullopt, {}, nullptr);
8351 auto handle_4 =
8352 MakePrefetchFromBrowserContext(url_4, std::nullopt, {}, nullptr);
8353 task_environment()->RunUntilIdle();
8354
8355 base::WeakPtr<PrefetchContainer> prefetch_container1, prefetch_container2,
8356 prefetch_container3, prefetch_container4;
8357 std::tie(std::ignore, prefetch_container1) =
8358 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8359 PrefetchContainer::Key(std::nullopt, url_1))[0];
8360 std::tie(std::ignore, prefetch_container2) =
8361 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8362 PrefetchContainer::Key(std::nullopt, url_2))[0];
8363 std::tie(std::ignore, prefetch_container3) =
8364 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8365 PrefetchContainer::Key(std::nullopt, url_3))[0];
8366 std::tie(std::ignore, prefetch_container4) =
8367 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8368 PrefetchContainer::Key(std::nullopt, url_4))[0];
8369
8370 ASSERT_EQ(prefetch_container1->GetLoadState(),
8371 PrefetchContainer::LoadState::kStarted);
8372 ASSERT_EQ(prefetch_container2->GetLoadState(),
8373 PrefetchContainer::LoadState::kEligible);
8374 ASSERT_EQ(prefetch_container3->GetLoadState(),
8375 PrefetchContainer::LoadState::kStarted);
8376 ASSERT_EQ(prefetch_container4->GetLoadState(),
8377 PrefetchContainer::LoadState::kEligible);
8378
8379 handle_3.reset();
8380 EXPECT_FALSE(prefetch_container3);
8381 // Resolve `PrefetchScheduler::ProgressAsync()`.
8382 task_environment()->RunUntilIdle();
8383
8384 ASSERT_EQ(prefetch_container1->GetLoadState(),
8385 PrefetchContainer::LoadState::kStarted);
8386 ASSERT_EQ(prefetch_container2->GetLoadState(),
8387 PrefetchContainer::LoadState::kEligible);
8388 ASSERT_EQ(prefetch_container4->GetLoadState(),
8389 PrefetchContainer::LoadState::kStarted);
8390
8391 handle_1.reset();
8392 EXPECT_FALSE(prefetch_container1);
8393 // Resolve `PrefetchScheduler::ProgressAsync()`.
8394 task_environment()->RunUntilIdle();
8395
8396 ASSERT_EQ(prefetch_container2->GetLoadState(),
8397 PrefetchContainer::LoadState::kEligible);
8398 ASSERT_EQ(prefetch_container4->GetLoadState(),
8399 PrefetchContainer::LoadState::kStarted);
8400}
8401
8402// Tests bursting behavior.
8403//
8404// Scenario:
8405//
8406// - Two prefetches are triggered.
8407// - Two prefetches are triggered with burst.
kenoss1b8ebc22025-04-04 00:11:358408// - `PrefetchScheduler` starts the third and fourth one.
8409// - The third one ended.
8410// - `PrefetchScheduler` doesn't start the first/second one as
8411// `ActiveSetSizeLimitForBase` is 1.
kenoss0a567362025-05-16 11:18:558412TEST_P(PrefetchServiceTest, PrefetchScheduler_BurstTakesPriority_Async) {
8413 if (!(UsePrefetchScheduler() &&
8414 !features::kPrefetchSchedulerProgressSyncBestEffort.Get())) {
8415 GTEST_SKIP() << "Assume PrefetchScheduler and not "
8416 "PrefetchSchedulerProgressSyncBestEffort";
kenoss1b8ebc22025-04-04 00:11:358417 }
8418
8419 base::test::ScopedFeatureList scoped_feature_list;
8420 scoped_feature_list.InitWithFeaturesAndParameters(
8421 {
8422 {features::kPrefetchSchedulerTesting,
8423 {{"kPrefetchSchedulerTestingActiveSetSizeLimitForBase", "1"},
8424 {"kPrefetchSchedulerTestingActiveSetSizeLimitForBurst", "2"}}},
8425 },
8426 {});
8427
8428 NavigateAndCommit(GURL("https://example.com"));
8429 MakePrefetchService(
8430 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
8431 /*num_on_prefetch_likely_calls=*/0));
8432 PrefetchService* prefetch_service =
8433 BrowserContextImpl::From(browser_context())->GetPrefetchService();
8434
8435 prefetch_service->GetPrefetchSchedulerForTesting()
8436 .SetCalculatePriorityForTesting(
8437 base::BindRepeating([](const PrefetchContainer& prefetch_container) {
8438 if (prefetch_container.GetURL().possibly_invalid_spec().ends_with(
8439 "?burst=1")) {
Taiyo Mizuhashid7c856992025-06-23 08:56:028440 return PrefetchSchedulerPriority::kBurstTest;
kenoss1b8ebc22025-04-04 00:11:358441 }
8442
Taiyo Mizuhashid7c856992025-06-23 08:56:028443 return PrefetchSchedulerPriority::kBase;
kenoss1b8ebc22025-04-04 00:11:358444 }));
8445
8446 const auto url_1 = GURL("https://example.com/one");
8447 const auto url_2 = GURL("https://example.com/two");
8448 const auto url_3 = GURL("https://example.com/three?burst=1");
8449 const auto url_4 = GURL("https://example.com/four?burst=1");
8450 auto handle_1 =
8451 MakePrefetchFromBrowserContext(url_1, std::nullopt, {}, nullptr);
8452 auto handle_2 =
8453 MakePrefetchFromBrowserContext(url_2, std::nullopt, {}, nullptr);
8454 auto handle_3 =
8455 MakePrefetchFromBrowserContext(url_3, std::nullopt, {}, nullptr);
8456 auto handle_4 =
8457 MakePrefetchFromBrowserContext(url_4, std::nullopt, {}, nullptr);
8458 task_environment()->RunUntilIdle();
8459
8460 base::WeakPtr<PrefetchContainer> prefetch_container1, prefetch_container2,
8461 prefetch_container3, prefetch_container4;
8462 std::tie(std::ignore, prefetch_container1) =
8463 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8464 PrefetchContainer::Key(std::nullopt, url_1))[0];
8465 std::tie(std::ignore, prefetch_container2) =
8466 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8467 PrefetchContainer::Key(std::nullopt, url_2))[0];
8468 std::tie(std::ignore, prefetch_container3) =
8469 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8470 PrefetchContainer::Key(std::nullopt, url_3))[0];
8471 std::tie(std::ignore, prefetch_container4) =
8472 prefetch_service->GetAllForUrlWithoutRefAndQueryForTesting(
8473 PrefetchContainer::Key(std::nullopt, url_4))[0];
8474
8475 ASSERT_EQ(prefetch_container1->GetLoadState(),
8476 PrefetchContainer::LoadState::kEligible);
8477 ASSERT_EQ(prefetch_container2->GetLoadState(),
8478 PrefetchContainer::LoadState::kEligible);
8479 ASSERT_EQ(prefetch_container3->GetLoadState(),
8480 PrefetchContainer::LoadState::kStarted);
8481 ASSERT_EQ(prefetch_container4->GetLoadState(),
8482 PrefetchContainer::LoadState::kStarted);
8483
8484 handle_3.reset();
8485 EXPECT_FALSE(prefetch_container3);
8486 // Resolve `PrefetchScheduler::ProgressAsync()`.
8487 task_environment()->RunUntilIdle();
8488
8489 ASSERT_EQ(prefetch_container1->GetLoadState(),
8490 PrefetchContainer::LoadState::kEligible);
8491 ASSERT_EQ(prefetch_container2->GetLoadState(),
8492 PrefetchContainer::LoadState::kEligible);
8493 ASSERT_EQ(prefetch_container4->GetLoadState(),
8494 PrefetchContainer::LoadState::kStarted);
8495}
8496
kenosseab3c422025-04-03 12:50:198497TEST_P(PrefetchServiceTest,
8498 UMA_Prefetch_PrefetchContainer_AddedTo_Embedder_Success) {
8499 base::HistogramTester histogram_tester;
8500
8501 NavigateAndCommit(GURL("https://example.com"));
8502 MakePrefetchService(
8503 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
8504 /*num_on_prefetch_likely_calls=*/0));
8505
8506 const auto url = GURL("https://example.com/prefetched");
8507 auto handle = MakePrefetchFromBrowserContext(url, std::nullopt, {}, nullptr);
8508 task_environment()->RunUntilIdle();
8509
Taiyo Mizuhashi98ecb382025-06-03 15:24:558510 task_environment()->FastForwardBy(
8511 base::Milliseconds(kAddedToURLRequestStartLatency + kHeaderLatency));
kenosseab3c422025-04-03 12:50:198512
8513 MakeResponseAndWait(net::HTTP_OK, net::OK, kHTMLMimeType,
8514 /*use_prefetch_proxy=*/false,
8515 {{"X-Testing", "Hello World"}}, kHTMLBody);
8516
8517 // Call `PrefetchContainer::dtor()` to record UMAs.
8518 handle.reset();
8519
8520 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashia12acc42025-04-25 23:40:368521 base::StrCat(
8522 {"Prefetch.PrefetchContainer.AddedToInitialEligibility.Embedder_",
8523 test::kPreloadingEmbedderHistgramSuffixForTesting}),
kenosseab3c422025-04-03 12:50:198524 0, 1);
8525 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashia12acc42025-04-25 23:40:368526 base::StrCat(
8527 {"Prefetch.PrefetchContainer.AddedToPrefetchStarted.Embedder_",
8528 test::kPreloadingEmbedderHistgramSuffixForTesting}),
kenosseab3c422025-04-03 12:50:198529 0, 1);
8530 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashi98ecb382025-06-03 15:24:558531 base::StrCat(
8532 {"Prefetch.PrefetchContainer.AddedToURLRequestStarted.Embedder_",
8533 test::kPreloadingEmbedderHistgramSuffixForTesting}),
8534 kAddedToURLRequestStartLatency, 1);
8535 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashia12acc42025-04-25 23:40:368536 base::StrCat({"Prefetch.PrefetchContainer."
8537 "AddedToHeaderDeterminedSuccessfully.Embedder_",
8538 test::kPreloadingEmbedderHistgramSuffixForTesting}),
Taiyo Mizuhashi98ecb382025-06-03 15:24:558539 kAddedToURLRequestStartLatency + kHeaderLatency, 1);
kenosseab3c422025-04-03 12:50:198540 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashia12acc42025-04-25 23:40:368541 base::StrCat({"Prefetch.PrefetchContainer."
8542 "AddedToPrefetchCompletedSuccessfully.Embedder_",
8543 test::kPreloadingEmbedderHistgramSuffixForTesting}),
Taiyo Mizuhashi98ecb382025-06-03 15:24:558544 kAddedToURLRequestStartLatency + kHeaderLatency, 1);
kenosseab3c422025-04-03 12:50:198545}
8546
8547TEST_P(PrefetchServiceTest,
8548 UMA_Prefetch_PrefetchContainer_AddedTo_Embedder_Fail) {
8549 base::HistogramTester histogram_tester;
8550
8551 NavigateAndCommit(GURL("https://example.com"));
8552 MakePrefetchService(
8553 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>(
8554 /*num_on_prefetch_likely_calls=*/0));
8555
8556 const auto url = GURL("https://example.com/prefetched");
8557 auto handle = MakePrefetchFromBrowserContext(url, std::nullopt, {}, nullptr);
8558 task_environment()->RunUntilIdle();
8559
Taiyo Mizuhashi98ecb382025-06-03 15:24:558560 task_environment()->FastForwardBy(
8561 base::Milliseconds(kAddedToURLRequestStartLatency + kHeaderLatency));
kenosseab3c422025-04-03 12:50:198562
8563 // Call `PrefetchContainer::dtor()` to record UMAs.
8564 handle.reset();
8565
8566 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashia12acc42025-04-25 23:40:368567 base::StrCat(
8568 {"Prefetch.PrefetchContainer.AddedToInitialEligibility.Embedder_",
8569 test::kPreloadingEmbedderHistgramSuffixForTesting}),
kenosseab3c422025-04-03 12:50:198570 0, 1);
8571 histogram_tester.ExpectUniqueSample(
Taiyo Mizuhashia12acc42025-04-25 23:40:368572 base::StrCat(
8573 {"Prefetch.PrefetchContainer.AddedToPrefetchStarted.Embedder_",
8574 test::kPreloadingEmbedderHistgramSuffixForTesting}),
kenosseab3c422025-04-03 12:50:198575 0, 1);
8576 histogram_tester.ExpectTotalCount(
Taiyo Mizuhashi98ecb382025-06-03 15:24:558577 base::StrCat(
8578 {"Prefetch.PrefetchContainer.AddedToURLRequestStarted.Embedder_",
8579 test::kPreloadingEmbedderHistgramSuffixForTesting}),
8580 0);
8581 histogram_tester.ExpectTotalCount(
Taiyo Mizuhashia12acc42025-04-25 23:40:368582 base::StrCat({"Prefetch.PrefetchContainer."
8583 "AddedToHeaderDeterminedSuccesfully.Embedder_",
8584 test::kPreloadingEmbedderHistgramSuffixForTesting}),
kenosseab3c422025-04-03 12:50:198585 0);
8586 histogram_tester.ExpectTotalCount(
Taiyo Mizuhashia12acc42025-04-25 23:40:368587 base::StrCat({"Prefetch.PrefetchContainer."
8588 "AddedToPrefetchCompletedSuccessfully.Embedder_",
8589 test::kPreloadingEmbedderHistgramSuffixForTesting}),
kenosseab3c422025-04-03 12:50:198590 0);
8591}
8592
kenoss4bdf2d82025-05-27 01:58:458593TEST_P(
8594 PrefetchServiceTest,
8595 UMA_Prefetch_PrefetchMatchingBlockedNavigation_And_BlockUntilHeadDuration_PrerenderOrNonPrerender) {
8596 base::HistogramTester histogram_tester;
8597
8598 MakePrefetchService(
8599 std::make_unique<testing::NiceMock<MockPrefetchServiceDelegate>>());
8600
Takashi Nakayama978f0a152025-06-17 08:26:258601 const PrefetchType prefetch_type =
8602 PrefetchType(PreloadingTriggerType::kSpeculationRule,
8603 /*use_prefetch_proxy=*/true,
8604 blink::mojom::SpeculationEagerness::kImmediate);
kenoss4bdf2d82025-05-27 01:58:458605 MakePrefetchOnMainFrame(GURL("https://example.com"), prefetch_type);
8606 task_environment()->RunUntilIdle();
8607
8608 // Request the prefetch from the PrefetchService. The given callback shouldn't
8609 // be called until after the head is received.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:578610 base::test::TestFuture<PrefetchServingHandle> future;
kenoss4bdf2d82025-05-27 01:58:458611 GetPrefetchToServe(future, GURL("https://example.com"), MainDocumentToken());
8612 EXPECT_FALSE(future.IsReady());
8613
8614 task_environment()->FastForwardBy(base::Milliseconds(kHeaderLatency));
8615
8616 // Sends the head of the prefetch response. This should trigger the above
8617 // callback.
8618 SendHeadOfResponseAndWait(net::HTTP_OK, kHTMLMimeType,
8619 /*use_prefetch_proxy=*/true,
8620 {{"X-Testing", "Hello World"}},
8621 std::size(kHTMLBody));
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:578622 PrefetchServingHandle serving_handle = future.Take();
8623 ASSERT_TRUE(serving_handle);
kenoss4bdf2d82025-05-27 01:58:458624
8625 // Send the body and completion status of the request,
8626 SendBodyContentOfResponseAndWait(kHTMLBody);
8627 CompleteResponseAndWait(net::OK, std::size(kHTMLBody));
8628
8629 histogram_tester.ExpectUniqueSample(
8630 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
Takashi Nakayamac8183502025-08-04 14:18:508631 "SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458632 true, 1);
8633 histogram_tester.ExpectUniqueSample(
8634 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
Takashi Nakayamac8183502025-08-04 14:18:508635 "NonPrerender.SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458636 true, 1);
8637 histogram_tester.ExpectTotalCount(
8638 "Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate."
Takashi Nakayamac8183502025-08-04 14:18:508639 "Prerender.SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458640 0);
8641 histogram_tester.ExpectUniqueTimeSample(
8642 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Served."
Takashi Nakayamac8183502025-08-04 14:18:508643 "SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458644 base::Milliseconds(kHeaderLatency), 1);
8645 histogram_tester.ExpectTotalCount(
8646 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NotServed."
Takashi Nakayamac8183502025-08-04 14:18:508647 "SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458648 0);
8649 histogram_tester.ExpectUniqueTimeSample(
8650 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NonPrerender."
Takashi Nakayamac8183502025-08-04 14:18:508651 "Served.SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458652 base::Milliseconds(kHeaderLatency), 1);
8653 histogram_tester.ExpectTotalCount(
8654 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.NonPrerender."
Takashi Nakayamac8183502025-08-04 14:18:508655 "NotServed.SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458656 0);
8657 histogram_tester.ExpectTotalCount(
8658 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Prerender.Served."
Takashi Nakayamac8183502025-08-04 14:18:508659 "SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458660 0);
8661 histogram_tester.ExpectTotalCount(
8662 "Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.Prerender."
Takashi Nakayamac8183502025-08-04 14:18:508663 "NotServed.SpeculationRule_Immediate2",
kenoss4bdf2d82025-05-27 01:58:458664 0);
8665}
8666
Max Curran6c2835ea2022-03-07 19:52:388667} // namespace
8668} // namespace content