blob: 547d969b39062833a80bf9f56a07ccd6e1eee542 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2022 The Chromium Authors
Max Curran646fb642022-03-16 00:44:092// 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_container.h"
Max Curran646fb642022-03-16 00:44:096
kenoss081c9d92024-11-13 10:21:577#include "base/debug/crash_logging.h"
David Sanders1b28c242025-01-27 00:46:538#include "base/debug/dump_without_crashing.h"
Max Curran983f21a52023-03-23 23:52:589#include "base/metrics/histogram_functions.h"
Max Curran679a3d52022-07-11 23:03:3110#include "base/metrics/histogram_macros.h"
Daniel Cheng4f1c5332025-06-19 19:37:2111#include "base/notimplemented.h"
William Liu77089052022-12-15 18:53:3512#include "base/notreached.h"
kenosscc25ed12024-11-14 09:20:3713#include "base/strings/strcat.h"
Max Curranc4445fc2022-06-02 18:43:4314#include "base/time/time.h"
Liviu Tinta2e1ffe22024-06-21 21:01:3315#include "components/variations/net/variations_http_headers.h"
Robert Lin233948df2023-02-10 01:37:3916#include "content/browser/devtools/devtools_instrumentation.h"
Hiroshige Hayashizaki583b4962025-08-19 18:58:2617#include "content/browser/devtools/network_service_devtools_observer.h"
Hiroshige Hayashizakieb3867c2024-12-26 07:30:3018#include "content/browser/loader/navigation_url_loader_impl.h"
Max Curran5d4da4b42023-03-10 23:41:4619#include "content/browser/preloading/prefetch/no_vary_search_helper.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2720#include "content/browser/preloading/prefetch/prefetch_cookie_listener.h"
21#include "content/browser/preloading/prefetch/prefetch_document_manager.h"
Hiroshige Hayashizaki1919c7c2023-10-13 21:46:4222#include "content/browser/preloading/prefetch/prefetch_features.h"
kenossf6fbd5652024-09-04 00:40:2823#include "content/browser/preloading/prefetch/prefetch_match_resolver.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2724#include "content/browser/preloading/prefetch/prefetch_network_context.h"
Max Curran892ca5422022-12-12 20:55:3425#include "content/browser/preloading/prefetch/prefetch_params.h"
Hiroshige Hayashizakif55280f62025-08-19 17:23:2026#include "content/browser/preloading/prefetch/prefetch_request.h"
Hiroshige Hayashizakibf37abc32023-09-13 10:02:3627#include "content/browser/preloading/prefetch/prefetch_response_reader.h"
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:0828#include "content/browser/preloading/prefetch/prefetch_servable_state.h"
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:5729#include "content/browser/preloading/prefetch/prefetch_serving_handle.h"
Max Curran892ca5422022-12-12 20:55:3430#include "content/browser/preloading/prefetch/prefetch_serving_page_metrics_container.h"
Hiroshige Hayashizaki87a43932025-08-12 06:57:2531#include "content/browser/preloading/prefetch/prefetch_single_redirect_hop.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2732#include "content/browser/preloading/prefetch/prefetch_status.h"
Max Curran892ca5422022-12-12 20:55:3433#include "content/browser/preloading/prefetch/prefetch_streaming_url_loader.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2734#include "content/browser/preloading/prefetch/prefetch_type.h"
Adithya Srinivasan38e0c402023-07-19 16:12:5835#include "content/browser/preloading/preloading_attempt_impl.h"
Taiyo Mizuhashi0889c072024-03-13 13:29:2036#include "content/browser/preloading/preloading_trigger_type_impl.h"
kenossf6fbd5652024-09-04 00:40:2837#include "content/browser/preloading/prerender/prerender_features.h"
Peter Conn38164602025-08-06 14:41:1938#include "content/browser/preloading/proxy_lookup_client_impl.h"
HuanPo Lin740620b2025-03-21 12:37:2639#include "content/browser/preloading/speculation_rules/speculation_rules_tags.h"
Robert Lin233948df2023-02-10 01:37:3940#include "content/browser/renderer_host/frame_tree_node.h"
Kevin McNee06824c72024-02-06 18:59:5241#include "content/browser/renderer_host/render_frame_host_impl.h"
42#include "content/browser/web_contents/web_contents_impl.h"
Jeremy Roman586b5ec2024-02-13 15:14:4043#include "content/public/browser/client_hints.h"
Max Curran646fb642022-03-16 00:44:0944#include "content/public/browser/global_routing_id.h"
Wayne Jackson Jr.b22d53b22024-11-15 11:40:0645#include "content/public/browser/prefetch_request_status_listener.h"
Simon Pelchat9ed3e992023-02-17 01:16:1646#include "content/public/browser/preloading.h"
William Liu77089052022-12-15 18:53:3547#include "content/public/browser/web_contents.h"
Jeremy Roman45c89d012023-08-30 21:22:0248#include "net/base/load_flags.h"
Jeremy Roman45c89d012023-08-30 21:22:0249#include "net/http/http_request_headers.h"
50#include "net/url_request/redirect_util.h"
Max Curran210cffa2022-09-06 22:24:3151#include "services/metrics/public/cpp/ukm_builders.h"
52#include "services/metrics/public/cpp/ukm_recorder.h"
Jeremy Roman586b5ec2024-02-13 15:14:4053#include "services/network/public/cpp/client_hints.h"
kenoss1ce36df592024-12-03 01:04:3554#include "services/network/public/cpp/devtools_observer_util.h"
Jeremy Roman586b5ec2024-02-13 15:14:4055#include "third_party/blink/public/common/client_hints/client_hints.h"
David Risneya1dd2bf2025-03-06 03:40:4256#include "third_party/blink/public/common/navigation/preloading_headers.h"
Max Curran646fb642022-03-16 00:44:0957#include "url/gurl.h"
58
59namespace content {
Max Curran679a3d52022-07-11 23:03:3160namespace {
61
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:2162PrefetchStatus PrefetchStatusFromIneligibleReason(
63 PreloadingEligibility eligibility) {
64 switch (eligibility) {
65 case PreloadingEligibility::kBatterySaverEnabled:
66 return PrefetchStatus::kPrefetchIneligibleBatterySaverEnabled;
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:2167 case PreloadingEligibility::kDataSaverEnabled:
68 return PrefetchStatus::kPrefetchIneligibleDataSaverEnabled;
69 case PreloadingEligibility::kExistingProxy:
70 return PrefetchStatus::kPrefetchIneligibleExistingProxy;
71 case PreloadingEligibility::kHostIsNonUnique:
72 return PrefetchStatus::kPrefetchIneligibleHostIsNonUnique;
73 case PreloadingEligibility::kNonDefaultStoragePartition:
74 return PrefetchStatus::kPrefetchIneligibleNonDefaultStoragePartition;
75 case PreloadingEligibility::kPrefetchProxyNotAvailable:
76 return PrefetchStatus::kPrefetchIneligiblePrefetchProxyNotAvailable;
77 case PreloadingEligibility::kPreloadingDisabled:
78 return PrefetchStatus::kPrefetchIneligiblePreloadingDisabled;
79 case PreloadingEligibility::kRetryAfter:
80 return PrefetchStatus::kPrefetchIneligibleRetryAfter;
81 case PreloadingEligibility::kSameSiteCrossOriginPrefetchRequiredProxy:
82 return PrefetchStatus::
83 kPrefetchIneligibleSameSiteCrossOriginPrefetchRequiredProxy;
84 case PreloadingEligibility::kSchemeIsNotHttps:
85 return PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps;
86 case PreloadingEligibility::kUserHasCookies:
87 return PrefetchStatus::kPrefetchIneligibleUserHasCookies;
88 case PreloadingEligibility::kUserHasServiceWorker:
89 return PrefetchStatus::kPrefetchIneligibleUserHasServiceWorker;
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:3190 case PreloadingEligibility::kUserHasServiceWorkerNoFetchHandler:
91 return PrefetchStatus::
92 kPrefetchIneligibleUserHasServiceWorkerNoFetchHandler;
93 case PreloadingEligibility::kRedirectFromServiceWorker:
94 return PrefetchStatus::kPrefetchIneligibleRedirectFromServiceWorker;
95 case PreloadingEligibility::kRedirectToServiceWorker:
96 return PrefetchStatus::kPrefetchIneligibleRedirectToServiceWorker;
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:2197
98 case PreloadingEligibility::kEligible:
99 default:
100 // Other ineligible cases are not used in `PrefetchService`.
Peter Boströmfc7ddc182024-10-31 19:37:21101 NOTREACHED();
William Liu77089052022-12-15 18:53:35102 }
103}
104
Arthur Sonzognic686e8f2024-01-11 08:36:37105std::optional<PreloadingTriggeringOutcome> TriggeringOutcomeFromStatus(
Robert Lin92f3617e2023-05-20 10:14:49106 PrefetchStatus prefetch_status) {
107 switch (prefetch_status) {
108 case PrefetchStatus::kPrefetchNotFinishedInTime:
109 return PreloadingTriggeringOutcome::kRunning;
110 case PrefetchStatus::kPrefetchSuccessful:
111 return PreloadingTriggeringOutcome::kReady;
112 case PrefetchStatus::kPrefetchResponseUsed:
113 return PreloadingTriggeringOutcome::kSuccess;
114 case PrefetchStatus::kPrefetchIsPrivacyDecoy:
Adithya Srinivasan006b3652023-12-14 14:52:12115 case PrefetchStatus::kPrefetchIsStale:
Robert Lin92f3617e2023-05-20 10:14:49116 case PrefetchStatus::kPrefetchFailedNetError:
117 case PrefetchStatus::kPrefetchFailedNon2XX:
118 case PrefetchStatus::kPrefetchFailedMIMENotSupported:
119 case PrefetchStatus::kPrefetchFailedInvalidRedirect:
120 case PrefetchStatus::kPrefetchFailedIneligibleRedirect:
Adithya Srinivasanb7f08b42023-12-05 15:49:36121 case PrefetchStatus::kPrefetchEvictedAfterCandidateRemoved:
122 case PrefetchStatus::kPrefetchEvictedForNewerPrefetch:
Hiroshige Hayashizaki238198d82023-10-23 23:04:12123 case PrefetchStatus::kPrefetchIneligibleUserHasServiceWorker:
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31124 case PrefetchStatus::kPrefetchIneligibleUserHasServiceWorkerNoFetchHandler:
125 case PrefetchStatus::kPrefetchIneligibleRedirectFromServiceWorker:
126 case PrefetchStatus::kPrefetchIneligibleRedirectToServiceWorker:
Hiroshige Hayashizaki238198d82023-10-23 23:04:12127 case PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps:
128 case PrefetchStatus::kPrefetchIneligibleNonDefaultStoragePartition:
129 case PrefetchStatus::kPrefetchIneligibleHostIsNonUnique:
130 case PrefetchStatus::kPrefetchIneligibleDataSaverEnabled:
131 case PrefetchStatus::kPrefetchIneligibleBatterySaverEnabled:
132 case PrefetchStatus::kPrefetchIneligiblePreloadingDisabled:
133 case PrefetchStatus::kPrefetchIneligibleExistingProxy:
134 case PrefetchStatus::kPrefetchIneligibleUserHasCookies:
Robert Lin92f3617e2023-05-20 10:14:49135 case PrefetchStatus::kPrefetchIneligibleRetryAfter:
Robert Lin92f3617e2023-05-20 10:14:49136 case PrefetchStatus::kPrefetchNotUsedCookiesChanged:
Robert Lin92f3617e2023-05-20 10:14:49137 case PrefetchStatus::kPrefetchNotUsedProbeFailed:
138 case PrefetchStatus::
Hiroshige Hayashizaki238198d82023-10-23 23:04:12139 kPrefetchIneligibleSameSiteCrossOriginPrefetchRequiredProxy:
Adithya Srinivasan0aab9702024-08-01 13:25:42140 case PrefetchStatus::kPrefetchIneligiblePrefetchProxyNotAvailable:
Steven Weie829c85f72025-03-17 18:57:59141 case PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved:
Robert Lin07e52572023-05-22 07:10:18142 return PreloadingTriggeringOutcome::kFailure;
143 case PrefetchStatus::kPrefetchHeldback:
Robert Lin07e52572023-05-22 07:10:18144 case PrefetchStatus::kPrefetchNotStarted:
Arthur Sonzognic686e8f2024-01-11 08:36:37145 return std::nullopt;
Robert Lin92f3617e2023-05-20 10:14:49146 }
Arthur Sonzognic686e8f2024-01-11 08:36:37147 return std::nullopt;
Robert Lin92f3617e2023-05-20 10:14:49148}
149
Adithya Srinivasan006b3652023-12-14 14:52:12150// Returns true if SetPrefetchStatus(|status|) can be called after a prefetch
Taiyo Mizuhashi070baec2025-03-28 15:37:00151// has already been marked as failed. We ignore such status updates
Taiyo Mizuhashi4e693172025-03-26 12:07:50152// as they may end up overwriting the initial failure reason.
Taiyo Mizuhashi070baec2025-03-28 15:37:00153bool StatusUpdateIsPossibleAfterFailure(PrefetchStatus status) {
Adithya Srinivasan006b3652023-12-14 14:52:12154 switch (status) {
155 case PrefetchStatus::kPrefetchEvictedAfterCandidateRemoved:
156 case PrefetchStatus::kPrefetchIsStale:
Steven Weicbbda74f2025-03-19 02:12:34157 case PrefetchStatus::kPrefetchEvictedForNewerPrefetch:
158 case PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved: {
kenoss081c9d92024-11-13 10:21:57159 CHECK(TriggeringOutcomeFromStatus(status) ==
160 PreloadingTriggeringOutcome::kFailure);
Adithya Srinivasan006b3652023-12-14 14:52:12161 return true;
kenoss081c9d92024-11-13 10:21:57162 }
Adithya Srinivasan006b3652023-12-14 14:52:12163 case PrefetchStatus::kPrefetchNotFinishedInTime:
164 case PrefetchStatus::kPrefetchSuccessful:
165 case PrefetchStatus::kPrefetchResponseUsed:
166 case PrefetchStatus::kPrefetchIsPrivacyDecoy:
167 case PrefetchStatus::kPrefetchFailedNetError:
168 case PrefetchStatus::kPrefetchFailedNon2XX:
169 case PrefetchStatus::kPrefetchFailedMIMENotSupported:
170 case PrefetchStatus::kPrefetchFailedInvalidRedirect:
171 case PrefetchStatus::kPrefetchFailedIneligibleRedirect:
Adithya Srinivasan006b3652023-12-14 14:52:12172 case PrefetchStatus::kPrefetchIneligibleUserHasServiceWorker:
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31173 case PrefetchStatus::kPrefetchIneligibleUserHasServiceWorkerNoFetchHandler:
174 case PrefetchStatus::kPrefetchIneligibleRedirectFromServiceWorker:
175 case PrefetchStatus::kPrefetchIneligibleRedirectToServiceWorker:
Adithya Srinivasan006b3652023-12-14 14:52:12176 case PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps:
177 case PrefetchStatus::kPrefetchIneligibleNonDefaultStoragePartition:
178 case PrefetchStatus::kPrefetchIneligibleHostIsNonUnique:
179 case PrefetchStatus::kPrefetchIneligibleDataSaverEnabled:
180 case PrefetchStatus::kPrefetchIneligibleBatterySaverEnabled:
181 case PrefetchStatus::kPrefetchIneligiblePreloadingDisabled:
182 case PrefetchStatus::kPrefetchIneligibleExistingProxy:
183 case PrefetchStatus::kPrefetchIneligibleUserHasCookies:
184 case PrefetchStatus::kPrefetchIneligibleRetryAfter:
185 case PrefetchStatus::kPrefetchNotUsedCookiesChanged:
186 case PrefetchStatus::kPrefetchNotUsedProbeFailed:
Adithya Srinivasan006b3652023-12-14 14:52:12187 case PrefetchStatus::
188 kPrefetchIneligibleSameSiteCrossOriginPrefetchRequiredProxy:
189 case PrefetchStatus::kPrefetchHeldback:
Adithya Srinivasan006b3652023-12-14 14:52:12190 case PrefetchStatus::kPrefetchNotStarted:
191 case PrefetchStatus::kPrefetchIneligiblePrefetchProxyNotAvailable:
Adithya Srinivasan006b3652023-12-14 14:52:12192 return false;
193 }
194}
195
Hiroshige Hayashizaki77083bc82023-11-28 06:04:17196void RecordPrefetchProxyPrefetchMainframeNetError(int net_error) {
197 base::UmaHistogramSparse("PrefetchProxy.Prefetch.Mainframe.NetError",
198 std::abs(net_error));
199}
200
201void RecordPrefetchProxyPrefetchMainframeBodyLength(int64_t body_length) {
202 UMA_HISTOGRAM_COUNTS_10M("PrefetchProxy.Prefetch.Mainframe.BodyLength",
203 body_length);
204}
205
kenoss8644f6bf2025-02-10 05:51:44206bool CalculateIsLikelyAheadOfPrerender(
kenossc3f9a2c2025-03-06 15:35:51207 const PreloadPipelineInfoImpl& preload_pipeline_info) {
kenossc6492942025-04-10 19:36:32208 if (!features::UsePrefetchPrerenderIntegration()) {
kenossb68c9e82024-10-10 10:11:15209 return false;
210 }
211
kenoss8644f6bf2025-02-10 05:51:44212 switch (preload_pipeline_info.planned_max_preloading_type()) {
213 case PreloadingType::kPrefetch:
214 return false;
215 case PreloadingType::kPrerender:
Lingqi Chi844e05d42025-07-23 02:50:50216 case PreloadingType::kPrerenderUntilScript:
kenoss8644f6bf2025-02-10 05:51:44217 return true;
218 case PreloadingType::kUnspecified:
219 case PreloadingType::kPreconnect:
220 case PreloadingType::kNoStatePrefetch:
221 case PreloadingType::kLinkPreview:
222 NOTREACHED();
kenossb68c9e82024-10-10 10:11:15223 }
kenossb68c9e82024-10-10 10:11:15224}
225
Hiroshige Hayashizaki29e14dd2025-03-14 16:03:07226PrefetchContainer::PrefetchResponseCompletedCallbackForTesting&
227GetPrefetchResponseCompletedCallbackForTesting() {
228 static base::NoDestructor<
229 PrefetchContainer::PrefetchResponseCompletedCallbackForTesting>
230 prefetch_response_completed_callback_for_testing;
231 return *prefetch_response_completed_callback_for_testing;
232}
233
Max Curran679a3d52022-07-11 23:03:31234} // namespace
Max Curran646fb642022-03-16 00:44:09235
Max Curran646fb642022-03-16 00:44:09236PrefetchContainer::PrefetchContainer(
Kevin McNee06824c72024-02-06 18:59:52237 RenderFrameHostImpl& referring_render_frame_host,
Hiroshige Hayashizaki2df45292023-10-10 22:59:03238 const blink::DocumentToken& referring_document_token,
Max Curran646fb642022-03-16 00:44:09239 const GURL& url,
Max Curran18a6f2b2022-05-02 23:13:24240 const PrefetchType& prefetch_type,
Kevin McNeee6ca0892022-09-28 15:36:22241 const blink::mojom::Referrer& referrer,
HuanPo Lin740620b2025-03-21 12:37:26242 std::optional<SpeculationRulesTags> speculation_rules_tags,
Arthur Sonzognic686e8f2024-01-11 08:36:37243 std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
Taiyo Mizuhashid7c856992025-06-23 08:56:02244 std::optional<PrefetchPriority> priority,
Hiroshige Hayashizakia00e4c82023-10-31 20:20:17245 base::WeakPtr<PrefetchDocumentManager> prefetch_document_manager,
kenoss3bd73b82024-10-10 20:33:49246 scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
Taiyo Mizuhashi0192d3f2024-02-15 05:09:43247 base::WeakPtr<PreloadingAttempt> attempt)
Hiroshige Hayashizakice4413252025-08-19 19:05:45248 : PrefetchContainer(std::make_unique<PrefetchRequest>(
249 prefetch_type,
250 PrefetchKey(referring_document_token, url),
251 std::move(no_vary_search_hint),
252 std::move(priority),
253 std::move(preload_pipeline_info),
254 std::move(attempt),
255 WebContentsImpl::FromRenderFrameHostImpl(&referring_render_frame_host)
256 ->GetOrCreateWebPreferences()
257 .javascript_enabled,
258 referrer,
259 referring_render_frame_host.GetLastCommittedOrigin(),
260 referring_render_frame_host.GetBrowserContext()->GetWeakPtr(),
261 std::move(speculation_rules_tags),
262 /*Must be empty: additional_headers=*/net::HttpRequestHeaders(),
263 PrefetchContainerDefaultTtlInPrefetchService(),
264 /*holdback_status_override=*/std::nullopt,
265 /*should_append_variations_header=*/true,
266 /*should_disable_block_until_head_timeout=*/false,
267 PrefetchRendererInitiatorInfo(
268 referring_render_frame_host,
269 std::move(prefetch_document_manager)))) {}
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37270
271PrefetchContainer::PrefetchContainer(
272 WebContents& referring_web_contents,
273 const GURL& url,
274 const PrefetchType& prefetch_type,
Taiyo Mizuhashi49959d02025-04-22 16:07:54275 const std::string& embedder_histogram_suffix,
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37276 const blink::mojom::Referrer& referrer,
277 const std::optional<url::Origin>& referring_origin,
278 std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
Taiyo Mizuhashid7c856992025-06-23 08:56:02279 std::optional<PrefetchPriority> priority,
kenossa1af66f12025-03-07 06:10:55280 scoped_refptr<PreloadPipelineInfo> preload_pipeline_info,
Taiyo Mizuhashi26f86c6b32024-10-02 03:58:46281 base::WeakPtr<PreloadingAttempt> attempt,
kenoss6938554c2025-06-10 10:18:17282 std::optional<PreloadingHoldbackStatus> holdback_status_override,
283 std::optional<base::TimeDelta> ttl)
Hiroshige Hayashizakice4413252025-08-19 19:05:45284 : PrefetchContainer(std::make_unique<PrefetchRequest>(
285 prefetch_type,
286 PrefetchKey(std::optional<blink::DocumentToken>(std::nullopt), url),
287 std::move(no_vary_search_hint),
288 std::move(priority),
289 std::move(preload_pipeline_info),
290 std::move(attempt),
291 referring_web_contents.GetOrCreateWebPreferences().javascript_enabled,
292 referrer,
293 referring_origin,
294 referring_web_contents.GetBrowserContext()->GetWeakPtr(),
295 /*speculation_rules_tags=*/std::nullopt,
296 /*Must be empty: additional_headers=*/net::HttpRequestHeaders(),
297 ttl.has_value() ? ttl.value()
298 : PrefetchContainerDefaultTtlInPrefetchService(),
299 std::move(holdback_status_override),
300 /*should_append_variations_header=*/true,
301 /*should_disable_block_until_head_timeout=*/false,
302 PrefetchBrowserInitiatorInfo(embedder_histogram_suffix,
303 /*request_status_listener=*/nullptr))) {}
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37304
305PrefetchContainer::PrefetchContainer(
Wayne Jackson Jr.5b1dc042024-09-09 12:53:10306 BrowserContext* browser_context,
307 const GURL& url,
308 const PrefetchType& prefetch_type,
Taiyo Mizuhashi49959d02025-04-22 16:07:54309 const std::string& embedder_histogram_suffix,
Wayne Jackson Jr.5b1dc042024-09-09 12:53:10310 const blink::mojom::Referrer& referrer,
311 bool javascript_enabled,
312 const std::optional<url::Origin>& referring_origin,
313 std::optional<net::HttpNoVarySearchData> no_vary_search_hint,
Taiyo Mizuhashid7c856992025-06-23 08:56:02314 std::optional<PrefetchPriority> priority,
Wayne Jackson Jr.03a15fa62024-09-16 14:42:15315 base::WeakPtr<PreloadingAttempt> attempt,
Wayne Jackson Jr.1b7322472024-10-29 13:08:51316 const net::HttpRequestHeaders& additional_headers,
elabadysayedf7d6b00f2025-02-05 11:27:00317 std::unique_ptr<PrefetchRequestStatusListener> request_status_listener,
kenoss70bfbfe2025-06-10 08:04:42318 base::TimeDelta ttl,
Taiyo Mizuhashicd08a8f2025-06-10 17:27:32319 bool should_append_variations_header,
320 bool should_disable_block_until_head_timeout)
Hiroshige Hayashizakice4413252025-08-19 19:05:45321 : PrefetchContainer(std::make_unique<PrefetchRequest>(
322 prefetch_type,
323 PrefetchKey(std::optional<blink::DocumentToken>(std::nullopt), url),
324 std::move(no_vary_search_hint),
325 std::move(priority),
326 PreloadPipelineInfo::Create(
327 /*planned_max_preloading_type=*/PreloadingType::kPrefetch),
328 std::move(attempt),
329 javascript_enabled,
330 referrer,
331 referring_origin,
332 browser_context->GetWeakPtr(),
333 /*speculation_rules_tags=*/std::nullopt,
334 additional_headers,
335 ttl,
336 /*holdback_status_override=*/std::nullopt,
337 should_append_variations_header,
338 should_disable_block_until_head_timeout,
339 PrefetchBrowserInitiatorInfo(embedder_histogram_suffix,
340 std::move(request_status_listener)))) {}
Wayne Jackson Jr.5b1dc042024-09-09 12:53:10341
Hiroshige Hayashizakice4413252025-08-19 19:05:45342PrefetchContainer::PrefetchContainer(std::unique_ptr<PrefetchRequest> request)
Hiroshige Hayashizakif55280f62025-08-19 17:23:20343 : request_(std::move(request)),
Hiroshige Hayashizakiff0133db2025-08-19 19:03:10344 referrer_(request_->initial_referrer()),
Hiroshige Hayashizakice4413252025-08-19 19:05:45345 request_id_(base::UnguessableToken::Create().ToString()) {
Hiroshige Hayashizakif55280f62025-08-19 17:23:20346 CHECK(request_);
347
kenossb68c9e82024-10-10 10:11:15348 is_likely_ahead_of_prerender_ =
Hiroshige Hayashizaki1ec17a52025-08-19 18:49:34349 CalculateIsLikelyAheadOfPrerender(request_->preload_pipeline_info());
kenossaa7dbe72024-10-17 11:20:03350
Hiroshige Hayashizaki87a43932025-08-12 06:57:25351 redirect_chain_.push_back(std::make_unique<PrefetchSingleRedirectHop>(
Hiroshige Hayashizaki40270b72025-07-23 01:52:01352 *this, GetURL(), IsCrossSiteRequest(url::Origin::Create(GetURL()))));
Hiroshige Hayashizaki0830e182025-03-14 02:03:06353
354 // Disallow prefetching ServiceWorker-controlled responses for isolated
355 // network contexts.
Hiroshige Hayashizakid75d328a2025-08-19 18:46:15356 if (!features::IsPrefetchServiceWorkerEnabled(request_->browser_context()) ||
Hiroshige Hayashizaki0830e182025-03-14 02:03:06357 IsIsolatedNetworkContextRequiredForCurrentPrefetch()) {
358 service_worker_state_ = PrefetchServiceWorkerState::kDisallowed;
359 }
William Liu77089052022-12-15 18:53:35360}
Max Curran646fb642022-03-16 00:44:09361
Max Curran210cffa2022-09-06 22:24:31362PrefetchContainer::~PrefetchContainer() {
kenoss6c5fb092024-07-05 05:42:14363 is_in_dtor_ = true;
364
kenossf6fbd5652024-09-04 00:40:28365 // Ideally, this method should be called just before dtor.
366 // https://chromium-review.googlesource.com/c/chromium/src/+/5657659/comments/0cfb14c0_3050963e
367 //
368 // TODO(crbug.com/356314759): Do it.
kenossc6c712a2025-03-10 23:17:02369 OnWillBeDestroyed();
kenossf6fbd5652024-09-04 00:40:28370
Hiroshige Hayashizaki638018f2023-09-12 17:05:45371 CancelStreamingURLLoaderIfNotServing();
372
Adithya Srinivasan4d3dad02024-10-17 18:06:59373 MaybeRecordPrefetchStatusToUMA(
374 prefetch_status_.value_or(PrefetchStatus::kPrefetchNotStarted));
Taiyo Mizuhashi98061e22025-07-12 05:26:54375 RecordPrefetchDurationHistogram();
Taiyo Mizuhashia009caf2025-08-05 07:34:22376 RecordPrefetchContainerServedCountHistogram();
Adithya Srinivasan4d3dad02024-10-17 18:06:59377
Hiroshige Hayashizakif55280f62025-08-19 17:23:20378 ukm::SourceId ukm_source_id = ukm::kInvalidSourceId;
379 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
380 ukm_source_id = renderer_initiator_info->ukm_source_id();
381 }
382 ukm::builders::PrefetchProxy_PrefetchedResource builder(ukm_source_id);
Max Curran210cffa2022-09-06 22:24:31383 builder.SetResourceType(/*mainframe*/ 1);
384 builder.SetStatus(static_cast<int>(
385 prefetch_status_.value_or(PrefetchStatus::kPrefetchNotStarted)));
Taiyo Mizuhashia009caf2025-08-05 07:34:22386 builder.SetLinkClicked(served_count_ > 0);
Max Curran210cffa2022-09-06 22:24:31387
Hiroshige Hayashizaki57872f32025-07-09 11:52:02388 if (GetNonRedirectResponseReader()) {
389 GetNonRedirectResponseReader()->RecordOnPrefetchContainerDestroyed(
390 base::PassKey<PrefetchContainer>(), builder);
Max Curran210cffa2022-09-06 22:24:31391 }
392
393 if (probe_result_) {
394 builder.SetISPFilteringStatus(static_cast<int>(probe_result_.value()));
395 }
396
Alison Gale770f3fc2024-04-27 00:39:58397 // TODO(crbug.com/40215782): Get the navigation start time and set the
Max Curran210cffa2022-09-06 22:24:31398 // NavigationStartToFetchStartMs field of the PrefetchProxy.PrefetchedResource
399 // UKM event.
400
401 builder.Record(ukm::UkmRecorder::Get());
Adithya Srinivasand476f4f82023-06-20 15:55:13402
Hiroshige Hayashizakif55280f62025-08-19 17:23:20403 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
404 if (renderer_initiator_info->prefetch_document_manager()) {
405 renderer_initiator_info->prefetch_document_manager()
406 ->PrefetchWillBeDestroyed(this);
407 }
Adithya Srinivasand476f4f82023-06-20 15:55:13408 }
kenossf6fbd5652024-09-04 00:40:28409}
410
411void PrefetchContainer::OnWillBeDestroyed() {
kenossf6fbd5652024-09-04 00:40:28412 for (auto& observer : observers_) {
413 observer.OnWillBeDestroyed(*this);
414 }
Max Curran210cffa2022-09-06 22:24:31415}
Max Curran646fb642022-03-16 00:44:09416
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57417PrefetchServingHandle PrefetchContainer::CreateServingHandle() {
418 return PrefetchServingHandle(GetWeakPtr(), 0);
Hiroshige Hayashizaki00b3f002023-07-15 00:32:48419}
Hiroshige Hayashizaki6a99ee92023-05-30 19:54:37420
Hiroshige Hayashizaki386c5022025-08-12 14:43:37421const std::vector<std::unique_ptr<PrefetchSingleRedirectHop>>&
422PrefetchContainer::redirect_chain(base::PassKey<PrefetchServingHandle>) const {
423 return redirect_chain_;
424}
425
426void PrefetchContainer::SetProbeResult(base::PassKey<PrefetchServingHandle>,
427 PrefetchProbeResult probe_result) {
428 probe_result_ = probe_result;
429}
430
431std::optional<PreloadingTriggeringOutcome>
432PrefetchContainer::TriggeringOutcomeFromStatusForServingHandle(
433 base::PassKey<PrefetchServingHandle>,
434 PrefetchStatus prefetch_status) {
435 return TriggeringOutcomeFromStatus(prefetch_status);
436}
437
Adithya Srinivasan4d3dad02024-10-17 18:06:59438// Please follow go/preloading-dashboard-updates if a new outcome enum or a
439// failure reason enum is added.
440void PrefetchContainer::SetTriggeringOutcomeAndFailureReasonFromStatus(
441 PrefetchStatus new_prefetch_status) {
442 std::optional<PrefetchStatus> old_prefetch_status = prefetch_status_;
443 if (old_prefetch_status &&
444 old_prefetch_status.value() == PrefetchStatus::kPrefetchResponseUsed) {
445 // Skip this update if the triggering outcome has already been updated
446 // to kSuccess.
447 return;
448 }
449
450 if (old_prefetch_status &&
Taiyo Mizuhashi4e693172025-03-26 12:07:50451 (TriggeringOutcomeFromStatus(old_prefetch_status.value()) ==
Taiyo Mizuhashi070baec2025-03-28 15:37:00452 PreloadingTriggeringOutcome::kFailure)) {
453 if (StatusUpdateIsPossibleAfterFailure(new_prefetch_status)) {
454 // Note that `StatusUpdateIsPossibleAfterFailure()` implies that
Taiyo Mizuhashi4e693172025-03-26 12:07:50455 // the new status is a failure.
kenoss081c9d92024-11-13 10:21:57456 CHECK(TriggeringOutcomeFromStatus(new_prefetch_status) ==
457 PreloadingTriggeringOutcome::kFailure);
Taiyo Mizuhashi4e693172025-03-26 12:07:50458
Taiyo Mizuhashi070baec2025-03-28 15:37:00459 // Skip this update since if the triggering outcome has already been
460 // updated to kFailure, we don't need to overwrite it.
kenoss081c9d92024-11-13 10:21:57461 return;
462 } else {
463 SCOPED_CRASH_KEY_NUMBER("PrefetchContainer", "prefetch_status_from",
464 static_cast<int>(old_prefetch_status.value()));
465 SCOPED_CRASH_KEY_NUMBER("PrefetchContainer", "prefetch_status_to",
466 static_cast<int>(new_prefetch_status));
467 NOTREACHED()
468 << "PrefetchStatus illegal transition: (old_prefetch_status, "
469 "new_prefetch_status) = ("
470 << static_cast<int>(old_prefetch_status.value()) << ", "
471 << static_cast<int>(new_prefetch_status) << ")";
472 }
Adithya Srinivasan4d3dad02024-10-17 18:06:59473 }
474
475 // We record the prefetch status to UMA if it's a failure, or if the prefetch
476 // response is being used. For other statuses, there may be more updates in
477 // the future, so we only record them in the destructor.
478 // Note: The prefetch may have an updated failure status in the future
479 // (for example: if the triggering speculation candidate for a failed prefetch
480 // is removed), but the original failure is more pertinent for metrics
481 // purposes.
482 if (TriggeringOutcomeFromStatus(new_prefetch_status) ==
483 PreloadingTriggeringOutcome::kFailure ||
484 new_prefetch_status == PrefetchStatus::kPrefetchResponseUsed) {
485 MaybeRecordPrefetchStatusToUMA(new_prefetch_status);
486 }
487
Hiroshige Hayashizakib374e642025-08-19 18:53:32488 if (request().attempt()) {
Adithya Srinivasan4d3dad02024-10-17 18:06:59489 switch (new_prefetch_status) {
490 case PrefetchStatus::kPrefetchNotFinishedInTime:
Hiroshige Hayashizakib374e642025-08-19 18:53:32491 request().attempt()->SetTriggeringOutcome(
492 PreloadingTriggeringOutcome::kRunning);
Adithya Srinivasan4d3dad02024-10-17 18:06:59493 break;
494 case PrefetchStatus::kPrefetchSuccessful:
495 // A successful prefetch means the response is ready to be used for the
496 // next navigation.
Hiroshige Hayashizakib374e642025-08-19 18:53:32497 request().attempt()->SetTriggeringOutcome(
498 PreloadingTriggeringOutcome::kReady);
Adithya Srinivasan4d3dad02024-10-17 18:06:59499 break;
500 case PrefetchStatus::kPrefetchResponseUsed:
501 if (old_prefetch_status && old_prefetch_status.value() !=
502 PrefetchStatus::kPrefetchSuccessful) {
503 // If the new prefetch status is |kPrefetchResponseUsed| or
504 // |kPrefetchUsedNoProbe| but the previous status is not
505 // |kPrefetchSuccessful|, then temporarily update the triggering
506 // outcome to |kReady| to ensure valid triggering outcome state
507 // transitions. This can occur in cases where the prefetch is served
508 // before the body is fully received.
Hiroshige Hayashizakib374e642025-08-19 18:53:32509 request().attempt()->SetTriggeringOutcome(
510 PreloadingTriggeringOutcome::kReady);
Adithya Srinivasan4d3dad02024-10-17 18:06:59511 }
Hiroshige Hayashizakib374e642025-08-19 18:53:32512 request().attempt()->SetTriggeringOutcome(
513 PreloadingTriggeringOutcome::kSuccess);
Adithya Srinivasan4d3dad02024-10-17 18:06:59514 break;
515 // A decoy is considered eligible because a network request is made for
516 // it. It is considered as a failure as the final response is never
517 // served.
518 case PrefetchStatus::kPrefetchIsPrivacyDecoy:
519 case PrefetchStatus::kPrefetchFailedNetError:
520 case PrefetchStatus::kPrefetchFailedNon2XX:
521 case PrefetchStatus::kPrefetchFailedMIMENotSupported:
522 case PrefetchStatus::kPrefetchFailedInvalidRedirect:
523 case PrefetchStatus::kPrefetchFailedIneligibleRedirect:
524 case PrefetchStatus::kPrefetchNotUsedProbeFailed:
525 case PrefetchStatus::kPrefetchNotUsedCookiesChanged:
526 // TODO(adithyas): This would report 'eviction' as a failure even though
527 // the initial prefetch succeeded, consider introducing a different
528 // PreloadingTriggerOutcome for eviction.
529 case PrefetchStatus::kPrefetchEvictedAfterCandidateRemoved:
530 case PrefetchStatus::kPrefetchEvictedForNewerPrefetch:
531 case PrefetchStatus::kPrefetchIsStale:
Steven Weie829c85f72025-03-17 18:57:59532 case PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved:
Hiroshige Hayashizakib374e642025-08-19 18:53:32533 request().attempt()->SetFailureReason(
Adithya Srinivasan4d3dad02024-10-17 18:06:59534 ToPreloadingFailureReason(new_prefetch_status));
535 break;
536 case PrefetchStatus::kPrefetchHeldback:
Adithya Srinivasan4d3dad02024-10-17 18:06:59537 case PrefetchStatus::kPrefetchNotStarted:
538 // `kPrefetchNotStarted` is set in
Hiroshige Hayashizakif1f8b3602025-01-15 11:50:49539 // `PrefetchService::OnGotEligibilityForNonRedirect()` when the
540 // container is pushed onto the prefetch queue, which occurs before the
541 // holdback status is determined in
542 // `PrefetchService::StartSinglePrefetch`. After the container is queued
543 // and before it is sent for prefetch, the only status change is when
544 // the container is popped from the queue but heldback. This is covered
545 // by attempt's holdback status. For these two reasons this
546 // PrefetchStatus does not fire a `SetTriggeringOutcome`.
Adithya Srinivasan4d3dad02024-10-17 18:06:59547 break;
548 case PrefetchStatus::kPrefetchIneligibleUserHasServiceWorker:
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31549 case PrefetchStatus::
550 kPrefetchIneligibleUserHasServiceWorkerNoFetchHandler:
551 case PrefetchStatus::kPrefetchIneligibleRedirectFromServiceWorker:
552 case PrefetchStatus::kPrefetchIneligibleRedirectToServiceWorker:
Adithya Srinivasan4d3dad02024-10-17 18:06:59553 case PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps:
554 case PrefetchStatus::kPrefetchIneligibleNonDefaultStoragePartition:
555 case PrefetchStatus::kPrefetchIneligibleHostIsNonUnique:
556 case PrefetchStatus::kPrefetchIneligibleDataSaverEnabled:
557 case PrefetchStatus::kPrefetchIneligibleBatterySaverEnabled:
558 case PrefetchStatus::kPrefetchIneligiblePreloadingDisabled:
559 case PrefetchStatus::kPrefetchIneligibleExistingProxy:
560 case PrefetchStatus::kPrefetchIneligibleUserHasCookies:
561 case PrefetchStatus::kPrefetchIneligibleRetryAfter:
562 case PrefetchStatus::kPrefetchIneligiblePrefetchProxyNotAvailable:
563 case PrefetchStatus::
564 kPrefetchIneligibleSameSiteCrossOriginPrefetchRequiredProxy:
565 NOTIMPLEMENTED();
566 }
567 }
568}
569
Robert Lin92f3617e2023-05-20 10:14:49570void PrefetchContainer::SetPrefetchStatusWithoutUpdatingTriggeringOutcome(
571 PrefetchStatus prefetch_status) {
572 prefetch_status_ = prefetch_status;
Hiroshige Hayashizaki1ec17a52025-08-19 18:49:34573 request().preload_pipeline_info().SetPrefetchStatus(prefetch_status);
kenoss4858a802024-10-11 06:50:56574 for (auto& preload_pipeline_info : inherited_preload_pipeline_infos_) {
575 preload_pipeline_info->SetPrefetchStatus(prefetch_status);
576 }
Robert Lin1f778f892023-05-12 13:51:44577
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37578 // Currently DevTools only supports when the prefetch is initiated by
579 // renderer.
Hiroshige Hayashizakif55280f62025-08-19 17:23:20580 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37581 std::optional<PreloadingTriggeringOutcome> preloading_trigger_outcome =
582 TriggeringOutcomeFromStatus(prefetch_status);
Hiroshige Hayashizakif55280f62025-08-19 17:23:20583 if (renderer_initiator_info->devtools_navigation_token().has_value() &&
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37584 preloading_trigger_outcome.has_value()) {
585 devtools_instrumentation::DidUpdatePrefetchStatus(
Hiroshige Hayashizakif55280f62025-08-19 17:23:20586 FrameTreeNode::From(renderer_initiator_info->GetRenderFrameHost()),
587 renderer_initiator_info->devtools_navigation_token().value(),
Hiroshige Hayashizaki1ec17a52025-08-19 18:49:34588 GetURL(), request().preload_pipeline_info().id(),
Hiroshige Hayashizakif55280f62025-08-19 17:23:20589 preloading_trigger_outcome.value(), prefetch_status, RequestId());
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37590 }
Robert Lin1f778f892023-05-12 13:51:44591 }
William Liu77089052022-12-15 18:53:35592}
593
Robert Lin92f3617e2023-05-20 10:14:49594void PrefetchContainer::SetPrefetchStatus(PrefetchStatus prefetch_status) {
Taiyo Mizuhashi070baec2025-03-28 15:37:00595 // The concept of `PreloadingAttempt`'s `PreloadingTriggeringOutcome` is to
596 // record the outcomes of started triggers. Therefore, this should
597 // only be called once prefetching has actually started, and not for
598 // ineligible or eligibled but not started triggers (e.g., holdback triggers,
599 // triggers waiting on a queue).
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:32600 switch (GetLoadState()) {
601 case LoadState::kStarted:
602 case LoadState::kDeterminedHead:
603 case LoadState::kCompletedOrFailed:
604 SetTriggeringOutcomeAndFailureReasonFromStatus(prefetch_status);
605 break;
606 case LoadState::kNotStarted:
607 case LoadState::kEligible:
608 case LoadState::kFailedIneligible:
609 case LoadState::kFailedHeldback:
610 break;
Taiyo Mizuhashi070baec2025-03-28 15:37:00611 }
Robert Lin92f3617e2023-05-20 10:14:49612 SetPrefetchStatusWithoutUpdatingTriggeringOutcome(prefetch_status);
613}
614
Max Curran146bf442022-03-28 23:22:14615PrefetchStatus PrefetchContainer::GetPrefetchStatus() const {
616 DCHECK(prefetch_status_);
617 return prefetch_status_.value();
618}
619
Max Currancc1ab0c2022-09-12 22:03:11620void PrefetchContainer::TakeProxyLookupClient(
621 std::unique_ptr<ProxyLookupClientImpl> proxy_lookup_client) {
622 DCHECK(!proxy_lookup_client_);
623 proxy_lookup_client_ = std::move(proxy_lookup_client);
624}
625
626std::unique_ptr<ProxyLookupClientImpl>
627PrefetchContainer::ReleaseProxyLookupClient() {
628 DCHECK(proxy_lookup_client_);
629 return std::move(proxy_lookup_client_);
630}
631
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:58632PrefetchNetworkContext*
Kouhei Ueno4442db92023-11-13 06:38:13633PrefetchContainer::GetOrCreateNetworkContextForCurrentPrefetch() {
Max Currana84311e2023-05-16 20:40:25634 bool is_isolated_network_context_required =
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:58635 IsIsolatedNetworkContextRequiredForCurrentPrefetch();
Max Currana84311e2023-05-16 20:40:25636
Hiroshige Hayashizakid44edd4e2025-08-12 07:35:46637 PrefetchNetworkContext* network_context =
638 GetNetworkContext(is_isolated_network_context_required);
639 if (network_context) {
640 return network_context;
Max Curran18a6f2b2022-05-02 23:13:24641 }
Max Currana84311e2023-05-16 20:40:25642
Hiroshige Hayashizakif55280f62025-08-19 17:23:20643 GlobalRenderFrameHostId referring_render_frame_host_id;
644 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
645 referring_render_frame_host_id =
646 renderer_initiator_info->GetRenderFrameHostId();
647 }
648
Hiroshige Hayashizakid44edd4e2025-08-12 07:35:46649 auto owned_network_context = std::make_unique<PrefetchNetworkContext>(
Hiroshige Hayashizakif55280f62025-08-19 17:23:20650 is_isolated_network_context_required, request().prefetch_type(),
Hiroshige Hayashizaki65285e32025-08-19 17:24:20651 referring_render_frame_host_id, request().referring_origin());
Hiroshige Hayashizakid44edd4e2025-08-12 07:35:46652 network_context = owned_network_context.get();
653 network_contexts_.emplace(is_isolated_network_context_required,
654 std::move(owned_network_context));
655
656 return network_context;
657}
658
659PrefetchNetworkContext* PrefetchContainer::GetNetworkContext(
660 bool is_isolated_network_context_required) const {
661 const auto network_context_itr =
662 network_contexts_.find(is_isolated_network_context_required);
663 if (network_context_itr == network_contexts_.end()) {
664 return nullptr;
665 }
Max Currana84311e2023-05-16 20:40:25666 return network_context_itr->second.get();
667}
668
Max Currana84311e2023-05-16 20:40:25669void PrefetchContainer::CloseIdleConnections() {
670 for (const auto& network_context_itr : network_contexts_) {
671 CHECK(network_context_itr.second);
672 network_context_itr.second->CloseIdleConnections();
673 }
Max Curran18a6f2b2022-05-02 23:13:24674}
675
Hiroshige Hayashizaki4ce0f4292023-11-07 19:24:58676void PrefetchContainer::SetLoadState(LoadState new_load_state) {
677 switch (new_load_state) {
678 case LoadState::kNotStarted:
Peter Boströmfc7ddc182024-10-31 19:37:21679 NOTREACHED();
Hiroshige Hayashizaki4ce0f4292023-11-07 19:24:58680
681 case LoadState::kEligible:
682 case LoadState::kFailedIneligible:
683 CHECK_EQ(load_state_, LoadState::kNotStarted);
684 break;
685
686 case LoadState::kStarted:
687 case LoadState::kFailedHeldback:
688 CHECK_EQ(load_state_, LoadState::kEligible);
689 break;
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:32690
691 case LoadState::kDeterminedHead:
692 CHECK_EQ(load_state_, LoadState::kStarted);
693 break;
694
695 case LoadState::kCompletedOrFailed:
696 CHECK_EQ(load_state_, LoadState::kDeterminedHead);
697 break;
Hiroshige Hayashizaki4ce0f4292023-11-07 19:24:58698 }
Kouhei Ueno929ee8e12024-07-05 03:09:56699 DVLOG(1) << (*this) << " LoadState " << load_state_ << " -> "
700 << new_load_state;
Hiroshige Hayashizaki4ce0f4292023-11-07 19:24:58701 load_state_ = new_load_state;
702}
703
704PrefetchContainer::LoadState PrefetchContainer::GetLoadState() const {
705 return load_state_;
706}
707
kenosseab3c422025-04-03 12:50:19708void PrefetchContainer::OnAddedToPrefetchService() {
709 time_added_to_prefetch_service_ = base::TimeTicks::Now();
710}
711
William Liu77089052022-12-15 18:53:35712void PrefetchContainer::OnEligibilityCheckComplete(
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:21713 PreloadingEligibility eligibility) {
Hiroshige Hayashizaki1ec17a52025-08-19 18:49:34714 request().preload_pipeline_info().SetPrefetchEligibility(eligibility);
kenoss4858a802024-10-11 06:50:56715 for (auto& preload_pipeline_info : inherited_preload_pipeline_infos_) {
716 preload_pipeline_info->SetPrefetchEligibility(eligibility);
717 }
718
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:21719 bool is_eligible = eligibility == PreloadingEligibility::kEligible;
Max Curran5d4da4b42023-03-10 23:41:46720
Hiroshige Hayashizaki079949e2023-05-26 01:05:23721 if (redirect_chain_.size() == 1) {
Max Curran5d4da4b42023-03-10 23:41:46722 // This case is for just the URL that was originally requested to be
723 // prefetched.
Hiroshige Hayashizaki4ce0f4292023-11-07 19:24:58724 if (is_eligible) {
725 SetLoadState(LoadState::kEligible);
726 } else {
727 SetLoadState(LoadState::kFailedIneligible);
Adithya Srinivasan4d3dad02024-10-17 18:06:59728 PrefetchStatus new_prefetch_status =
729 PrefetchStatusFromIneligibleReason(eligibility);
730 MaybeRecordPrefetchStatusToUMA(new_prefetch_status);
731 SetPrefetchStatusWithoutUpdatingTriggeringOutcome(new_prefetch_status);
Wayne Jackson Jr.00e3e8b2024-09-25 12:35:56732 OnInitialPrefetchFailedIneligible(eligibility);
Max Curran5d4da4b42023-03-10 23:41:46733 }
734
Hiroshige Hayashizakib374e642025-08-19 18:53:32735 if (request().attempt()) {
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:21736 // Please follow go/preloading-dashboard-updates if a new eligibility is
737 // added.
Hiroshige Hayashizakib374e642025-08-19 18:53:32738 request().attempt()->SetEligibility(eligibility);
Max Curran5d4da4b42023-03-10 23:41:46739 }
740
kenosseab3c422025-04-03 12:50:19741 time_initial_eligibility_got_ = base::TimeTicks::Now();
742
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37743 // Recording an eligiblity for PrefetchReferringPageMetrics.
744 // TODO(crbug.com/40946257): Current code doesn't support
745 // PrefetchReferringPageMetrics when the prefetch is initiated by browser.
Hiroshige Hayashizakif55280f62025-08-19 17:23:20746 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
747 if (renderer_initiator_info->prefetch_document_manager()) {
748 renderer_initiator_info->prefetch_document_manager()
749 ->OnEligibilityCheckComplete(is_eligible);
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37750 }
Max Curranb9c7bba2023-03-28 19:31:23751 }
kenossb68c9e82024-10-10 10:11:15752
753 for (auto& observer : observers_) {
754 observer.OnGotInitialEligibility(*this, eligibility);
755 }
Max Curran5d4da4b42023-03-10 23:41:46756 } else {
757 // This case is for any URLs from redirects.
758 if (!is_eligible) {
759 SetPrefetchStatus(PrefetchStatus::kPrefetchFailedIneligibleRedirect);
760 }
761 }
762}
763
Jeremy Roman45c89d012023-08-30 21:22:02764void PrefetchContainer::AddRedirectHop(const net::RedirectInfo& redirect_info) {
765 CHECK(resource_request_);
766
767 // There are sometimes other headers that are modified during navigation
768 // redirects; see |NavigationRequest::OnRedirectChecksComplete| (including
769 // some which are added by throttles). These aren't yet supported for
770 // prefetch, including browsing topics and client hints.
771 net::HttpRequestHeaders updated_headers;
Liviu Tinta2e1ffe22024-06-21 21:01:33772 std::vector<std::string> headers_to_remove = {variations::kClientDataHeader};
David Risneya1dd2bf2025-03-06 03:40:42773 updated_headers.SetHeader(blink::kSecPurposeHeaderName,
kenoss5df53e052024-06-17 06:59:52774 GetSecPurposeHeaderValue(redirect_info.new_url));
Jeremy Roman45c89d012023-08-30 21:22:02775
Jeremy Roman586b5ec2024-02-13 15:14:40776 // Remove any existing client hints headers (except, below, if we still want
777 // to send this particular hint).
778 if (base::FeatureList::IsEnabled(features::kPrefetchClientHints)) {
779 const auto& client_hints = network::GetClientHintToNameMap();
780 headers_to_remove.reserve(headers_to_remove.size() + client_hints.size());
781 for (const auto& [_, header] : client_hints) {
782 headers_to_remove.push_back(header);
783 }
784 }
785
HuanPo Lind8887e32025-04-17 04:08:14786 // Sec-Speculation-Tags is set only when the prefetch is triggered
787 // by speculation rules and it is not cross-site prefetch redirection.
788 // To see more details:
789 // https://github.com/WICG/nav-speculation/blob/main/speculation-rules-tags.md#the-cross-site-case
790 headers_to_remove.push_back(blink::kSecSpeculationTagsHeaderName);
Hiroshige Hayashizaki995741b2025-08-19 18:41:13791 if (request().speculation_rules_tags().has_value() &&
HuanPo Lind8887e32025-04-17 04:08:14792 !IsCrossSiteRequest(url::Origin::Create(redirect_info.new_url))) {
HuanPo Lind8887e32025-04-17 04:08:14793 std::optional<std::string> serialized_list =
Hiroshige Hayashizaki995741b2025-08-19 18:41:13794 request().speculation_rules_tags()->ConvertStringToHeaderString();
HuanPo Lind8887e32025-04-17 04:08:14795 CHECK(serialized_list.has_value());
796 updated_headers.SetHeader(blink::kSecSpeculationTagsHeaderName,
797 serialized_list.value());
798 }
799
Jeremy Roman586b5ec2024-02-13 15:14:40800 // Then add the client hints that are appropriate for the redirect.
801 AddClientHintsHeaders(url::Origin::Create(redirect_info.new_url),
802 &updated_headers);
803
804 // To avoid spurious reordering, don't remove headers that will be updated
805 // anyway.
Andrew Rayskiyf65990362024-02-27 18:43:24806 std::erase_if(headers_to_remove, [&](const std::string& header) {
Jeremy Roman586b5ec2024-02-13 15:14:40807 return updated_headers.HasHeader(header);
808 });
809
Jeremy Roman45c89d012023-08-30 21:22:02810 // TODO(jbroman): We have several places that invoke
811 // `net::RedirectUtil::UpdateHttpRequest` and then need to do very similar
812 // work afterward. Ideally we would deduplicate these more.
813 bool should_clear_upload = false;
814 net::RedirectUtil::UpdateHttpRequest(
815 resource_request_->url, resource_request_->method, redirect_info,
Jeremy Roman586b5ec2024-02-13 15:14:40816 std::move(headers_to_remove), std::move(updated_headers),
Jeremy Roman45c89d012023-08-30 21:22:02817 &resource_request_->headers, &should_clear_upload);
818 CHECK(!should_clear_upload);
819
820 resource_request_->url = redirect_info.new_url;
821 resource_request_->method = redirect_info.new_method;
822 resource_request_->site_for_cookies = redirect_info.new_site_for_cookies;
823
824 resource_request_->trusted_params->isolation_info =
825 resource_request_->trusted_params->isolation_info.CreateForRedirect(
826 url::Origin::Create(resource_request_->url));
827
828 // TODO(jbroman): This somewhat duplicates |referrer_|. Revisit usage of that
829 // (and related data members) to see if they can/should use this data instead.
830 resource_request_->referrer = GURL(redirect_info.new_referrer);
831 resource_request_->referrer_policy = redirect_info.new_referrer_policy;
832
Liviu Tinta2e1ffe22024-06-21 21:01:33833 AddXClientDataHeader(*resource_request_.get());
834
Hiroshige Hayashizaki87a43932025-08-12 06:57:25835 redirect_chain_.push_back(std::make_unique<PrefetchSingleRedirectHop>(
Hiroshige Hayashizaki40270b72025-07-23 01:52:01836 *this, redirect_info.new_url,
kenossdc9024772025-07-01 10:28:38837 IsCrossSiteRequest(url::Origin::Create(redirect_info.new_url))));
Max Curran5d4da4b42023-03-10 23:41:46838}
839
Taiyo Mizuhashi9dfeb632024-10-24 08:44:20840bool PrefetchContainer::IsCrossSiteRequest(const url::Origin& origin) const {
Hiroshige Hayashizaki65285e32025-08-19 17:24:20841 return request().referring_origin().has_value() &&
842 !net::SchemefulSite::IsSameSite(request().referring_origin().value(),
843 origin);
Taiyo Mizuhashi9dfeb632024-10-24 08:44:20844}
845
846bool PrefetchContainer::IsCrossOriginRequest(const url::Origin& origin) const {
Hiroshige Hayashizaki65285e32025-08-19 17:24:20847 return request().referring_origin().has_value() &&
848 !request().referring_origin().value().IsSameOriginWith(origin);
Taiyo Mizuhashi9dfeb632024-10-24 08:44:20849}
850
Jeremy Romane561b412024-02-15 18:31:34851void PrefetchContainer::MarkCrossSiteContaminated() {
852 is_cross_site_contaminated_ = true;
853}
854
Liviu Tinta2e1ffe22024-06-21 21:01:33855void PrefetchContainer::AddXClientDataHeader(
Hiroshige Hayashizakid75d328a2025-08-19 18:46:15856 network::ResourceRequest& resource_request) {
857 if (request().browser_context()) {
Liviu Tinta2e1ffe22024-06-21 21:01:33858 // Add X-Client-Data header with experiment IDs from field trials.
Hiroshige Hayashizakid75d328a2025-08-19 18:46:15859 variations::AppendVariationsHeader(
860 resource_request.url,
861 request().browser_context()->IsOffTheRecord()
862 ? variations::InIncognito::kYes
863 : variations::InIncognito::kNo,
864 variations::SignedIn::kNo, &resource_request);
Liviu Tinta2e1ffe22024-06-21 21:01:33865 }
866}
867
Max Curranc4445fc2022-06-02 18:43:43868void PrefetchContainer::RegisterCookieListener(
869 network::mojom::CookieManager* cookie_manager) {
Hiroshige Hayashizaki87a43932025-08-12 06:57:25870 PrefetchSingleRedirectHop& this_prefetch =
871 GetCurrentSingleRedirectHopToPrefetch();
Hiroshige Hayashizakieec97ed12023-05-26 06:38:32872 this_prefetch.cookie_listener_ = PrefetchCookieListener::MakeAndRegister(
873 this_prefetch.url_, cookie_manager);
Max Curranc4445fc2022-06-02 18:43:43874}
875
Taiyo Mizuhashi8b5ddcc2025-02-21 22:12:12876void PrefetchContainer::PauseAllCookieListeners() {
877 // TODO(crbug.com/377440445): Consider whether we actually need to
878 // pause/resume all single prefetch's cookie listener during each single
879 // prefetch's isolated cookie copy.
Max Curran7dbdea4d2023-03-21 23:47:37880 for (const auto& single_prefetch : redirect_chain_) {
881 if (single_prefetch->cookie_listener_) {
Taiyo Mizuhashi8b5ddcc2025-02-21 22:12:12882 single_prefetch->cookie_listener_->PauseListening();
883 }
884 }
885}
886
887void PrefetchContainer::ResumeAllCookieListeners() {
888 // TODO(crbug.com/377440445): Consider whether we actually need to
889 // pause/resume all single prefetch's cookie listener during each single
890 // prefetch's isolated cookie copy.
891 for (const auto& single_prefetch : redirect_chain_) {
892 if (single_prefetch->cookie_listener_) {
893 single_prefetch->cookie_listener_->ResumeListening();
Max Curran7dbdea4d2023-03-21 23:47:37894 }
Max Curran5d4da4b42023-03-10 23:41:46895 }
Max Curranc4445fc2022-06-02 18:43:43896}
897
Hiroshige Hayashizaki638018f2023-09-12 17:05:45898void PrefetchContainer::SetStreamingURLLoader(
899 base::WeakPtr<PrefetchStreamingURLLoader> streaming_loader) {
900 // The previous streaming loader (if any) should be already deleted or to be
901 // deleted soon when the new `streaming_loader` is set here.
902 CHECK(!streaming_loader_ || streaming_loader_->IsDeletionScheduledForCHECK());
903
904 streaming_loader_ = std::move(streaming_loader);
Max Currana84311e2023-05-16 20:40:25905}
906
Taiyo Mizuhashi08c47d12025-03-05 23:46:07907base::WeakPtr<PrefetchStreamingURLLoader>
Hiroshige Hayashizaki638018f2023-09-12 17:05:45908PrefetchContainer::GetStreamingURLLoader() const {
Taiyo Mizuhashi08c47d12025-03-05 23:46:07909 // Streaming loaders already deleted or scheduled to be deleted shouldn't be
910 // used.
911 if (!streaming_loader_ || streaming_loader_->IsDeletionScheduledForCHECK()) {
912 return nullptr;
913 }
Hiroshige Hayashizaki638018f2023-09-12 17:05:45914 return streaming_loader_;
Max Curran892ca5422022-12-12 20:55:34915}
916
Hiroshige Hayashizaki5c50ec52023-09-13 00:35:49917bool PrefetchContainer::IsStreamingURLLoaderDeletionScheduledForTesting()
918 const {
919 return streaming_loader_ && streaming_loader_->IsDeletionScheduledForCHECK();
920}
921
Hiroshige Hayashizaki334d0fa2023-08-16 23:33:27922const PrefetchResponseReader* PrefetchContainer::GetNonRedirectResponseReader()
923 const {
Hiroshige Hayashizakib3e51a52025-01-18 00:17:21924 CHECK(!redirect_chain_.empty());
Hiroshige Hayashizaki334d0fa2023-08-16 23:33:27925 if (!redirect_chain_.back()->response_reader_->GetHead()) {
926 // Either the last PrefetchResponseReader is for a redirect response, or for
927 // a final response not yet receiving its header.
928 return nullptr;
929 }
930 return redirect_chain_.back()->response_reader_.get();
931}
932
kenossf7b4d60d2024-07-16 15:15:08933const network::mojom::URLResponseHead* PrefetchContainer::GetNonRedirectHead()
934 const {
935 return GetNonRedirectResponseReader()
936 ? GetNonRedirectResponseReader()->GetHead()
937 : nullptr;
938}
939
Hiroshige Hayashizaki638018f2023-09-12 17:05:45940void PrefetchContainer::CancelStreamingURLLoaderIfNotServing() {
941 if (!streaming_loader_) {
942 return;
Max Currana84311e2023-05-16 20:40:25943 }
Hiroshige Hayashizaki638018f2023-09-12 17:05:45944 streaming_loader_->CancelIfNotServing();
945 streaming_loader_.reset();
Max Curran892ca5422022-12-12 20:55:34946}
947
kenoss8fb82852025-03-11 02:20:42948void PrefetchContainer::OnDeterminedHead() {
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:32949 SetLoadState(LoadState::kDeterminedHead);
950
kenosseab3c422025-04-03 12:50:19951 if (GetNonRedirectHead()) {
952 time_header_determined_successfully_ = base::TimeTicks::Now();
953 }
954
kenossf6fbd5652024-09-04 00:40:28955 // Propagates the header to `no_vary_search_data_` if a non-redirect response
956 // header is got.
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:08957 MaybeSetNoVarySearchData();
kenossf6fbd5652024-09-04 00:40:28958
959 for (auto& observer : observers_) {
960 observer.OnDeterminedHead(*this);
961 }
962}
963
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:08964void PrefetchContainer::MaybeSetNoVarySearchData() {
kenossf6fbd5652024-09-04 00:40:28965 CHECK(!no_vary_search_data_.has_value());
kenossf7b4d60d2024-07-16 15:15:08966
967 if (!GetNonRedirectHead()) {
968 return;
969 }
970
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:08971 // RenderFrameHostImpl will be used to display error messagse in DevTools
972 // console. Can be null when the prefetch is browser-initiated.
Hiroshige Hayashizakif55280f62025-08-19 17:23:20973 RenderFrameHostImpl* rfhi_can_be_null = nullptr;
974 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
975 rfhi_can_be_null = renderer_initiator_info->GetRenderFrameHost();
976 }
Taiyo Mizuhashi9a1ab9882024-11-28 08:29:08977 no_vary_search_data_ = no_vary_search::ProcessHead(
978 *GetNonRedirectHead(), GetURL(), rfhi_can_be_null);
kenossf7b4d60d2024-07-16 15:15:08979}
980
elabadysayedf7d6b00f2025-02-05 11:27:00981void PrefetchContainer::StartTimeoutTimerIfNeeded(
Hiroshige Hayashizakid2161a2e2023-10-23 16:22:50982 base::OnceClosure on_timeout_callback) {
Hiroshige Hayashizakiaf261432025-08-19 19:04:32983 if (request().ttl().is_positive()) {
elabadysayedf7d6b00f2025-02-05 11:27:00984 CHECK(!timeout_timer_);
985 timeout_timer_ = std::make_unique<base::OneShotTimer>();
Hiroshige Hayashizakiaf261432025-08-19 19:04:32986 timeout_timer_->Start(FROM_HERE, request().ttl(),
987 std::move(on_timeout_callback));
elabadysayedf7d6b00f2025-02-05 11:27:00988 }
Hiroshige Hayashizakid2161a2e2023-10-23 16:22:50989}
990
Hiroshige Hayashizaki29e14dd2025-03-14 16:03:07991// static
992void PrefetchContainer::SetPrefetchResponseCompletedCallbackForTesting(
993 PrefetchResponseCompletedCallbackForTesting callback) {
994 GetPrefetchResponseCompletedCallbackForTesting() = // IN-TEST
995 std::move(callback);
996}
997
Hiroshige Hayashizakidb5f1962025-07-14 22:38:20998void PrefetchContainer::OnPrefetchCompleteInternal(
Hiroshige Hayashizaki77083bc82023-11-28 06:04:17999 const network::URLLoaderCompletionStatus& completion_status) {
1000 DVLOG(1) << *this << "::OnPrefetchComplete";
1001
Max Curran7d2578b2023-04-12 19:19:281002 UMA_HISTOGRAM_COUNTS_100("PrefetchProxy.Prefetch.RedirectChainSize",
1003 redirect_chain_.size());
Hiroshige Hayashizaki77083bc82023-11-28 06:04:171004
1005 if (GetNonRedirectResponseReader()) {
1006 UpdatePrefetchRequestMetrics(
Hiroshige Hayashizaki77083bc82023-11-28 06:04:171007 GetNonRedirectResponseReader()->GetHead());
1008 UpdateServingPageMetrics();
1009 } else {
Liviu Tinta1fe1a6d2023-09-20 19:44:041010 DVLOG(1) << *this << "::OnPrefetchComplete:"
1011 << "no non redirect response reader";
Hiroshige Hayashizaki77083bc82023-11-28 06:04:171012 }
1013
1014 if (IsDecoy()) {
1015 SetPrefetchStatus(PrefetchStatus::kPrefetchIsPrivacyDecoy);
Max Curran210cffa2022-09-06 22:24:311016 return;
Max Curran892ca5422022-12-12 20:55:341017 }
1018
Alison Gale81f4f2c72024-04-22 19:33:311019 // TODO(crbug.com/40250089): Call
kenoss1ce36df592024-12-03 01:04:351020 // `devtools_instrumentation::OnPrefetchBodyDataReceived()` with body of the
1021 // response.
1022 NotifyPrefetchRequestComplete(completion_status);
Hiroshige Hayashizaki77083bc82023-11-28 06:04:171023
1024 int net_error = completion_status.error_code;
1025 int64_t body_length = completion_status.decoded_body_length;
1026
1027 RecordPrefetchProxyPrefetchMainframeNetError(net_error);
1028
1029 // Updates the prefetch's status if it hasn't been updated since the request
1030 // first started. For the prefetch to reach the network stack, it must have
Hiroshige Hayashizakif1f8b3602025-01-15 11:50:491031 // `PrefetchStatus::kPrefetchNotStarted` or beyond.
Hiroshige Hayashizaki77083bc82023-11-28 06:04:171032 DCHECK(HasPrefetchStatus());
1033 if (GetPrefetchStatus() == PrefetchStatus::kPrefetchNotFinishedInTime) {
1034 SetPrefetchStatus(net_error == net::OK
1035 ? PrefetchStatus::kPrefetchSuccessful
1036 : PrefetchStatus::kPrefetchFailedNetError);
1037 UpdateServingPageMetrics();
1038 }
1039
1040 if (net_error == net::OK) {
kenosseab3c422025-04-03 12:50:191041 time_prefetch_completed_successfully_ = base::TimeTicks::Now();
Hiroshige Hayashizaki77083bc82023-11-28 06:04:171042 RecordPrefetchProxyPrefetchMainframeBodyLength(body_length);
1043 }
1044
Wayne Jackson Jr.b22d53b22024-11-15 11:40:061045 const PrefetchStatus prefetch_status = GetPrefetchStatus();
Hiroshige Hayashizakif55280f62025-08-19 17:23:201046
1047 if (prefetch_status == PrefetchStatus::kPrefetchSuccessful) {
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371048 // TODO(crbug.com/40946257): Current code doesn't support
1049 // PrefetchReferringPageMetrics when the prefetch is initiated by browser.
Hiroshige Hayashizakif55280f62025-08-19 17:23:201050 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
1051 if (renderer_initiator_info->prefetch_document_manager()) {
1052 renderer_initiator_info->prefetch_document_manager()
1053 ->OnPrefetchSuccessful(this);
1054 }
Wayne Jackson Jr.b22d53b22024-11-15 11:40:061055 }
1056 }
1057
Hiroshige Hayashizakib2a491b2025-08-19 18:55:411058 if (auto* browser_initiator_info = request().GetBrowserInitiatorInfo()) {
1059 if (auto* listener = browser_initiator_info->request_status_listener()) {
1060 switch (prefetch_status) {
1061 case PrefetchStatus::kPrefetchSuccessful:
1062 case PrefetchStatus::kPrefetchResponseUsed:
1063 listener->OnPrefetchResponseCompleted();
1064 break;
1065 case PrefetchStatus::kPrefetchFailedNon2XX: {
1066 int response_code =
1067 GetNonRedirectHead()
1068 ? GetNonRedirectHead()->headers->response_code()
1069 : 0;
1070 listener->OnPrefetchResponseServerError(response_code);
1071 break;
1072 }
1073 default:
1074 listener->OnPrefetchResponseError();
1075 break;
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371076 }
Hiroshige Hayashizaki77083bc82023-11-28 06:04:171077 }
1078 }
Hiroshige Hayashizakidb5f1962025-07-14 22:38:201079}
1080
1081void PrefetchContainer::OnPrefetchComplete(
1082 const network::URLLoaderCompletionStatus& completion_status) {
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:321083 SetLoadState(LoadState::kCompletedOrFailed);
Hiroshige Hayashizakidb5f1962025-07-14 22:38:201084 OnPrefetchCompleteInternal(completion_status);
Taiyo Mizuhashi2e51a632025-03-28 02:33:361085
kenossb4e57f72025-06-16 08:28:401086 std::optional<int> response_code = std::nullopt;
Hiroshige Hayashizakidb5f1962025-07-14 22:38:201087 int net_error = completion_status.error_code;
kenossb4e57f72025-06-16 08:28:401088 if (net_error == net::OK && GetNonRedirectHead() &&
1089 GetNonRedirectHead()->headers) {
1090 response_code = GetNonRedirectHead()->headers->response_code();
1091 }
1092 for (auto& observer : observers_) {
Hiroshige Hayashizaki6a103bad2025-07-11 02:15:321093 observer.OnPrefetchCompletedOrFailed(*this, completion_status,
1094 response_code);
kenossb4e57f72025-06-16 08:28:401095 }
1096
Taiyo Mizuhashi2e51a632025-03-28 02:33:361097 if (GetPrefetchResponseCompletedCallbackForTesting()) {
1098 GetPrefetchResponseCompletedCallbackForTesting().Run( // IN-TEST
1099 GetWeakPtr());
1100 }
Max Curran210cffa2022-09-06 22:24:311101}
1102
1103void PrefetchContainer::UpdatePrefetchRequestMetrics(
Max Curran210cffa2022-09-06 22:24:311104 const network::mojom::URLResponseHead* head) {
Liviu Tinta1fe1a6d2023-09-20 19:44:041105 DVLOG(1) << *this << "::UpdatePrefetchRequestMetrics:"
1106 << "head = " << head;
Max Curran210cffa2022-09-06 22:24:311107
1108 if (head)
1109 header_latency_ =
1110 head->load_timing.receive_headers_end - head->load_timing.request_start;
Max Curran210cffa2022-09-06 22:24:311111}
1112
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:081113PrefetchServableState PrefetchContainer::GetServableState(
Max Curranc4445fc2022-06-02 18:43:431114 base::TimeDelta cacheable_duration) const {
Hiroshige Hayashizakie556eb02023-09-13 00:20:481115 // Servable if the non-redirect response (either fully or partially
Hiroshige Hayashizaki334d0fa2023-08-16 23:33:271116 // received body) is servable.
Hiroshige Hayashizakie556eb02023-09-13 00:20:481117 if (GetNonRedirectResponseReader() &&
1118 GetNonRedirectResponseReader()->Servable(cacheable_duration)) {
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:081119 return PrefetchServableState::kServable;
Hiroshige Hayashizakie556eb02023-09-13 00:20:481120 }
1121
Liviu Tinta1fe1a6d2023-09-20 19:44:041122 DVLOG(1) << *this << "(GetServableState)"
Taiyo Mizuhashi08c47d12025-03-05 23:46:071123 << "(streaming_loader=" << GetStreamingURLLoader().get()
Hiroshige Hayashizakib3e51a52025-01-18 00:17:211124 << ", LoadState=" << load_state_ << ")";
Hiroshige Hayashizakie556eb02023-09-13 00:20:481125 // Can only block until head if the request has been started using a
1126 // streaming URL loader and head/failure/redirect hasn't been received yet.
Taiyo Mizuhashi08c47d12025-03-05 23:46:071127 if (GetStreamingURLLoader() &&
Kouhei Uenofc482142024-07-03 01:32:571128 redirect_chain_.back()->response_reader_->IsWaitingForResponse()) {
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:081129 return PrefetchServableState::kShouldBlockUntilHeadReceived;
Hiroshige Hayashizakie556eb02023-09-13 00:20:481130 }
1131
kenossc6492942025-04-10 19:36:321132 if (features::UsePrefetchPrerenderIntegration()) {
kenossb68c9e82024-10-10 10:11:151133 switch (load_state_) {
1134 case LoadState::kNotStarted:
1135 case LoadState::kEligible:
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:081136 return PrefetchServableState::kShouldBlockUntilEligibilityGot;
kenossb68c9e82024-10-10 10:11:151137 case LoadState::kFailedIneligible:
1138 case LoadState::kStarted:
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:321139 case LoadState::kDeterminedHead:
1140 case LoadState::kCompletedOrFailed:
kenossb68c9e82024-10-10 10:11:151141 case LoadState::kFailedHeldback:
1142 // nop
1143 break;
1144 }
1145 }
1146
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:081147 return PrefetchServableState::kNotServable;
Max Curran6b93426f2022-05-03 00:55:521148}
1149
Hiroshige Hayashizaki87a43932025-08-12 06:57:251150PrefetchSingleRedirectHop&
1151PrefetchContainer::GetCurrentSingleRedirectHopToPrefetch() const {
Hiroshige Hayashizaki8ce7d8d2023-05-26 00:16:321152 CHECK(redirect_chain_.size() > 0);
1153 return *redirect_chain_[redirect_chain_.size() - 1];
1154}
1155
Hiroshige Hayashizaki87a43932025-08-12 06:57:251156const PrefetchSingleRedirectHop&
1157PrefetchContainer::GetPreviousSingleRedirectHopToPrefetch() const {
Hiroshige Hayashizaki8ce7d8d2023-05-26 00:16:321158 CHECK(redirect_chain_.size() > 1);
1159 return *redirect_chain_[redirect_chain_.size() - 2];
1160}
1161
Max Curran892ca5422022-12-12 20:55:341162void PrefetchContainer::SetServingPageMetrics(
1163 base::WeakPtr<PrefetchServingPageMetricsContainer>
1164 serving_page_metrics_container) {
1165 serving_page_metrics_container_ = serving_page_metrics_container;
1166}
1167
1168void PrefetchContainer::UpdateServingPageMetrics() {
Liviu Tinta1fe1a6d2023-09-20 19:44:041169 DVLOG(1) << *this << "::UpdateServingPageMetrics:"
1170 << "serving_page_metrics_container_ = "
1171 << serving_page_metrics_container_.get();
Max Curran892ca5422022-12-12 20:55:341172 if (!serving_page_metrics_container_) {
1173 return;
1174 }
1175
1176 serving_page_metrics_container_->SetRequiredPrivatePrefetchProxy(
Hiroshige Hayashizakif55280f62025-08-19 17:23:201177 request().prefetch_type().IsProxyRequiredWhenCrossOrigin());
Max Curran892ca5422022-12-12 20:55:341178 serving_page_metrics_container_->SetPrefetchHeaderLatency(
1179 GetPrefetchHeaderLatency());
1180 if (HasPrefetchStatus()) {
1181 serving_page_metrics_container_->SetPrefetchStatus(GetPrefetchStatus());
1182 }
Liviu Tintad97a6a32022-12-08 23:28:401183}
1184
Hiroshige Hayashizaki64802cf12025-01-16 23:18:461185void PrefetchContainer::SimulatePrefetchEligibleForTest() {
Hiroshige Hayashizakib374e642025-08-19 18:53:321186 if (request().attempt()) {
1187 request().attempt()->SetEligibility(PreloadingEligibility::kEligible);
1188 request().attempt()->SetHoldbackStatus(PreloadingHoldbackStatus::kAllowed);
Adithya Srinivasana486d5e2024-03-08 15:19:321189 }
kenossb68c9e82024-10-10 10:11:151190 SetLoadState(LoadState::kEligible);
Hiroshige Hayashizakif1f8b3602025-01-15 11:50:491191 SetPrefetchStatus(PrefetchStatus::kPrefetchNotStarted);
Hiroshige Hayashizaki64802cf12025-01-16 23:18:461192}
1193
1194void PrefetchContainer::SimulatePrefetchStartedForTest() {
kenossb68c9e82024-10-10 10:11:151195 SetLoadState(LoadState::kStarted);
Adithya Srinivasana486d5e2024-03-08 15:19:321196 SetPrefetchStatus(PrefetchStatus::kPrefetchNotFinishedInTime);
1197}
1198
Hiroshige Hayashizaki64802cf12025-01-16 23:18:461199void PrefetchContainer::SimulatePrefetchCompletedForTest() {
William Liu77089052022-12-15 18:53:351200 SetPrefetchStatus(PrefetchStatus::kPrefetchSuccessful);
1201}
1202
Hiroshige Hayashizaki64802cf12025-01-16 23:18:461203void PrefetchContainer::SimulatePrefetchFailedIneligibleForTest(
kenossb68c9e82024-10-10 10:11:151204 PreloadingEligibility eligibility) {
1205 CHECK_NE(PreloadingEligibility::kEligible, eligibility);
1206
Hiroshige Hayashizakib374e642025-08-19 18:53:321207 if (request().attempt()) {
1208 request().attempt()->SetEligibility(eligibility);
kenossb68c9e82024-10-10 10:11:151209 }
1210 SetLoadState(LoadState::kFailedIneligible);
1211}
1212
kenoss8fb82852025-03-11 02:20:421213void PrefetchContainer::OnDetectedCookiesChange(
kenossb1c65dbc2024-12-13 08:23:491214 std::optional<bool>
1215 is_unblock_for_cookies_changed_triggered_by_this_prefetch_container) {
kenoss8fb82852025-03-11 02:20:421216 // Multiple `PrefetchMatchResolver` can wait the same `PrefetchContainer`. So,
1217 // `OnDetectedCookiesChange()` can be called multiple times,
kenossf6fbd5652024-09-04 00:40:281218 if (on_detected_cookies_change_called_) {
1219 return;
1220 }
kenoss8fb82852025-03-11 02:20:421221 on_detected_cookies_change_called_ = true;
kenossf6fbd5652024-09-04 00:40:281222
kenossb1c65dbc2024-12-13 08:23:491223 // There are cases that `prefetch_status_` is failure but this method is
1224 // called. For more details, see
1225 // https://docs.google.com/document/d/1G48SaWbdOy1yNBT1wio2IHVuUtddF5VLFsT6BRSYPMI/edit?tab=t.hpkotaxo7tfh#heading=h.woaoy8erwx63
1226 //
1227 // To prevent crash, we don't call `SetPrefetchStatus()`.
1228 if (prefetch_status_ &&
1229 TriggeringOutcomeFromStatus(prefetch_status_.value()) ==
1230 PreloadingTriggeringOutcome::kFailure) {
1231 SCOPED_CRASH_KEY_NUMBER("PrefetchContainer", "ODCC2_from",
1232 static_cast<int>(prefetch_status_.value()));
1233 if (is_unblock_for_cookies_changed_triggered_by_this_prefetch_container
1234 .has_value()) {
1235 SCOPED_CRASH_KEY_BOOL(
1236 "PrefetchContainer", "ODCC2_iufcctbtpc",
1237 is_unblock_for_cookies_changed_triggered_by_this_prefetch_container
1238 .value());
1239 }
1240 base::debug::DumpWithoutCrashing();
1241 return;
1242 }
1243
kenoss8fb82852025-03-11 02:20:421244 CHECK_NE(GetPrefetchStatus(), PrefetchStatus::kPrefetchNotUsedCookiesChanged);
1245 SetPrefetchStatus(PrefetchStatus::kPrefetchNotUsedCookiesChanged);
1246 UpdateServingPageMetrics();
1247 CancelStreamingURLLoaderIfNotServing();
kenossf6fbd5652024-09-04 00:40:281248}
1249
Wayne Jackson Jr.03a15fa62024-09-16 14:42:151250void PrefetchContainer::OnPrefetchStarted() {
1251 SetLoadState(PrefetchContainer::LoadState::kStarted);
kenosseab3c422025-04-03 12:50:191252 time_prefetch_started_ = base::TimeTicks::Now();
Wayne Jackson Jr.03a15fa62024-09-16 14:42:151253}
1254
Kevin McNee06824c72024-02-06 18:59:521255bool PrefetchContainer::HasSameReferringURLForMetrics(
1256 const PrefetchContainer& other) const {
Hiroshige Hayashizakif55280f62025-08-19 17:23:201257 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
1258 if (auto* other_renderer_initiator_info =
1259 other.request().GetRendererInitiatorInfo()) {
1260 return renderer_initiator_info->url_hash() ==
1261 other_renderer_initiator_info->url_hash();
1262 }
1263 }
1264 return false;
1265}
1266
1267bool PrefetchContainer::HasSameReferringRenderFrameHostIdForMetrics(
1268 const PrefetchContainer& other) const {
1269 if (auto* renderer_initiator_info = request().GetRendererInitiatorInfo()) {
1270 if (auto* other_renderer_initiator_info =
1271 other.request().GetRendererInitiatorInfo()) {
1272 return renderer_initiator_info->GetRenderFrameHostId() ==
1273 other_renderer_initiator_info->GetRenderFrameHostId();
1274 }
1275 }
1276 return false;
Kevin McNee06824c72024-02-06 18:59:521277}
1278
Jeremy Roman45c89d012023-08-30 21:22:021279GURL PrefetchContainer::GetCurrentURL() const {
Hiroshige Hayashizaki87a43932025-08-12 06:57:251280 return GetCurrentSingleRedirectHopToPrefetch().url_;
Jeremy Roman45c89d012023-08-30 21:22:021281}
1282
1283GURL PrefetchContainer::GetPreviousURL() const {
Hiroshige Hayashizaki87a43932025-08-12 06:57:251284 return GetPreviousSingleRedirectHopToPrefetch().url_;
Jeremy Roman45c89d012023-08-30 21:22:021285}
1286
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581287bool PrefetchContainer::IsIsolatedNetworkContextRequiredForCurrentPrefetch()
1288 const {
Hiroshige Hayashizaki87a43932025-08-12 06:57:251289 const PrefetchSingleRedirectHop& this_prefetch =
1290 GetCurrentSingleRedirectHopToPrefetch();
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581291 return this_prefetch.is_isolated_network_context_required_;
Max Curran48700962023-05-15 18:35:521292}
1293
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581294bool PrefetchContainer::IsIsolatedNetworkContextRequiredForPreviousRedirectHop()
1295 const {
Hiroshige Hayashizaki87a43932025-08-12 06:57:251296 const PrefetchSingleRedirectHop& previous_prefetch =
1297 GetPreviousSingleRedirectHopToPrefetch();
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581298 return previous_prefetch.is_isolated_network_context_required_;
1299}
1300
Hiroshige Hayashizaki73e42892023-06-26 16:48:481301base::WeakPtr<PrefetchResponseReader>
1302PrefetchContainer::GetResponseReaderForCurrentPrefetch() {
Hiroshige Hayashizaki87a43932025-08-12 06:57:251303 const PrefetchSingleRedirectHop& this_prefetch =
1304 GetCurrentSingleRedirectHopToPrefetch();
Hiroshige Hayashizaki44862202023-09-18 21:53:301305 CHECK(this_prefetch.response_reader_);
Hiroshige Hayashizaki73e42892023-06-26 16:48:481306 return this_prefetch.response_reader_->GetWeakPtr();
1307}
1308
Max Curranc0137502023-05-02 18:06:221309bool PrefetchContainer::IsProxyRequiredForURL(const GURL& url) const {
Taiyo Mizuhashi9dfeb632024-10-24 08:44:201310 return IsCrossOriginRequest(url::Origin::Create(url)) &&
Hiroshige Hayashizakif55280f62025-08-19 17:23:201311 request().prefetch_type().IsProxyRequiredWhenCrossOrigin();
Max Curranc0137502023-05-02 18:06:221312}
1313
Jeremy Roman45c89d012023-08-30 21:22:021314void PrefetchContainer::MakeResourceRequest(
1315 const net::HttpRequestHeaders& additional_headers) {
1316 // |AddRedirectHop| updates this request later on. Anything here that should
1317 // be changed on redirect should happen there.
1318
1319 const GURL& url = GetURL();
1320 url::Origin origin = url::Origin::Create(url);
1321 net::IsolationInfo isolation_info = net::IsolationInfo::Create(
1322 net::IsolationInfo::RequestType::kMainFrame, origin, origin,
1323 net::SiteForCookies::FromOrigin(origin));
Jeremy Roman45c89d012023-08-30 21:22:021324
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301325 auto priority = [&] {
Hiroshige Hayashizaki8b59d9082025-08-19 18:58:041326 if (request().priority().has_value()) {
1327 switch (request().priority().value()) {
Taiyo Mizuhashi39f09192025-06-23 08:57:131328 case PrefetchPriority::kLow:
1329 return net::RequestPriority::IDLE;
1330 case PrefetchPriority::kMedium:
1331 return net::RequestPriority::LOW;
1332 case PrefetchPriority::kHigh:
1333 return net::RequestPriority::MEDIUM;
1334 case PrefetchPriority::kHighest:
1335 return net::RequestPriority::HIGHEST;
1336 }
1337 }
1338
1339 // TODO(crbug.com/426404355): Migrate to use `PrefetchPriority`.
Hiroshige Hayashizakif55280f62025-08-19 17:23:201340 if (IsSpeculationRuleType(request().prefetch_type().trigger_type())) {
Takashi Nakayama978f0a152025-06-17 08:26:251341 // This may seem inverted (surely immediate prefetches would be higher
Taiyo Mizuhashi0889c072024-03-13 13:29:201342 // priority), but the fact that we're doing this at all for more
1343 // conservative candidates suggests a strong engagement signal.
1344 //
Alison Gale81f4f2c72024-04-22 19:33:311345 // TODO(crbug.com/40276985): Ideally, we would actually use a combination
Taiyo Mizuhashi0889c072024-03-13 13:29:201346 // of the actual engagement seen (rather than the minimum required to
1347 // trigger the candidate) and the declared eagerness, and update them as
1348 // the prefetch becomes increasingly likely.
1349 blink::mojom::SpeculationEagerness eagerness =
Hiroshige Hayashizakif55280f62025-08-19 17:23:201350 request().prefetch_type().GetEagerness();
Taiyo Mizuhashi0889c072024-03-13 13:29:201351 switch (eagerness) {
1352 case blink::mojom::SpeculationEagerness::kConservative:
1353 return net::RequestPriority::MEDIUM;
1354 case blink::mojom::SpeculationEagerness::kModerate:
1355 return net::RequestPriority::LOW;
Takashi Nakayama9eb7988f2025-06-25 06:14:001356 // TODO(crbug.com/40287486, crbug.com/406927300): Set appropriate value
1357 // after changing the behavior for `kEager`
1358 case blink::mojom::SpeculationEagerness::kEager:
Takashi Nakayama978f0a152025-06-17 08:26:251359 case blink::mojom::SpeculationEagerness::kImmediate:
Taiyo Mizuhashi0889c072024-03-13 13:29:201360 return net::RequestPriority::IDLE;
1361 }
1362 } else {
Taiyo Mizuhashi2bef6682025-03-07 08:58:451363 if (base::FeatureList::IsEnabled(
1364 features::kPrefetchNetworkPriorityForEmbedders)) {
1365 return net::RequestPriority::MEDIUM;
1366 } else {
1367 return net::RequestPriority::IDLE;
1368 }
Taiyo Mizuhashi0889c072024-03-13 13:29:201369 }
1370 }();
Jeremy Roman45c89d012023-08-30 21:22:021371
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301372 mojo::PendingRemote<network::mojom::DevToolsObserver>
1373 devtools_observer_remote;
kenoss1ce36df592024-12-03 01:04:351374 if (std::optional<mojo::PendingRemote<network::mojom::DevToolsObserver>>
1375 devtools_observer = MakeSelfOwnedNetworkServiceDevToolsObserver()) {
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301376 devtools_observer_remote = std::move(devtools_observer.value());
Jeremy Roman45c89d012023-08-30 21:22:021377 }
1378
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301379 // If we ever implement prefetching for subframes, this value should be
1380 // reconsidered, as this causes us to reset the site for cookies on cross-site
1381 // redirect.
1382 const bool is_main_frame = true;
1383
Hiroshige Hayashizakif55280f62025-08-19 17:23:201384 auto resource_request = CreateResourceRequestForNavigation(
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301385 net::HttpRequestHeaders::kGetMethod, url,
Hiroshige Hayashizakie3f677492025-01-07 05:50:251386 network::mojom::RequestDestination::kDocument, referrer_, isolation_info,
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301387 std::move(devtools_observer_remote), priority, is_main_frame);
1388
1389 // Note: Even without LOAD_DISABLE_CACHE, a cross-site prefetch uses a
1390 // separate network context, which means responses cached before the prefetch
1391 // are not visible to the prefetch, and anything cached by this request will
1392 // not be visible outside of the network context.
Hiroshige Hayashizakif55280f62025-08-19 17:23:201393 resource_request->load_flags = net::LOAD_PREFETCH;
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301394
Hiroshige Hayashizakieaccf7392025-08-19 18:56:021395 resource_request->headers.MergeFrom(request().additional_headers());
Hiroshige Hayashizakif55280f62025-08-19 17:23:201396 resource_request->headers.MergeFrom(additional_headers);
Steven Weiac284752025-05-29 21:10:041397 if (!base::FeatureList::IsEnabled(
1398 blink::features::kRemovePurposeHeaderForPrefetch)) {
Hiroshige Hayashizakif55280f62025-08-19 17:23:201399 resource_request->headers.SetHeader(blink::kPurposeHeaderName,
1400 blink::kSecPurposePrefetchHeaderValue);
Steven Weiac284752025-05-29 21:10:041401 }
Hiroshige Hayashizakif55280f62025-08-19 17:23:201402 resource_request->headers.SetHeader(blink::kSecPurposeHeaderName,
1403 GetSecPurposeHeaderValue(url));
1404 resource_request->headers.SetHeader("Upgrade-Insecure-Requests", "1");
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301405
HuanPo Lin740620b2025-03-21 12:37:261406 // Sec-Speculation-Tags is set only when the prefetch is triggered
HuanPo Lind8887e32025-04-17 04:08:141407 // by speculation rules and it is not cross-site prefetch.
1408 // To see more details:
1409 // https://github.com/WICG/nav-speculation/blob/main/speculation-rules-tags.md#the-cross-site-case
Hiroshige Hayashizaki995741b2025-08-19 18:41:131410 if (request().speculation_rules_tags().has_value() &&
1411 !IsCrossSiteRequest(origin)) {
HuanPo Lin740620b2025-03-21 12:37:261412 std::optional<std::string> serialized_list =
Hiroshige Hayashizaki995741b2025-08-19 18:41:131413 request().speculation_rules_tags()->ConvertStringToHeaderString();
HuanPo Lin740620b2025-03-21 12:37:261414 CHECK(serialized_list.has_value());
Hiroshige Hayashizakif55280f62025-08-19 17:23:201415 resource_request->headers.SetHeader(blink::kSecSpeculationTagsHeaderName,
1416 serialized_list.value());
HuanPo Lin740620b2025-03-21 12:37:261417 }
1418
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301419 // There are sometimes other headers that are set during navigation. These
1420 // aren't yet supported for prefetch, including browsing topics.
1421
Hiroshige Hayashizakif55280f62025-08-19 17:23:201422 resource_request->devtools_request_id = RequestId();
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301423
Hiroshige Hayashizakif55280f62025-08-19 17:23:201424 AddClientHintsHeaders(origin, &resource_request->headers);
Hiroshige Hayashizaki4a9ddf182025-08-19 19:04:471425 if (request().should_append_variations_header()) {
Hiroshige Hayashizakif55280f62025-08-19 17:23:201426 AddXClientDataHeader(*resource_request.get());
elabadysayed90651cc2025-03-28 00:23:221427 }
Hiroshige Hayashizakieb3867c2024-12-26 07:30:301428
Hiroshige Hayashizaki9b63d50a2025-05-21 16:22:011429 // `URLLoaderNetworkServiceObserver`
Hiroshige Hayashizakif55280f62025-08-19 17:23:201430 // (`resource_request->trusted_params->url_loader_network_observer`) is NOT
1431 // set here, because for prefetching request we don't want to ask users e.g.
1432 // for authentication/cert errors, and instead make the prefetch fail. Because
1433 // of this, `ServiceWorkerClient::GetOngoingNavigationRequestBeforeCommit()`
1434 // is never called. `NavPrefetchBrowserTest` has the corresponding test
1435 // coverage.
Hiroshige Hayashizaki9b63d50a2025-05-21 16:22:011436
Hiroshige Hayashizakif55280f62025-08-19 17:23:201437 resource_request_ = std::move(resource_request);
Jeremy Roman45c89d012023-08-30 21:22:021438}
1439
Devlin Cronin7f318c12023-06-09 00:57:011440void PrefetchContainer::UpdateReferrer(
1441 const GURL& new_referrer_url,
1442 const network::mojom::ReferrerPolicy& new_referrer_policy) {
1443 referrer_.url = new_referrer_url;
1444 referrer_.policy = new_referrer_policy;
1445}
1446
Hiroshige Hayashizaki1d9c40b82025-08-19 18:44:441447const PrefetchKey& PrefetchContainer::key() const {
1448 return request().key();
1449}
1450
1451const GURL& PrefetchContainer::GetURL() const {
1452 return request().key().url();
1453}
1454
Hiroshige Hayashizaki88d1017b2025-08-19 17:24:321455const std::optional<net::HttpNoVarySearchData>&
1456PrefetchContainer::GetNoVarySearchHint() const {
1457 return request().no_vary_search_hint();
1458}
1459
Jeremy Roman586b5ec2024-02-13 15:14:401460void PrefetchContainer::AddClientHintsHeaders(
1461 const url::Origin& origin,
1462 net::HttpRequestHeaders* request_headers) {
1463 if (!base::FeatureList::IsEnabled(features::kPrefetchClientHints)) {
1464 return;
1465 }
Hiroshige Hayashizakid75d328a2025-08-19 18:46:151466 if (!request().browser_context()) {
Jeremy Roman586b5ec2024-02-13 15:14:401467 return;
1468 }
1469 ClientHintsControllerDelegate* client_hints_delegate =
Hiroshige Hayashizakid75d328a2025-08-19 18:46:151470 request().browser_context()->GetClientHintsControllerDelegate();
Jeremy Roman586b5ec2024-02-13 15:14:401471 if (!client_hints_delegate) {
1472 return;
1473 }
1474
Alison Gale923a33e2024-04-22 23:34:281475 // TODO(crbug.com/41497015): Consider supporting UA override mode here
Jeremy Roman586b5ec2024-02-13 15:14:401476 const bool is_ua_override_on = false;
1477 net::HttpRequestHeaders client_hints_headers;
Hiroshige Hayashizaki0eab2b12025-08-19 18:56:511478 if (request().is_javascript_enabled()) {
Kouhei Ueno27f86cb2025-02-06 09:03:361479 // Historically, `AddClientHintsHeadersToPrefetchNavigation` added
Hiroshige Hayashizaki0eab2b12025-08-19 18:56:511480 // Client Hints headers iff `request().is_javascript_enabled()`, so the `if`
1481 // block here is to persist the behavior.
Kouhei Ueno295957b2025-02-06 11:14:581482 // TODO(crbug.com/394716357): Revisit if we really want to allow prefetch
1483 // for non-Javascript enabled profile/origins.
Kouhei Ueno27f86cb2025-02-06 09:03:361484 AddClientHintsHeadersToPrefetchNavigation(
Hiroshige Hayashizakid75d328a2025-08-19 18:46:151485 origin, &client_hints_headers, request().browser_context(),
1486 client_hints_delegate, is_ua_override_on);
Kouhei Ueno27f86cb2025-02-06 09:03:361487 }
Jeremy Roman586b5ec2024-02-13 15:14:401488
1489 // Merge in the client hints which are suitable to include given this is a
1490 // prefetch, and potentially a cross-site only. (This logic might need to be
1491 // revisited if we ever supported prefetching in another site's partition,
1492 // such as in a subframe.)
Taiyo Mizuhashi9dfeb632024-10-24 08:44:201493 const bool is_cross_site = IsCrossSiteRequest(origin);
Jeremy Roman586b5ec2024-02-13 15:14:401494 const auto cross_site_behavior =
1495 features::kPrefetchClientHintsCrossSiteBehavior.Get();
Taiyo Mizuhashi9dfeb632024-10-24 08:44:201496 if (!is_cross_site ||
Jeremy Roman586b5ec2024-02-13 15:14:401497 cross_site_behavior ==
1498 features::PrefetchClientHintsCrossSiteBehavior::kAll) {
1499 request_headers->MergeFrom(client_hints_headers);
1500 } else if (cross_site_behavior ==
1501 features::PrefetchClientHintsCrossSiteBehavior::kLowEntropy) {
Jeremy Roman586b5ec2024-02-13 15:14:401502 for (const auto& [ch, header] : network::GetClientHintToNameMap()) {
Chris Fredricksond923c682024-07-30 18:19:341503 if (blink::IsClientHintSentByDefault(ch)) {
1504 std::optional<std::string> header_value =
1505 client_hints_headers.GetHeader(header);
1506 if (header_value) {
1507 request_headers->SetHeader(header, std::move(header_value).value());
1508 }
Jeremy Roman586b5ec2024-02-13 15:14:401509 }
1510 }
1511 }
1512}
1513
Hiroshige Hayashizaki3876eee2023-03-27 04:42:041514std::ostream& operator<<(std::ostream& ostream,
1515 const PrefetchContainer& prefetch_container) {
1516 return ostream << "PrefetchContainer[" << &prefetch_container
kenossaf6e0ae2024-09-05 03:06:501517 << ", Key=" << prefetch_container.key() << "]";
Hiroshige Hayashizakif7f16b82023-10-10 01:57:471518}
1519
1520std::ostream& operator<<(std::ostream& ostream,
Kouhei Ueno929ee8e12024-07-05 03:09:561521 PrefetchContainer::LoadState state) {
1522 switch (state) {
1523 case PrefetchContainer::LoadState::kNotStarted:
1524 return ostream << "NotStarted";
1525 case PrefetchContainer::LoadState::kEligible:
1526 return ostream << "Eligible";
1527 case PrefetchContainer::LoadState::kFailedIneligible:
1528 return ostream << "FailedIneligible";
1529 case PrefetchContainer::LoadState::kStarted:
1530 return ostream << "Started";
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:321531 case PrefetchContainer::LoadState::kDeterminedHead:
1532 return ostream << "DeterminedHead";
1533 case PrefetchContainer::LoadState::kCompletedOrFailed:
1534 return ostream << "CompletedOrFailed";
Kouhei Ueno929ee8e12024-07-05 03:09:561535 case PrefetchContainer::LoadState::kFailedHeldback:
1536 return ostream << "FailedHeldback";
1537 }
1538}
1539
kenoss5df53e052024-06-17 06:59:521540const char* PrefetchContainer::GetSecPurposeHeaderValue(
1541 const GURL& request_url) const {
Hiroshige Hayashizaki1ec17a52025-08-19 18:49:341542 switch (request().preload_pipeline_info().planned_max_preloading_type()) {
kenoss8644f6bf2025-02-10 05:51:441543 case PreloadingType::kPrefetch:
1544 if (IsProxyRequiredForURL(request_url)) {
David Risneya1dd2bf2025-03-06 03:40:421545 return blink::kSecPurposePrefetchAnonymousClientIpHeaderValue;
kenoss8644f6bf2025-02-10 05:51:441546 } else {
David Risneya1dd2bf2025-03-06 03:40:421547 return blink::kSecPurposePrefetchHeaderValue;
kenoss8644f6bf2025-02-10 05:51:441548 }
Lingqi Chi844e05d42025-07-23 02:50:501549 case PreloadingType::kPrerenderUntilScript:
kenoss8644f6bf2025-02-10 05:51:441550 case PreloadingType::kPrerender:
1551 if (IsProxyRequiredForURL(request_url)) {
1552 // Note that this path would be reachable if a prefetch ahead of
1553 // prerender were triggered with a speculation candidate with
1554 // `requires_anonymous_client_ip_when_cross_origin`. But such
1555 // Speculation Rules are discarded in blink.
1556 //
1557 // See
1558 // https://github.com/WICG/nav-speculation/blob/main/triggers.md#requirements
Peter Boström01ab59a2024-08-15 02:39:491559 NOTREACHED();
kenoss8644f6bf2025-02-10 05:51:441560 } else {
David Risneya1dd2bf2025-03-06 03:40:421561 return blink::kSecPurposePrefetchPrerenderHeaderValue;
kenoss8644f6bf2025-02-10 05:51:441562 }
1563 case PreloadingType::kUnspecified:
1564 case PreloadingType::kPreconnect:
1565 case PreloadingType::kNoStatePrefetch:
1566 case PreloadingType::kLinkPreview:
1567 NOTREACHED();
kenoss5df53e052024-06-17 06:59:521568 }
1569}
1570
Wayne Jackson Jr.00e3e8b2024-09-25 12:35:561571void PrefetchContainer::OnInitialPrefetchFailedIneligible(
1572 PreloadingEligibility eligibility) {
1573 CHECK(redirect_chain_.size() == 1);
1574 CHECK_NE(eligibility, PreloadingEligibility::kEligible);
Hiroshige Hayashizakib2a491b2025-08-19 18:55:411575 if (auto* browser_initiator_info = request().GetBrowserInitiatorInfo()) {
1576 if (auto* listener = browser_initiator_info->request_status_listener()) {
1577 listener->OnPrefetchStartFailedGeneric();
1578 }
Wayne Jackson Jr.00e3e8b2024-09-25 12:35:561579 }
1580}
1581
kenossf6fbd5652024-09-04 00:40:281582void PrefetchContainer::AddObserver(Observer* observer) {
kenossf6fbd5652024-09-04 00:40:281583 observers_.AddObserver(observer);
1584}
1585
1586void PrefetchContainer::RemoveObserver(Observer* observer) {
kenossf6fbd5652024-09-04 00:40:281587 observers_.RemoveObserver(observer);
1588}
1589
kenossda3d13b2024-08-15 16:31:191590bool PrefetchContainer::IsExactMatch(const GURL& url) const {
1591 return url == GetURL();
1592}
1593
1594bool PrefetchContainer::IsNoVarySearchHeaderMatch(const GURL& url) const {
1595 const std::optional<net::HttpNoVarySearchData>& no_vary_search_data =
1596 GetNoVarySearchData();
1597 return no_vary_search_data &&
1598 no_vary_search_data->AreEquivalent(url, GetURL());
1599}
1600
kenossb9b40632024-10-01 15:43:241601bool PrefetchContainer::ShouldWaitForNoVarySearchHeader(const GURL& url) const {
1602 const std::optional<net::HttpNoVarySearchData>& no_vary_search_hint =
Hiroshige Hayashizaki88d1017b2025-08-19 17:24:321603 request().no_vary_search_hint();
kenossb9b40632024-10-01 15:43:241604 return !GetNonRedirectHead() && no_vary_search_hint &&
1605 no_vary_search_hint->AreEquivalent(url, GetURL());
1606}
1607
kenossf6fbd5652024-09-04 00:40:281608void PrefetchContainer::OnUnregisterCandidate(
1609 const GURL& navigated_url,
1610 bool is_served,
Taiyo Mizuhashi09f571f2025-08-04 16:05:541611 PrefetchPotentialCandidateServingResult matching_result,
kenoss4bdf2d82025-05-27 01:58:451612 bool is_nav_prerender,
kenossf6fbd5652024-09-04 00:40:281613 std::optional<base::TimeDelta> blocked_duration) {
1614 // Note that this method can be called with `is_in_dtor_` true.
1615 //
1616 // TODO(crbug.com/356314759): Avoid calling this with `is_in_dtor_`
1617 // true.
1618
kenossf6fbd5652024-09-04 00:40:281619 if (is_served) {
Taiyo Mizuhashia009caf2025-08-05 07:34:221620 served_count_++;
kenoss8fb82852025-03-11 02:20:421621
1622 UMA_HISTOGRAM_COUNTS_100("PrefetchProxy.AfterClick.RedirectChainSize",
1623 redirect_chain_.size());
kenossf6fbd5652024-09-04 00:40:281624 }
1625
kenoss4bdf2d82025-05-27 01:58:451626 RecordPrefetchMatchingBlockedNavigationHistogram(blocked_duration.has_value(),
1627 is_nav_prerender);
kenossf6fbd5652024-09-04 00:40:281628
kenoss4bdf2d82025-05-27 01:58:451629 RecordBlockUntilHeadDurationHistogram(blocked_duration, is_served,
1630 is_nav_prerender);
kenossf6fbd5652024-09-04 00:40:281631
Taiyo Mizuhashi09f571f2025-08-04 16:05:541632 RecordPrefetchPotentialCandidateServingResultHistogram(matching_result);
1633
kenossc6c712a2025-03-10 23:17:021634 // Note that `PreloadingAttemptImpl::SetIsAccurateTriggering()` is called for
1635 // prefetch in
1636 //
1637 // - A. `PreloadingDataImpl::DidStartNavigation()`
1638 // - B. Here
1639 //
1640 // A covers prefetches that satisfy `bool(GetNonRedirectHead())` at that
1641 // timing. B covers almost all ones that were once potentially matching to the
1642 // navigation, including that was `kBlockUntilHead` state.
1643 //
1644 // Note that multiple calls are safe and set a correct value.
1645 //
1646 // Historical note: Before No-Vary-Search hint, the decision to use a
1647 // prefetched response was made at A. With No-Vary-Search hint the decision to
1648 // use an in-flight prefetched response is delayed until the headers are
1649 // received from the server. This happens after `DidStartNavigation()`. At
1650 // this point in the code we have already decided we are going to use the
1651 // prefetch, so we can safely call `SetIsAccurateTriggering()`.
Hiroshige Hayashizakib374e642025-08-19 18:53:321652 if (request().attempt()) {
1653 static_cast<PreloadingAttemptImpl*>(request().attempt())
kenossf6fbd5652024-09-04 00:40:281654 ->SetIsAccurateTriggering(navigated_url);
1655 }
1656}
1657
kenossb68c9e82024-10-10 10:11:151658void PrefetchContainer::MigrateNewlyAdded(
1659 std::unique_ptr<PrefetchContainer> added) {
kenoss4858a802024-10-11 06:50:561660 // `inherited_preload_pipeline_infos_` increases only if it is managed under
1661 // `PrefetchService`.
1662 CHECK(added->inherited_preload_pipeline_infos_.empty());
1663
1664 // Propagate eligibility (and status) to `added`.
1665 //
1666 // Assume we don't. (*) case is problematic.
1667 //
1668 // - If eligibility is not got, eligibility and status will be propagated by
1669 // the following `OnEligibilityCheckComplete()` and
1670 // `SetPrefetchStatusWithoutUpdatingTriggeringOutcome()`.
1671 // - If eligibility is got and ineligible, this `PrefetchContainer` is
1672 // `kNotServed` and `MigrateNewlyAdded()` is not called.
1673 // - If eligibility is got and `kEligible`:
1674 // - If status is not got, status will be propagated by the following
1675 // `SetPrefetchStatusWithoutUpdatingTriggeringOutcome()`.
1676 // - If status is eventually `kPrefetchSuccessful` or
1677 // `kPrefetchResponseUsed`, `kPrefetchResponseUsed` will be propagated
1678 // at the prefetch matching end.
1679 // - If status is eventually failure, status is propagated, but
1680 // eligibility is `kUnspecified`. (*)
1681 // - If status is got and `kPrefetchSuccessful` or `kPrefetchResponseUsed`,
1682 // `kPrefetchResponseUsed` will be propagated at the prefetch matching
1683 // end.
1684 // - If status is got and failure, this `PrefetchContainer` is `kNotServed`
1685 // and `MigrateNewlyAdded()` is not called.
1686 //
1687 // In (*), `PrerenderHost` have to cancel prerender with eligibility
1688 // `kUnspecified` and status failure. It's relatively complicated condition.
1689 // See a test
1690 // `PrerendererImplBrowserTestPrefetchAhead.PrefetchMigratedPrefetchFailurePrerenderFailure`.
1691 //
1692 // To make things simple, we propagate both eligibility and status.
Hiroshige Hayashizaki1ec17a52025-08-19 18:49:341693 scoped_refptr<PreloadPipelineInfoImpl> added_preload_pipeline_info =
1694 base::WrapRefCounted(&added->request().preload_pipeline_info());
1695
1696 added_preload_pipeline_info->SetPrefetchEligibility(
1697 request().preload_pipeline_info().prefetch_eligibility());
1698 if (auto prefetch_status =
1699 request().preload_pipeline_info().prefetch_status()) {
1700 added_preload_pipeline_info->SetPrefetchStatus(*prefetch_status);
kenoss4858a802024-10-11 06:50:561701 }
1702
1703 inherited_preload_pipeline_infos_.push_back(
Hiroshige Hayashizaki1ec17a52025-08-19 18:49:341704 std::move(added_preload_pipeline_info));
1705
kenossb68c9e82024-10-10 10:11:151706 is_likely_ahead_of_prerender_ |= added->is_likely_ahead_of_prerender_;
1707}
1708
kenoss1ce36df592024-12-03 01:04:351709void PrefetchContainer::NotifyPrefetchRequestWillBeSent(
1710 const network::mojom::URLResponseHeadPtr* redirect_head) {
1711 if (IsDecoy()) {
1712 return;
1713 }
1714
Hiroshige Hayashizakif55280f62025-08-19 17:23:201715 auto* renderer_initiator_info = request().GetRendererInitiatorInfo();
1716 if (!renderer_initiator_info) {
1717 // Don't emit CDP events if the trigger is not speculation rules.
kenoss1ce36df592024-12-03 01:04:351718 return;
1719 }
Hiroshige Hayashizakif55280f62025-08-19 17:23:201720
1721 auto* rfh = renderer_initiator_info->GetRenderFrameHost();
1722 auto* ftn = FrameTreeNode::From(rfh);
1723 if (!rfh) {
1724 // Don't emit CDP events if the initiator document isn't alive.
1725 return;
1726 }
kenoss1ce36df592024-12-03 01:04:351727
1728 if (redirect_head && *redirect_head) {
1729 const network::mojom::URLResponseHeadDevToolsInfoPtr info =
1730 network::ExtractDevToolsInfo(**redirect_head);
1731 const GURL url = GetPreviousURL();
1732 std::pair<const GURL&, const network::mojom::URLResponseHeadDevToolsInfo&>
1733 redirect_info{url, *info.get()};
1734 devtools_instrumentation::OnPrefetchRequestWillBeSent(
1735 *ftn, RequestId(), rfh->GetLastCommittedURL(), *GetResourceRequest(),
1736 std::move(redirect_info));
1737 } else {
1738 devtools_instrumentation::OnPrefetchRequestWillBeSent(
1739 *ftn, RequestId(), rfh->GetLastCommittedURL(), *GetResourceRequest(),
1740 std::nullopt);
1741 }
1742}
1743
1744void PrefetchContainer::NotifyPrefetchResponseReceived(
1745 const network::mojom::URLResponseHead& head) {
1746 // Ensured by the caller `PrefetchService::OnPrefetchResponseStarted()`.
1747 CHECK(!IsDecoy());
1748
Taiyo Mizuhashi98ecb382025-06-03 15:24:551749 time_url_request_started_ = head.load_timing.request_start;
1750
1751 // DevTools plumbing.
Hiroshige Hayashizakif55280f62025-08-19 17:23:201752 auto* renderer_initiator_info = request().GetRendererInitiatorInfo();
1753 if (!renderer_initiator_info) {
1754 // Don't emit CDP events if the trigger is not speculation rules.
kenoss1ce36df592024-12-03 01:04:351755 return;
1756 }
Hiroshige Hayashizakif55280f62025-08-19 17:23:201757
1758 auto* ftn =
1759 FrameTreeNode::From(renderer_initiator_info->GetRenderFrameHost());
1760 if (!ftn) {
1761 // Don't emit CDP events if the initiator document isn't alive.
1762 return;
1763 }
1764
kenoss1ce36df592024-12-03 01:04:351765 devtools_instrumentation::OnPrefetchResponseReceived(ftn, RequestId(),
1766 GetCurrentURL(), head);
1767}
1768
1769void PrefetchContainer::NotifyPrefetchRequestComplete(
1770 const network::URLLoaderCompletionStatus& completion_status) {
1771 // Ensured by the caller `PrefetchService::OnPrefetchResponseStarted()`.
1772 CHECK(!IsDecoy());
1773
Hiroshige Hayashizakif55280f62025-08-19 17:23:201774 auto* renderer_initiator_info = request().GetRendererInitiatorInfo();
1775 if (!renderer_initiator_info) {
1776 // Don't emit CDP events if the trigger is not speculation rules.
1777 return;
1778 }
1779
1780 auto* ftn =
1781 FrameTreeNode::From(renderer_initiator_info->GetRenderFrameHost());
kenoss1ce36df592024-12-03 01:04:351782 if (!ftn) {
Hiroshige Hayashizakif55280f62025-08-19 17:23:201783 // Don't emit CDP events if the initiator document isn't alive.
kenoss1ce36df592024-12-03 01:04:351784 return;
1785 }
1786
1787 devtools_instrumentation::OnPrefetchRequestComplete(ftn, RequestId(),
1788 completion_status);
1789}
1790
1791std::optional<mojo::PendingRemote<network::mojom::DevToolsObserver>>
1792PrefetchContainer::MakeSelfOwnedNetworkServiceDevToolsObserver() {
1793 if (IsDecoy()) {
1794 return std::nullopt;
1795 }
1796
Hiroshige Hayashizakif55280f62025-08-19 17:23:201797 auto* renderer_initiator_info = request().GetRendererInitiatorInfo();
1798 if (!renderer_initiator_info) {
1799 // Don't emit CDP events if the trigger is not speculation rules.
1800 return std::nullopt;
1801 }
1802
1803 auto* ftn =
1804 FrameTreeNode::From(renderer_initiator_info->GetRenderFrameHost());
kenoss1ce36df592024-12-03 01:04:351805 if (!ftn) {
Hiroshige Hayashizakif55280f62025-08-19 17:23:201806 // Don't emit CDP events if the initiator document isn't alive.
kenoss1ce36df592024-12-03 01:04:351807 return std::nullopt;
1808 }
1809
1810 return NetworkServiceDevToolsObserver::MakeSelfOwned(ftn);
1811}
1812
Hiroshige Hayashizaki03a59052025-08-12 14:44:231813std::string PrefetchContainer::GetMetricsSuffix() const {
Hiroshige Hayashizakif55280f62025-08-19 17:23:201814 std::optional<std::string> embedder_histogram_suffix;
1815 if (auto* browser_initiator_info = request().GetBrowserInitiatorInfo()) {
1816 embedder_histogram_suffix =
1817 browser_initiator_info->embedder_histogram_suffix();
1818 }
1819 return GetMetricsSuffixTriggerTypeAndEagerness(request().prefetch_type(),
1820 embedder_histogram_suffix);
Hiroshige Hayashizaki03a59052025-08-12 14:44:231821}
1822
Adithya Srinivasan4d3dad02024-10-17 18:06:591823void PrefetchContainer::MaybeRecordPrefetchStatusToUMA(
1824 PrefetchStatus prefetch_status) {
1825 if (prefetch_status_recorded_to_uma_) {
1826 return;
1827 }
1828
1829 base::UmaHistogramEnumeration("Preloading.Prefetch.PrefetchStatus",
1830 prefetch_status);
1831 prefetch_status_recorded_to_uma_ = true;
1832}
1833
Hiroshige Hayashizaki0830e182025-03-14 02:03:061834void PrefetchContainer::OnServiceWorkerStateDetermined(
1835 PrefetchServiceWorkerState service_worker_state) {
1836 switch (service_worker_state_) {
1837 case PrefetchServiceWorkerState::kDisallowed:
1838 CHECK_EQ(service_worker_state, PrefetchServiceWorkerState::kDisallowed);
1839 break;
1840 case PrefetchServiceWorkerState::kAllowed:
1841 CHECK_NE(service_worker_state, PrefetchServiceWorkerState::kAllowed);
1842 service_worker_state_ = service_worker_state;
1843 break;
1844 case PrefetchServiceWorkerState::kControlled:
1845 NOTREACHED();
1846 }
1847}
1848
Taiyo Mizuhashi98061e22025-07-12 05:26:541849void PrefetchContainer::RecordPrefetchDurationHistogram() {
kenosseab3c422025-04-03 12:50:191850 if (!time_added_to_prefetch_service_.has_value()) {
1851 return;
1852 }
1853
1854 if (!time_initial_eligibility_got_.has_value()) {
1855 return;
1856 }
1857
1858 base::UmaHistogramTimes(
1859 base::StrCat({
1860 "Prefetch.PrefetchContainer.AddedToInitialEligibility.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231861 GetMetricsSuffix(),
kenosseab3c422025-04-03 12:50:191862 }),
1863 time_initial_eligibility_got_.value() -
1864 time_added_to_prefetch_service_.value());
1865
1866 if (!time_prefetch_started_.has_value()) {
1867 return;
1868 }
1869
1870 base::UmaHistogramTimes(
1871 base::StrCat({
1872 "Prefetch.PrefetchContainer.AddedToPrefetchStarted.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231873 GetMetricsSuffix(),
kenosseab3c422025-04-03 12:50:191874 }),
1875 time_prefetch_started_.value() - time_added_to_prefetch_service_.value());
1876
Taiyo Mizuhashi98061e22025-07-12 05:26:541877 base::UmaHistogramTimes(
1878 base::StrCat({
1879 "Prefetch.PrefetchContainer.InitialEligibilityToPrefetchStarted.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231880 GetMetricsSuffix(),
Taiyo Mizuhashi98061e22025-07-12 05:26:541881 }),
1882 time_prefetch_started_.value() - time_initial_eligibility_got_.value());
1883
Taiyo Mizuhashi98ecb382025-06-03 15:24:551884 if (!time_url_request_started_.has_value()) {
1885 return;
1886 }
1887
1888 base::UmaHistogramTimes(base::StrCat({
1889 "Prefetch.PrefetchContainer."
1890 "AddedToURLRequestStarted.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231891 GetMetricsSuffix(),
Taiyo Mizuhashi98ecb382025-06-03 15:24:551892 }),
1893 time_url_request_started_.value() -
1894 time_added_to_prefetch_service_.value());
1895
Taiyo Mizuhashi98061e22025-07-12 05:26:541896 base::UmaHistogramTimes(
1897 base::StrCat({
1898 "Prefetch.PrefetchContainer.PrefetchStartedToURLRequestStarted.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231899 GetMetricsSuffix(),
Taiyo Mizuhashi98061e22025-07-12 05:26:541900 }),
1901 time_url_request_started_.value() - time_prefetch_started_.value());
1902
kenosseab3c422025-04-03 12:50:191903 if (!time_header_determined_successfully_.has_value()) {
1904 return;
1905 }
1906
Taiyo Mizuhashia12acc42025-04-25 23:40:361907 base::UmaHistogramTimes(base::StrCat({
1908 "Prefetch.PrefetchContainer."
1909 "AddedToHeaderDeterminedSuccessfully.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231910 GetMetricsSuffix(),
Taiyo Mizuhashia12acc42025-04-25 23:40:361911 }),
1912 time_header_determined_successfully_.value() -
1913 time_added_to_prefetch_service_.value());
kenosseab3c422025-04-03 12:50:191914
Taiyo Mizuhashi98061e22025-07-12 05:26:541915 base::UmaHistogramTimes(base::StrCat({
1916 "Prefetch.PrefetchContainer."
1917 "PrefetchStartedToHeaderDeterminedSuccessfully.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231918 GetMetricsSuffix(),
Taiyo Mizuhashi98061e22025-07-12 05:26:541919 }),
1920 time_header_determined_successfully_.value() -
1921 time_prefetch_started_.value());
1922
kenosseab3c422025-04-03 12:50:191923 if (!time_prefetch_completed_successfully_.has_value()) {
1924 return;
1925 }
1926
Taiyo Mizuhashia12acc42025-04-25 23:40:361927 base::UmaHistogramTimes(base::StrCat({
1928 "Prefetch.PrefetchContainer."
1929 "AddedToPrefetchCompletedSuccessfully.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231930 GetMetricsSuffix(),
Taiyo Mizuhashia12acc42025-04-25 23:40:361931 }),
1932 time_prefetch_completed_successfully_.value() -
1933 time_added_to_prefetch_service_.value());
Taiyo Mizuhashi98061e22025-07-12 05:26:541934
1935 base::UmaHistogramTimes(base::StrCat({
1936 "Prefetch.PrefetchContainer."
1937 "PrefetchStartedToPrefetchCompletedSuccessfully.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231938 GetMetricsSuffix(),
Taiyo Mizuhashi98061e22025-07-12 05:26:541939 }),
1940 time_prefetch_completed_successfully_.value() -
1941 time_prefetch_started_.value());
kenosseab3c422025-04-03 12:50:191942}
1943
Taiyo Mizuhashi43066071f2025-04-25 23:40:221944void PrefetchContainer::RecordPrefetchMatchingBlockedNavigationHistogram(
kenoss4bdf2d82025-05-27 01:58:451945 bool blocked_until_head,
1946 bool is_nav_prerender) {
Taiyo Mizuhashi43066071f2025-04-25 23:40:221947 base::UmaHistogramBoolean(
1948 base::StrCat(
1949 {"Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231950 GetMetricsSuffix()}),
Taiyo Mizuhashi43066071f2025-04-25 23:40:221951 blocked_until_head);
kenoss4bdf2d82025-05-27 01:58:451952 base::UmaHistogramBoolean(
1953 base::StrCat(
1954 {"Prefetch.PrefetchMatchingBlockedNavigation.PerMatchingCandidate.",
1955 is_nav_prerender ? "Prerender." : "NonPrerender.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231956 GetMetricsSuffix()}),
kenoss4bdf2d82025-05-27 01:58:451957 blocked_until_head);
Taiyo Mizuhashi43066071f2025-04-25 23:40:221958}
1959
1960void PrefetchContainer::RecordBlockUntilHeadDurationHistogram(
1961 const std::optional<base::TimeDelta>& blocked_duration,
kenoss4bdf2d82025-05-27 01:58:451962 bool served,
1963 bool is_nav_prerender) {
Taiyo Mizuhashi43066071f2025-04-25 23:40:221964 base::UmaHistogramTimes(
1965 base::StrCat({"Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231966 served ? "Served." : "NotServed.", GetMetricsSuffix()}),
Taiyo Mizuhashi43066071f2025-04-25 23:40:221967 blocked_duration.value_or(base::Seconds(0)));
kenoss4bdf2d82025-05-27 01:58:451968 base::UmaHistogramTimes(
1969 base::StrCat({"Prefetch.BlockUntilHeadDuration.PerMatchingCandidate.",
1970 is_nav_prerender ? "Prerender." : "NonPrerender.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231971 served ? "Served." : "NotServed.", GetMetricsSuffix()}),
kenoss4bdf2d82025-05-27 01:58:451972 blocked_duration.value_or(base::Seconds(0)));
Taiyo Mizuhashi43066071f2025-04-25 23:40:221973}
kenoss4bdf2d82025-05-27 01:58:451974
Taiyo Mizuhashi09f571f2025-08-04 16:05:541975void PrefetchContainer::RecordPrefetchPotentialCandidateServingResultHistogram(
1976 PrefetchPotentialCandidateServingResult matching_result) {
1977 base::UmaHistogramEnumeration(
1978 base::StrCat({"Prefetch.PrefetchPotentialCandidateServingResult."
1979 "PerMatchingCandidate.",
Hiroshige Hayashizaki03a59052025-08-12 14:44:231980 GetMetricsSuffix()}),
Taiyo Mizuhashi09f571f2025-08-04 16:05:541981 matching_result);
1982}
1983
Taiyo Mizuhashia009caf2025-08-05 07:34:221984void PrefetchContainer::RecordPrefetchContainerServedCountHistogram() {
1985 base::UmaHistogramCounts100(
Hiroshige Hayashizaki03a59052025-08-12 14:44:231986 base::StrCat(
1987 {"Prefetch.PrefetchContainer.ServedCount.", GetMetricsSuffix()}),
Taiyo Mizuhashia009caf2025-08-05 07:34:221988 served_count_);
1989}
1990
Max Curran646fb642022-03-16 00:44:091991} // namespace content