blob: 6aa9f103c8ed2efb81737a46c85a64712257cca9 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2022 The Chromium Authors
Max Curran6c2835ea2022-03-07 19:52:382// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Sreeja Kamishettyf66553a2022-07-14 17:41:275#include "content/browser/preloading/prefetch/prefetch_service.h"
Max Curran6c2835ea2022-03-07 19:52:386
Peter Kasting1557e5f2025-01-28 01:14:087#include <algorithm>
Max Curran18a6f2b2022-05-02 23:13:248#include <memory>
Arthur Sonzognic686e8f2024-01-11 08:36:379#include <optional>
Md Hasibul Hasana963a9342024-04-03 10:15:1410#include <string_view>
Max Curran646fb642022-03-16 00:44:0911#include <utility>
Andrew Rayskiyf65990362024-02-27 18:43:2412#include <vector>
Max Curran646fb642022-03-16 00:44:0913
Jeremy Roman84e6e102023-08-15 16:06:1414#include "base/auto_reset.h"
Max Curranc4445fc2022-06-02 18:43:4315#include "base/barrier_closure.h"
kenoss1b8ebc22025-04-04 00:11:3516#include "base/check_is_test.h"
Liviu Tinta1fe1a6d2023-09-20 19:44:0417#include "base/containers/fixed_flat_set.h"
Max Curran6c2835ea2022-03-07 19:52:3818#include "base/feature_list.h"
Max Curran18a6f2b2022-05-02 23:13:2419#include "base/location.h"
Max Curran646fb642022-03-16 00:44:0920#include "base/memory/weak_ptr.h"
Max Curran6b93426f2022-05-03 00:55:5221#include "base/metrics/histogram_functions.h"
22#include "base/metrics/histogram_macros.h"
Max Curran18a6f2b2022-05-02 23:13:2423#include "base/timer/timer.h"
Hiroshige Hayashizaki805be6862023-03-28 08:16:2224#include "content/browser/browser_context_impl.h"
kenoba0b85b2023-08-23 16:40:4225#include "content/browser/devtools/render_frame_devtools_agent_host.h"
Kevin McNee7ca2e852024-04-30 02:38:3126#include "content/browser/preloading/prefetch/no_vary_search_helper.h"
Wayne Jackson Jr.0118d2a2025-02-21 10:53:4627#include "content/browser/preloading/prefetch/prefetch_container.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2728#include "content/browser/preloading/prefetch/prefetch_document_manager.h"
29#include "content/browser/preloading/prefetch/prefetch_features.h"
Taiyo Mizuhashi94ce74f2025-01-28 04:20:1430#include "content/browser/preloading/prefetch/prefetch_handle_impl.h"
Liviu Tinta4eaa53c22023-08-02 21:01:0631#include "content/browser/preloading/prefetch/prefetch_match_resolver.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2732#include "content/browser/preloading/prefetch/prefetch_network_context.h"
Max Curran0146f852022-08-01 19:49:3333#include "content/browser/preloading/prefetch/prefetch_origin_prober.h"
Sreeja Kamishettyf66553a2022-07-14 17:41:2734#include "content/browser/preloading/prefetch/prefetch_params.h"
35#include "content/browser/preloading/prefetch/prefetch_proxy_configurator.h"
Taiyo Mizuhashi08c47d12025-03-05 23:46:0736#include "content/browser/preloading/prefetch/prefetch_response_reader.h"
kenoss1b8ebc22025-04-04 00:11:3537#include "content/browser/preloading/prefetch/prefetch_scheduler.h"
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:0838#include "content/browser/preloading/prefetch/prefetch_servable_state.h"
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:5739#include "content/browser/preloading/prefetch/prefetch_serving_handle.h"
William Liu77089052022-12-15 18:53:3540#include "content/browser/preloading/prefetch/prefetch_status.h"
Max Curran892ca5422022-12-12 20:55:3441#include "content/browser/preloading/prefetch/prefetch_streaming_url_loader.h"
Liviu Tintad608e012023-05-10 19:16:4142#include "content/browser/preloading/preloading_attempt_impl.h"
kenossb68c9e82024-10-10 10:11:1543#include "content/browser/preloading/prerender/prerender_features.h"
Peter Conn38164602025-08-06 14:41:1944#include "content/browser/preloading/proxy_lookup_client_impl.h"
David Sandersd467daa62025-06-24 23:01:3345#include "content/browser/renderer_host/render_frame_host_impl.h"
Max Curran146bf442022-03-28 23:22:1446#include "content/public/browser/browser_context.h"
Jeremy Romanbae6a422022-05-17 22:41:1747#include "content/public/browser/content_browser_client.h"
Max Curran3df677bf2022-09-12 19:47:0748#include "content/public/browser/frame_accept_header.h"
David Sandersd467daa62025-06-24 23:01:3349#include "content/public/browser/frame_tree_node_id.h"
Max Curranead64a62022-06-22 01:10:5250#include "content/public/browser/prefetch_service_delegate.h"
Simon Pelchat9ed3e992023-02-17 01:16:1651#include "content/public/browser/preloading.h"
Max Curran18a6f2b2022-05-02 23:13:2452#include "content/public/browser/render_frame_host.h"
53#include "content/public/browser/render_process_host.h"
Max Curran146bf442022-03-28 23:22:1454#include "content/public/browser/service_worker_context.h"
Patrick Monettea45111342024-09-24 00:33:2955#include "content/public/browser/spare_render_process_host_manager.h"
Max Curran146bf442022-03-28 23:22:1456#include "content/public/browser/storage_partition.h"
Wayne Jackson Jr.0118d2a2025-02-21 10:53:4657#include "content/public/browser/url_loader_request_interceptor.h"
Max Curran18a6f2b2022-05-02 23:13:2458#include "content/public/browser/visibility.h"
59#include "content/public/browser/web_contents.h"
Jeremy Romanbae6a422022-05-17 22:41:1760#include "content/public/common/content_client.h"
Max Curran18a6f2b2022-05-02 23:13:2461#include "content/public/common/content_constants.h"
Max Curran18a6f2b2022-05-02 23:13:2462#include "net/base/load_flags.h"
Max Curran146bf442022-03-28 23:22:1463#include "net/base/url_util.h"
64#include "net/cookies/canonical_cookie.h"
Max Curranc4445fc2022-06-02 18:43:4365#include "net/cookies/cookie_partition_key_collection.h"
Max Curran18a6f2b2022-05-02 23:13:2466#include "net/cookies/site_for_cookies.h"
Wayne Jackson Jr.0118d2a2025-02-21 10:53:4667#include "net/http/http_no_vary_search_data.h"
Jeremy Roman45c89d012023-08-30 21:22:0268#include "net/http/http_request_headers.h"
Max Curran6b93426f2022-05-03 00:55:5269#include "net/http/http_status_code.h"
70#include "net/http/http_util.h"
Max Curran18a6f2b2022-05-02 23:13:2471#include "net/traffic_annotation/network_traffic_annotation.h"
Jeremy Roman45c89d012023-08-30 21:22:0272#include "services/network/public/cpp/devtools_observer_util.h"
Max Curran146bf442022-03-28 23:22:1473#include "services/network/public/cpp/is_potentially_trustworthy.h"
Max Curran18a6f2b2022-05-02 23:13:2474#include "services/network/public/cpp/resource_request.h"
Max Curran146bf442022-03-28 23:22:1475#include "services/network/public/mojom/cookie_manager.mojom.h"
Jeremy Roman45c89d012023-08-30 21:22:0276#include "services/network/public/mojom/devtools_observer.mojom.h"
William Liu2c825472022-10-31 12:01:4477#include "services/network/public/mojom/url_response_head.mojom-shared.h"
Max Curran18a6f2b2022-05-02 23:13:2478#include "services/network/public/mojom/url_response_head.mojom.h"
Devlin Cronin7f318c12023-06-09 00:57:0179#include "third_party/blink/public/common/loader/referrer_utils.h"
Max Curran6c2835ea2022-03-07 19:52:3880#include "url/gurl.h"
Max Curran18a6f2b2022-05-02 23:13:2481#include "url/origin.h"
Max Curran146bf442022-03-28 23:22:1482#include "url/url_constants.h"
Max Curran6c2835ea2022-03-07 19:52:3883
84namespace content {
85
Max Curran146bf442022-03-28 23:22:1486namespace {
87
88static ServiceWorkerContext* g_service_worker_context_for_testing = nullptr;
89
Md Hasibul Hasana963a9342024-04-03 10:15:1490bool (*g_host_non_unique_filter)(std::string_view) = nullptr;
Max Curran146bf442022-03-28 23:22:1491
Hiroshige Hayashizakie51e2f262025-03-04 23:14:3892static network::SharedURLLoaderFactory* g_url_loader_factory_for_testing =
Max Curran18a6f2b2022-05-02 23:13:2493 nullptr;
94
Max Currancc1ab0c2022-09-12 22:03:1195static network::mojom::NetworkContext*
96 g_network_context_for_proxy_lookup_for_testing = nullptr;
97
kenossb68c9e82024-10-10 10:11:1598PrefetchService::DelayEligibilityCheckForTesting&
99GetDelayEligibilityCheckForTesting() {
100 static base::NoDestructor<PrefetchService::DelayEligibilityCheckForTesting>
101 prefetch_delay_eligibility_check_for_testing;
102 return *prefetch_delay_eligibility_check_for_testing;
103}
104
kenoss4858a802024-10-11 06:50:56105std::optional<PreloadingEligibility>& GetForceIneligibilityForTesting() {
106 static std::optional<PreloadingEligibility>
107 prefetch_force_ineligibility_for_testing;
108 return prefetch_force_ineligibility_for_testing;
109}
110
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:21111bool ShouldConsiderDecoyRequestForStatus(PreloadingEligibility eligibility) {
112 switch (eligibility) {
113 case PreloadingEligibility::kUserHasCookies:
114 case PreloadingEligibility::kUserHasServiceWorker:
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31115 case PreloadingEligibility::kUserHasServiceWorkerNoFetchHandler:
116 case PreloadingEligibility::kRedirectFromServiceWorker:
117 case PreloadingEligibility::kRedirectToServiceWorker:
Max Curran18a6f2b2022-05-02 23:13:24118 // If the prefetch is not eligible because of cookie or a service worker,
119 // then maybe send a decoy.
120 return true;
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:21121 case PreloadingEligibility::kBatterySaverEnabled:
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:21122 case PreloadingEligibility::kDataSaverEnabled:
123 case PreloadingEligibility::kExistingProxy:
124 case PreloadingEligibility::kHostIsNonUnique:
125 case PreloadingEligibility::kNonDefaultStoragePartition:
126 case PreloadingEligibility::kPrefetchProxyNotAvailable:
127 case PreloadingEligibility::kPreloadingDisabled:
128 case PreloadingEligibility::kRetryAfter:
129 case PreloadingEligibility::kSameSiteCrossOriginPrefetchRequiredProxy:
130 case PreloadingEligibility::kSchemeIsNotHttps:
Max Curran18a6f2b2022-05-02 23:13:24131 // These statuses don't relate to any user state, so don't send a decoy
132 // request.
133 return false;
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:21134 case PreloadingEligibility::kEligible:
135 default:
136 // Other ineligible cases are not used in `PrefetchService`.
Peter Boströmfc7ddc182024-10-31 19:37:21137 NOTREACHED();
Max Curran18a6f2b2022-05-02 23:13:24138 }
139}
140
141bool ShouldStartSpareRenderer() {
Max Curran94511aa2022-08-10 20:28:26142 if (!PrefetchStartsSpareRenderer()) {
143 return false;
144 }
145
Max Curran18a6f2b2022-05-02 23:13:24146 for (RenderProcessHost::iterator iter(RenderProcessHost::AllHostsIterator());
147 !iter.IsAtEnd(); iter.Advance()) {
148 if (iter.GetCurrentValue()->IsUnused()) {
149 // There is already a spare renderer.
150 return false;
151 }
152 }
153 return true;
154}
155
Max Curran892ca5422022-12-12 20:55:34156void RecordPrefetchProxyPrefetchMainframeTotalTime(
Max Curran6b93426f2022-05-03 00:55:52157 network::mojom::URLResponseHead* head) {
158 DCHECK(head);
159
160 base::Time start = head->request_time;
161 base::Time end = head->response_time;
162
Max Curran892ca5422022-12-12 20:55:34163 if (start.is_null() || end.is_null()) {
164 return;
165 }
166
167 UMA_HISTOGRAM_CUSTOM_TIMES("PrefetchProxy.Prefetch.Mainframe.TotalTime",
168 end - start, base::Milliseconds(10),
169 base::Seconds(30), 100);
Max Curran6b93426f2022-05-03 00:55:52170}
171
Max Curran892ca5422022-12-12 20:55:34172void RecordPrefetchProxyPrefetchMainframeConnectTime(
Max Curran6b93426f2022-05-03 00:55:52173 network::mojom::URLResponseHead* head) {
174 DCHECK(head);
175
176 base::TimeTicks start = head->load_timing.connect_timing.connect_start;
177 base::TimeTicks end = head->load_timing.connect_timing.connect_end;
178
Max Curran892ca5422022-12-12 20:55:34179 if (start.is_null() || end.is_null()) {
180 return;
181 }
182
183 UMA_HISTOGRAM_TIMES("PrefetchProxy.Prefetch.Mainframe.ConnectTime",
184 end - start);
185}
186
187void RecordPrefetchProxyPrefetchMainframeRespCode(int response_code) {
188 base::UmaHistogramSparse("PrefetchProxy.Prefetch.Mainframe.RespCode",
189 response_code);
190}
191
Max Curranc4445fc2022-06-02 18:43:43192void RecordPrefetchProxyPrefetchMainframeCookiesToCopy(
193 size_t cookie_list_size) {
194 UMA_HISTOGRAM_COUNTS_100("PrefetchProxy.Prefetch.Mainframe.CookiesToCopy",
195 cookie_list_size);
196}
197
198void CookieSetHelper(base::RepeatingClosure closure,
199 net::CookieAccessResult access_result) {
200 closure.Run();
201}
202
William Liu77089052022-12-15 18:53:35203// Returns true if the prefetch is heldback, and set the holdback status
204// correspondingly.
205bool CheckAndSetPrefetchHoldbackStatus(
206 base::WeakPtr<PrefetchContainer> prefetch_container) {
207 if (!prefetch_container->HasPreloadingAttempt()) {
208 return false;
209 }
kenoba0b85b2023-08-23 16:40:42210
Taiyo Mizuhashi26f86c6b32024-10-02 03:58:46211 bool devtools_client_exist = [&] {
212 // Currently DevTools only supports when the prefetch is initiated by
213 // renderer.
214 if (!prefetch_container->IsRendererInitiated()) {
215 return false;
216 }
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37217 RenderFrameHostImpl* initiator_rfh = RenderFrameHostImpl::FromID(
218 prefetch_container->GetReferringRenderFrameHostId());
Taiyo Mizuhashi26f86c6b32024-10-02 03:58:46219 return initiator_rfh &&
220 RenderFrameDevToolsAgentHost::GetFor(initiator_rfh) != nullptr;
221 }();
222
223 // Normally PreloadingAttemptImpl::ShouldHoldback() eventually computes its
kenoss9b82a0d2025-03-25 06:25:51224 // `holdback_status_`, but we forcely set the status in some special cases
Taiyo Mizuhashi26f86c6b32024-10-02 03:58:46225 // below, by calling PreloadingAttemptImpl::SetHoldbackStatus().
226 // As its comment describes, this is expected to be called only once.
kenoss9b82a0d2025-03-25 06:25:51227 //
228 // Note that, alternatively, determining holdback status can be done in
229 // triggers, e.g. in `PreloadingAttemptImpl::ctor()`. For more details, see
230 // https://crbug.com/406123867
Taiyo Mizuhashi26f86c6b32024-10-02 03:58:46231
232 if (devtools_client_exist) {
233 // 1. When developers debug Speculation Rules Prefetch using DevTools,
234 // always set status to kAllowed for developer experience.
235 prefetch_container->preloading_attempt()->SetHoldbackStatus(
236 PreloadingHoldbackStatus::kAllowed);
kenoss9b82a0d2025-03-25 06:25:51237 } else if (prefetch_container->IsLikelyAheadOfPrerender()) {
238 // 2. If PrefetchContainer is likely ahead of prerender, always set status
239 // to kAllowed as it is likely used for prerender.
240 //
241 // Note that we don't use `PrefetchContainer::overridden_holdback_status_`
242 // for this purpose because it can't handle a prefetch that was not ahead of
243 // prerender but another ahead of prerender one is migrated into it. We need
244 // to update migration if we'd like to do it.
245 prefetch_container->preloading_attempt()->SetHoldbackStatus(
246 PreloadingHoldbackStatus::kAllowed);
Taiyo Mizuhashi26f86c6b32024-10-02 03:58:46247 } else if (prefetch_container->HasOverriddenHoldbackStatus()) {
kenoss9b82a0d2025-03-25 06:25:51248 // 3. If PrefetchContainer has custom overridden status, set that value.
Taiyo Mizuhashi26f86c6b32024-10-02 03:58:46249 prefetch_container->preloading_attempt()->SetHoldbackStatus(
250 prefetch_container->GetOverriddenHoldbackStatus());
Simon Pelchat9ed3e992023-02-17 01:16:16251 }
kenoba0b85b2023-08-23 16:40:42252
Simon Pelchat9ed3e992023-02-17 01:16:16253 if (prefetch_container->preloading_attempt()->ShouldHoldback()) {
Hiroshige Hayashizaki4ce0f4292023-11-07 19:24:58254 prefetch_container->SetLoadState(
255 PrefetchContainer::LoadState::kFailedHeldback);
William Liu77089052022-12-15 18:53:35256 prefetch_container->SetPrefetchStatus(PrefetchStatus::kPrefetchHeldback);
257 return true;
258 }
Simon Pelchat9ed3e992023-02-17 01:16:16259 return false;
William Liu77089052022-12-15 18:53:35260}
261
Avi Drissmandcc8e682024-09-04 14:14:48262BrowserContext* BrowserContextFromFrameTreeNodeId(
263 FrameTreeNodeId frame_tree_node_id) {
Hiroshige Hayashizaki805be6862023-03-28 08:16:22264 WebContents* web_content =
265 WebContents::FromFrameTreeNodeId(frame_tree_node_id);
266 if (!web_content) {
267 return nullptr;
268 }
269 return web_content->GetBrowserContext();
270}
271
Max Curran7d2578b2023-04-12 19:19:28272void RecordRedirectResult(PrefetchRedirectResult result) {
273 UMA_HISTOGRAM_ENUMERATION("PrefetchProxy.Redirect.Result", result);
274}
275
276void RecordRedirectNetworkContextTransition(
Max Curran48700962023-05-15 18:35:52277 bool previous_requires_isolated_network_context,
Max Curran7d2578b2023-04-12 19:19:28278 bool redirect_requires_isolated_network_context) {
279 PrefetchRedirectNetworkContextTransition transition;
Max Curran48700962023-05-15 18:35:52280 if (!previous_requires_isolated_network_context &&
Max Curran7d2578b2023-04-12 19:19:28281 !redirect_requires_isolated_network_context) {
282 transition = PrefetchRedirectNetworkContextTransition::kDefaultToDefault;
283 }
Max Curran48700962023-05-15 18:35:52284 if (!previous_requires_isolated_network_context &&
Max Curran7d2578b2023-04-12 19:19:28285 redirect_requires_isolated_network_context) {
286 transition = PrefetchRedirectNetworkContextTransition::kDefaultToIsolated;
287 }
Max Curran48700962023-05-15 18:35:52288 if (previous_requires_isolated_network_context &&
Max Curran7d2578b2023-04-12 19:19:28289 !redirect_requires_isolated_network_context) {
290 transition = PrefetchRedirectNetworkContextTransition::kIsolatedToDefault;
291 }
Max Curran48700962023-05-15 18:35:52292 if (previous_requires_isolated_network_context &&
Max Curran7d2578b2023-04-12 19:19:28293 redirect_requires_isolated_network_context) {
294 transition = PrefetchRedirectNetworkContextTransition::kIsolatedToIsolated;
295 }
296
297 UMA_HISTOGRAM_ENUMERATION(
298 "PrefetchProxy.Redirect.NetworkContextStateTransition", transition);
299}
Hiroshige Hayashizaki6a99ee92023-05-30 19:54:37300
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:57301void OnIsolatedCookieCopyComplete(PrefetchServingHandle serving_handle) {
302 if (serving_handle) {
303 serving_handle.OnIsolatedCookieCopyComplete();
Hiroshige Hayashizaki6a99ee92023-05-30 19:54:37304 }
305}
306
Devlin Cronin7f318c12023-06-09 00:57:01307bool IsReferrerPolicySufficientlyStrict(
308 const network::mojom::ReferrerPolicy& referrer_policy) {
309 // https://github.com/WICG/nav-speculation/blob/main/prefetch.bs#L606
310 // "", "`strict-origin-when-cross-origin`", "`strict-origin`",
311 // "`same-origin`", "`no-referrer`".
312 switch (referrer_policy) {
313 case network::mojom::ReferrerPolicy::kDefault:
314 case network::mojom::ReferrerPolicy::kStrictOriginWhenCrossOrigin:
315 case network::mojom::ReferrerPolicy::kSameOrigin:
316 case network::mojom::ReferrerPolicy::kStrictOrigin:
317 return true;
318 case network::mojom::ReferrerPolicy::kAlways:
319 case network::mojom::ReferrerPolicy::kNoReferrerWhenDowngrade:
320 case network::mojom::ReferrerPolicy::kNever:
321 case network::mojom::ReferrerPolicy::kOrigin:
322 case network::mojom::ReferrerPolicy::kOriginWhenCrossOrigin:
323 return false;
324 }
325}
326
Max Curran146bf442022-03-28 23:22:14327} // namespace
328
Max Curran6c2835ea2022-03-07 19:52:38329// static
Hiroshige Hayashizaki805be6862023-03-28 08:16:22330PrefetchService* PrefetchService::GetFromFrameTreeNodeId(
Avi Drissmandcc8e682024-09-04 14:14:48331 FrameTreeNodeId frame_tree_node_id) {
Hiroshige Hayashizaki805be6862023-03-28 08:16:22332 BrowserContext* browser_context =
333 BrowserContextFromFrameTreeNodeId(frame_tree_node_id);
334 if (!browser_context) {
335 return nullptr;
336 }
337 return BrowserContextImpl::From(browser_context)->GetPrefetchService();
338}
339
340void PrefetchService::SetFromFrameTreeNodeIdForTesting(
Avi Drissmandcc8e682024-09-04 14:14:48341 FrameTreeNodeId frame_tree_node_id,
Hiroshige Hayashizaki805be6862023-03-28 08:16:22342 std::unique_ptr<PrefetchService> prefetch_service) {
343 BrowserContext* browser_context =
344 BrowserContextFromFrameTreeNodeId(frame_tree_node_id);
345 CHECK(browser_context);
346 return BrowserContextImpl::From(browser_context)
347 ->SetPrefetchServiceForTesting(std::move(prefetch_service)); // IN-TEST
348}
349
Max Curran146bf442022-03-28 23:22:14350PrefetchService::PrefetchService(BrowserContext* browser_context)
Max Curran18a6f2b2022-05-02 23:13:24351 : browser_context_(browser_context),
Max Curranead64a62022-06-22 01:10:52352 delegate_(GetContentClient()->browser()->CreatePrefetchServiceDelegate(
353 browser_context_)),
Max Curran18a6f2b2022-05-02 23:13:24354 prefetch_proxy_configurator_(
Max Curranead64a62022-06-22 01:10:52355 PrefetchProxyConfigurator::MaybeCreatePrefetchProxyConfigurator(
356 PrefetchProxyHost(delegate_
357 ? delegate_->GetDefaultPrefetchProxyHost()
358 : GURL("")),
Max Curran0146f852022-08-01 19:49:33359 delegate_ ? delegate_->GetAPIKey() : "")),
360 origin_prober_(std::make_unique<PrefetchOriginProber>(
361 browser_context_,
362 PrefetchDNSCanaryCheckURL(
363 delegate_ ? delegate_->GetDefaultDNSCanaryCheckURL() : GURL("")),
364 PrefetchTLSCanaryCheckURL(
365 delegate_ ? delegate_->GetDefaultTLSCanaryCheckURL()
kenoss1b8ebc22025-04-04 00:11:35366 : GURL("")))),
367 scheduler_(UsePrefetchScheduler()
368 ? std::make_unique<PrefetchScheduler>(this)
369 : nullptr) {}
Max Curran6c2835ea2022-03-07 19:52:38370
Hiroshige Hayashizaki70db51b2025-07-14 23:09:49371PrefetchService::~PrefetchService() { // NOLINT(modernize-use-equals-default)
372 // This is to avoid notifying `PrefetchService::OnWillBeDestroyed()` in the
373 // middle of `PrefetchService` destruction.
374 // This is for the future when we add some logic to `OnWillBeDestroyed()`.
375 // Currently `OnWillBeDestroyed()` is empty and thus this is no-op and we have
376 // to skip `modernize-use-equals-default` check.
377 for (const auto& iter : owned_prefetches()) {
378 if (iter.second) {
379 iter.second->RemoveObserver(this);
380 }
381 }
382}
Max Curran6c2835ea2022-03-07 19:52:38383
Jeremy Romane561b412024-02-15 18:31:34384void PrefetchService::SetPrefetchServiceDelegateForTesting(
385 std::unique_ptr<PrefetchServiceDelegate> delegate) {
386 DCHECK(!delegate_);
387 delegate_ = std::move(delegate);
388}
389
Hiroshige Hayashizaki805be6862023-03-28 08:16:22390PrefetchOriginProber* PrefetchService::GetPrefetchOriginProber() const {
391 return origin_prober_.get();
392}
393
Hiroshige Hayashizaki31484212024-01-30 13:35:21394void PrefetchService::AddPrefetchContainerWithoutStartingPrefetch(
Hiroshige Hayashizaki7a34d0182023-11-07 20:54:34395 std::unique_ptr<PrefetchContainer> owned_prefetch_container) {
Hiroshige Hayashizaki70db51b2025-07-14 23:09:49396 RecordExistingPrefetchWithMatchingURL(*owned_prefetch_container);
Hiroshige Hayashizakiedf8ac32023-10-21 03:30:12397
kenossb68c9e82024-10-10 10:11:15398 enum class Action {
399 kTakeOldWithMigration,
400 kReplaceOldWithNew,
401 kTakeNew,
402 };
403
404 // The comment below might be old. Currently,
405 // `PrefetchDocumentManager::PrefetchUrl()` reject colficting prefetches
406 // execpt for ahead of prerender case.
407 //
408 // TODO(crbug.com/371179869): Integrate these two processes.
409 //
Hiroshige Hayashizakif59d9022023-11-08 00:08:09410 // A newly submitted prefetch could already be in |owned_prefetches_| if and
Hiroshige Hayashizakiedf8ac32023-10-21 03:30:12411 // only if:
412 // 1) There was a same origin navigaition that used the same renderer.
413 // 2) Both pages requested a prefetch for the same URL.
414 // 3) The prefetch from the first page had at least started its network
415 // request (which would mean that it is in |owned_prefetches_| and owned
416 // by the prefetch service).
417 // If this happens, then we just delete the old prefetch and add the new
Hiroshige Hayashizakif59d9022023-11-08 00:08:09418 // prefetch to |owned_prefetches_|.
kenossb68c9e82024-10-10 10:11:15419 //
420 // Note that we might replace this by preserving existing prefetch and
421 // additional works, e.g. adding some properties to the old one and prolonging
422 // cacheable duration, to prevent additional fetch. See also
423 // https://chromium-review.googlesource.com/c/chromium/src/+/3880874/comment/5ecccbf7_8fbcba96/
424 //
425 // TODO(crbug.com/372186548): Revisit the merging process and comments here
426 // and below.
Hiroshige Hayashizaki70db51b2025-07-14 23:09:49427 auto prefetch_iter = owned_prefetches().find(owned_prefetch_container->key());
kenossb68c9e82024-10-10 10:11:15428 Action action = [&]() {
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:56429 if (prefetch_iter == owned_prefetches().end()) {
kenossb68c9e82024-10-10 10:11:15430 return Action::kTakeNew;
431 }
432 PrefetchContainer& prefetch_container_old = *prefetch_iter->second;
Hiroshige Hayashizaki7a34d0182023-11-07 20:54:34433
kenossc6492942025-04-10 19:36:32434 if (!features::UsePrefetchPrerenderIntegration()) {
kenossb68c9e82024-10-10 10:11:15435 return Action::kReplaceOldWithNew;
436 }
437
438 switch (
439 prefetch_container_old.GetServableState(PrefetchCacheableDuration())) {
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:08440 case PrefetchServableState::kNotServable:
kenossb68c9e82024-10-10 10:11:15441 return Action::kReplaceOldWithNew;
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:08442 case PrefetchServableState::kShouldBlockUntilEligibilityGot:
443 case PrefetchServableState::kShouldBlockUntilHeadReceived:
444 case PrefetchServableState::kServable:
kenossb68c9e82024-10-10 10:11:15445 // nop
446 break;
447 }
448
449 // Take preload pipeline info of prefetch ahead of prerender.
450 //
451 // Consider the following screnario (especially, in the same SpecRules and
452 // upgrading ):
453 //
454 // - A document adds SpecRules of prefetch A for URL X.
455 // - A document adds SpecRules of prerender B' for URL X.
456 //
457 // With `kPrerender2FallbackPrefetchSpecRules`, B' triggers prefetch ahead
458 // of prerender B for URL X. Sites use SpecRules A+B' with expectation
459 // "prefetch X then prerender X", but the order of
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:00460 // `PrefetchService::AddPrefetchContainer*()` for A and B is unstable in
kenossb68c9e82024-10-10 10:11:15461 // general.
462 //
463 // `PrerenderHost` of B' needs to know eligibility and status of B. We use
464 // `PreloadPipelineInfo` for this purpose.
465 //
466 // - If A is followed by B, take A and migrate B into A. A inherits
467 // `PreloadPipelineInfo` of B.
468 // - If B is followed by A, just reject A (by
469 // `PrefetchDocumentManager::PrefetchUrl()`).
470 //
471 // See also tests `PrerendererImplBrowserTestPrefetchAhead.*`.
472
473 return Action::kTakeOldWithMigration;
474 }();
475
476 switch (action) {
477 case Action::kTakeOldWithMigration:
478 prefetch_iter->second->MigrateNewlyAdded(
479 std::move(owned_prefetch_container));
kenoss1b8ebc22025-04-04 00:11:35480 if (UsePrefetchScheduler()) {
481 scheduler_->NotifyAttributeMightChangedAndProgressAsync(
kenosse8d8bbb52025-05-13 12:35:04482 *prefetch_iter->second, /*should_progress=*/false);
kenoss1b8ebc22025-04-04 00:11:35483 }
kenossb68c9e82024-10-10 10:11:15484 break;
485 case Action::kReplaceOldWithNew:
kenosse8d8bbb52025-05-13 12:35:04486 ResetPrefetchContainer(prefetch_iter->second->GetWeakPtr(),
487 /*should_progress=*/false);
Hiroshige Hayashizaki70db51b2025-07-14 23:09:49488 AddPrefetchContainerToOwnedPrefetches(
489 std::move(owned_prefetch_container));
kenossb68c9e82024-10-10 10:11:15490 break;
491 case Action::kTakeNew:
Hiroshige Hayashizaki70db51b2025-07-14 23:09:49492 AddPrefetchContainerToOwnedPrefetches(
493 std::move(owned_prefetch_container));
kenossb68c9e82024-10-10 10:11:15494 break;
495 }
Hiroshige Hayashizaki31484212024-01-30 13:35:21496}
Hiroshige Hayashizakiedf8ac32023-10-21 03:30:12497
Hiroshige Hayashizaki70db51b2025-07-14 23:09:49498void PrefetchService::AddPrefetchContainerToOwnedPrefetches(
499 std::unique_ptr<PrefetchContainer> owned_prefetch_container) {
500 const base::WeakPtr<PrefetchContainer> prefetch_container =
501 owned_prefetch_container->GetWeakPtr();
502
503 // There should be no existing entry for `prefetch_container_key`.
504 CHECK(owned_prefetches_
505 .emplace(prefetch_container->key(),
506 std::move(owned_prefetch_container))
507 .second);
508
509 prefetch_container->OnAddedToPrefetchService();
510
511 prefetch_container->AddObserver(this);
512}
513
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46514bool PrefetchService::IsPrefetchDuplicate(
515 GURL& url,
516 std::optional<net::HttpNoVarySearchData> no_vary_search_hint) {
517 TRACE_EVENT0("loading", "PrefetchService::IsPrefetchDuplicate");
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:56518 for (const auto& [key, prefetch_container] : owned_prefetches()) {
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46519 if (IsPrefetchStale(prefetch_container->GetWeakPtr())) {
520 continue;
521 }
522
523 // We will only compare the URLs if the no-vary-search hints match for
524 // determinism. This is because comparing URLs with different no-vary-search
525 // hints will change the outcome of the comparison based on the order the
526 // requests happened in.
527 //
528 // This approach optimizes for determinism over minimizing wasted
529 // or redundant prefetches.
530 bool nvs_hints_match =
531 no_vary_search_hint == prefetch_container->GetNoVarySearchHint();
532 if (!nvs_hints_match) {
533 continue;
534 }
535
536 bool urls_equal;
537 if (no_vary_search_hint) {
538 urls_equal = no_vary_search_hint->AreEquivalent(url, key.url());
539 } else {
540 // If there is no no-vary-search hint, just compare the URLs.
541 urls_equal = url == key.url();
542 }
543
544 if (!urls_equal) {
545 continue;
546 }
547 return true;
548 }
549 return false;
550}
551
Hiroshige Hayashizakia129d91c2025-03-05 22:28:12552bool PrefetchService::IsPrefetchAttemptFailedOrDiscardedInternal(
553 base::PassKey<PrefetchDocumentManager>,
554 PrefetchContainer::Key key) const {
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:56555 auto it = owned_prefetches().find(key);
556 if (it == owned_prefetches().end() || !it->second) {
Hiroshige Hayashizakia129d91c2025-03-05 22:28:12557 return true;
558 }
559
560 const std::unique_ptr<PrefetchContainer>& container = it->second;
561 if (!container->HasPrefetchStatus()) {
562 return false; // the container is not processed yet
563 }
564
565 switch (container->GetPrefetchStatus()) {
566 case PrefetchStatus::kPrefetchSuccessful:
567 case PrefetchStatus::kPrefetchResponseUsed:
568 return false;
569 case PrefetchStatus::kPrefetchIneligibleUserHasCookies:
570 case PrefetchStatus::kPrefetchIneligibleUserHasServiceWorker:
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31571 case PrefetchStatus::kPrefetchIneligibleUserHasServiceWorkerNoFetchHandler:
572 case PrefetchStatus::kPrefetchIneligibleRedirectFromServiceWorker:
573 case PrefetchStatus::kPrefetchIneligibleRedirectToServiceWorker:
Hiroshige Hayashizakia129d91c2025-03-05 22:28:12574 case PrefetchStatus::kPrefetchIneligibleSchemeIsNotHttps:
575 case PrefetchStatus::kPrefetchIneligibleNonDefaultStoragePartition:
576 case PrefetchStatus::kPrefetchIneligibleRetryAfter:
577 case PrefetchStatus::kPrefetchIneligiblePrefetchProxyNotAvailable:
578 case PrefetchStatus::kPrefetchIneligibleHostIsNonUnique:
579 case PrefetchStatus::kPrefetchIneligibleDataSaverEnabled:
580 case PrefetchStatus::kPrefetchIneligibleBatterySaverEnabled:
581 case PrefetchStatus::kPrefetchIneligiblePreloadingDisabled:
582 case PrefetchStatus::kPrefetchIneligibleExistingProxy:
583 case PrefetchStatus::kPrefetchIsStale:
584 case PrefetchStatus::kPrefetchNotUsedProbeFailed:
585 case PrefetchStatus::kPrefetchNotStarted:
586 case PrefetchStatus::kPrefetchNotFinishedInTime:
587 case PrefetchStatus::kPrefetchFailedNetError:
588 case PrefetchStatus::kPrefetchFailedNon2XX:
589 case PrefetchStatus::kPrefetchFailedMIMENotSupported:
590 case PrefetchStatus::kPrefetchIsPrivacyDecoy:
591 case PrefetchStatus::kPrefetchNotUsedCookiesChanged:
592 case PrefetchStatus::kPrefetchHeldback:
593 case PrefetchStatus::kPrefetchFailedInvalidRedirect:
594 case PrefetchStatus::kPrefetchFailedIneligibleRedirect:
595 case PrefetchStatus::
596 kPrefetchIneligibleSameSiteCrossOriginPrefetchRequiredProxy:
597 case PrefetchStatus::kPrefetchEvictedAfterCandidateRemoved:
598 case PrefetchStatus::kPrefetchEvictedForNewerPrefetch:
Steven Weie829c85f72025-03-17 18:57:59599 case PrefetchStatus::kPrefetchEvictedAfterBrowsingDataRemoved:
Hiroshige Hayashizakia129d91c2025-03-05 22:28:12600 return true;
601 }
602}
603
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46604bool PrefetchService::IsPrefetchStale(
605 base::WeakPtr<PrefetchContainer> prefetch_container) {
606 TRACE_EVENT0("loading", "PrefetchService::IsPrefetchStale");
607 if (!prefetch_container) {
608 return true;
609 }
610
611 // `PrefetchContainer::LoadState` check.
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:32612 switch (prefetch_container->GetLoadState()) {
613 case PrefetchContainer::LoadState::kFailedIneligible:
614 case PrefetchContainer::LoadState::kFailedHeldback:
615 return true;
616 case PrefetchContainer::LoadState::kNotStarted:
617 case PrefetchContainer::LoadState::kEligible:
618 case PrefetchContainer::LoadState::kStarted:
619 case PrefetchContainer::LoadState::kDeterminedHead:
620 case PrefetchContainer::LoadState::kCompletedOrFailed:
621 break;
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46622 }
623
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:08624 // `PrefetchServableState` check.
625 PrefetchServableState servable_state =
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46626 prefetch_container->GetServableState(PrefetchCacheableDuration());
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:08627 if (servable_state == PrefetchServableState::kNotServable) {
Wayne Jackson Jr.0118d2a2025-02-21 10:53:46628 return true;
629 }
630 return false;
631}
632
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49633// Parameter class used during eligibility check and `OnGotEligibility*` methods
634// (`callback`).
635struct PrefetchService::CheckEligibilityParams final {
636 void Finish(PreloadingEligibility eligibility) && {
637 // `callback_local` is needed to avoid use-after-move.
638 auto callback_local = std::move(callback);
639 std::move(callback_local).Run(std::move(*this), eligibility);
640 }
641
642 // Returns if proxy is required for the next request.
643 bool IsProxyRequired() const {
644 CHECK(prefetch_container);
645 return prefetch_container->IsProxyRequiredForURL(url) &&
646 !ShouldPrefetchBypassProxyForTestHost(url.host());
647 }
648
649 base::WeakPtr<PrefetchContainer> prefetch_container;
650
651 // The URL of the next request.
652 GURL url;
653
654 // Whether this is eligibility check for a redirect, or for an initial
655 // request.
656 bool is_redirect;
657
658 // TODO(crbug.com/432783906): Add a `CHECK()` to ensure `callback` is always
659 // called. However, there are some cases where `callback` is not called (e.g.
660 // when `PrefetchService` is destroyed during eligibility check, which a valid
661 // exception, as well as other suspicious cases).
662 base::OnceCallback<void(CheckEligibilityParams, PreloadingEligibility)>
663 callback;
664};
665
Taiyo Mizuhashi94ce74f2025-01-28 04:20:14666std::unique_ptr<PrefetchHandle> PrefetchService::AddPrefetchContainerWithHandle(
667 std::unique_ptr<PrefetchContainer> owned_prefetch_container) {
668 base::WeakPtr<PrefetchContainer> prefetch_container =
669 owned_prefetch_container->GetWeakPtr();
Hiroshige Hayashizaki31484212024-01-30 13:35:21670 AddPrefetchContainerWithoutStartingPrefetch(
671 std::move(owned_prefetch_container));
kenossb68c9e82024-10-10 10:11:15672
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:00673 if (prefetch_container) {
674 PrefetchUrl(prefetch_container);
kenossb68c9e82024-10-10 10:11:15675 }
Hiroshige Hayashizaki40a2532f2025-03-07 21:42:00676
677 return std::make_unique<PrefetchHandleImpl>(GetWeakPtr(), prefetch_container);
Hiroshige Hayashizaki7a34d0182023-11-07 20:54:34678}
679
kenossb68c9e82024-10-10 10:11:15680void PrefetchService::AddPrefetchContainerWithoutStartingPrefetchForTesting(
681 std::unique_ptr<PrefetchContainer> prefetch_container) {
682 AddPrefetchContainerWithoutStartingPrefetch(std::move(prefetch_container));
683}
684
Hiroshige Hayashizaki7a34d0182023-11-07 20:54:34685void PrefetchService::PrefetchUrl(
686 base::WeakPtr<PrefetchContainer> prefetch_container) {
kenoss0c7bbc72024-07-24 08:52:40687 CHECK(prefetch_container);
Taiyo Mizuhashi3697ede2025-04-01 01:34:20688 TRACE_EVENT1("loading", "PrefetchService::PrefetchUrl", "prefetch_url",
689 prefetch_container->GetURL());
kenoss0c7bbc72024-07-24 08:52:40690
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49691 auto params = CheckEligibilityParams(
692 {.prefetch_container = prefetch_container,
693 .url = prefetch_container->GetURL(),
694 .is_redirect = false,
695 .callback =
696 base::BindOnce(&PrefetchService::OnGotEligibilityForNonRedirect,
697 weak_method_factory_.GetWeakPtr())});
698
Max Curranead64a62022-06-22 01:10:52699 if (delegate_) {
Hiroshige Hayashizaki84e98432025-07-23 01:06:47700 const auto eligibility_from_delegate = delegate_->IsSomePreloadingEnabled();
Johann41a60832023-01-25 16:20:33701 // If pre* actions are disabled then don't prefetch.
Hiroshige Hayashizaki84e98432025-07-23 01:06:47702 if (eligibility_from_delegate != PreloadingEligibility::kEligible) {
703 std::move(params).Finish(eligibility_from_delegate);
704 return;
Johann41a60832023-01-25 16:20:33705 }
706
Jeremy Roman95c2194d2022-11-30 17:20:25707 const auto& prefetch_type = prefetch_container->GetPrefetchType();
Kouhei Uenof332bdf12023-11-10 06:25:37708 if (prefetch_type.IsProxyRequiredWhenCrossOrigin()) {
Jeremy Roman95c2194d2022-11-30 17:20:25709 bool allow_all_domains =
710 PrefetchAllowAllDomains() ||
711 (PrefetchAllowAllDomainsForExtendedPreloading() &&
712 delegate_->IsExtendedPreloadingEnabled());
713 if (!allow_all_domains &&
Taiyo Mizuhashi9dfeb632024-10-24 08:44:20714 prefetch_container->GetReferringOrigin().has_value() &&
Jeremy Roman95c2194d2022-11-30 17:20:25715 !delegate_->IsDomainInPrefetchAllowList(
Taiyo Mizuhashi9dfeb632024-10-24 08:44:20716 prefetch_container->GetReferringOrigin().value().GetURL())) {
Hiroshige Hayashizaki3876eee2023-03-27 04:42:04717 DVLOG(1) << *prefetch_container
718 << ": not prefetched (not in allow list)";
Jeremy Roman95c2194d2022-11-30 17:20:25719 return;
720 }
Max Curranead64a62022-06-22 01:10:52721 }
Max Curran210cffa2022-09-06 22:24:31722
Taiyo Mizuhashi1b23ca62024-03-28 22:07:37723 // TODO(crbug.com/40946257): Current code doesn't support PageLoadMetrics
724 // when the prefetch is initiated by browser.
725 if (prefetch_container->IsRendererInitiated()) {
726 if (auto* rfh = RenderFrameHost::FromID(
727 prefetch_container->GetReferringRenderFrameHostId())) {
728 if (auto* web_contents = WebContents::FromRenderFrameHost(rfh)) {
729 delegate_->OnPrefetchLikely(web_contents);
730 }
Taiyo Mizuhashi21a2a252024-03-19 05:40:58731 }
732 }
Max Curranead64a62022-06-22 01:10:52733 }
734
kenossb68c9e82024-10-10 10:11:15735 if (GetDelayEligibilityCheckForTesting()) {
736 GetDelayEligibilityCheckForTesting().Run( // IN-TEST
737 base::BindOnce(&PrefetchService::CheckEligibilityOfPrefetch,
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49738 weak_method_factory_.GetWeakPtr(), std::move(params)));
kenossb68c9e82024-10-10 10:11:15739 return;
740 }
741
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49742 CheckEligibilityOfPrefetch(std::move(params));
Max Curran146bf442022-03-28 23:22:14743}
744
745void PrefetchService::CheckEligibilityOfPrefetch(
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49746 CheckEligibilityParams params) {
747 const auto prefetch_container = params.prefetch_container;
Hiroshige Hayashizakif4fc22602025-07-23 01:01:02748 if (!prefetch_container) {
749 // Test-only where the eligibility check is paused and resumed via
750 // `GetDelayEligibilityCheckForTesting()`.
751 std::move(params).Finish(PreloadingEligibility::kEligible);
752 return;
753 }
Liviu Tintaf7d62a72023-08-29 21:48:39754 CHECK(prefetch_container);
Etienne Pierre-doraye0fb4b162025-08-07 16:44:48755 TRACE_EVENT_BEGIN("loading", "PrefetchService::CheckEligibility",
756 perfetto::Track::FromPointer(this));
Max Curran146bf442022-03-28 23:22:14757
kenoss4858a802024-10-11 06:50:56758 // Inject failure in tests.
759 if (GetForceIneligibilityForTesting().has_value()) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49760 std::move(params).Finish(
761 GetForceIneligibilityForTesting().value()); // IN-TEST
kenoss4858a802024-10-11 06:50:56762 return;
763 }
764
Alison Gale770f3fc2024-04-27 00:39:58765 // TODO(crbug.com/40215782): Clean up the following checks by: 1)
Max Curran146bf442022-03-28 23:22:14766 // moving each check to a separate function, and 2) requiring that failed
767 // checks provide a PrefetchStatus related to the check.
768
Max Curran146bf442022-03-28 23:22:14769 // While a registry-controlled domain could still resolve to a non-publicly
770 // routable IP, this allows hosts which are very unlikely to work via the
771 // proxy to be discarded immediately.
Kouhei Ueno58165ea2023-11-13 03:41:07772 //
773 // Conditions on the outer-most if block:
774 // Host-uniqueness check is only applied to proxied prefetches, where that
775 // matters. Also, we bypass the check for the test hosts, since we run the
776 // test web servers on the localhost or private networks, where the check
777 // fails.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49778 if (params.IsProxyRequired()) {
Kouhei Ueno58165ea2023-11-13 03:41:07779 bool is_host_non_unique =
780 g_host_non_unique_filter
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49781 ? g_host_non_unique_filter(params.url.HostNoBrackets())
782 : net::IsHostnameNonUnique(params.url.HostNoBrackets());
Kouhei Ueno58165ea2023-11-13 03:41:07783 if (is_host_non_unique) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49784 std::move(params).Finish(PreloadingEligibility::kHostIsNonUnique);
Kouhei Ueno58165ea2023-11-13 03:41:07785 return;
786 }
Max Curran146bf442022-03-28 23:22:14787 }
788
789 // Only HTTP(S) URLs which are believed to be secure are eligible.
790 // For proxied prefetches, we only want HTTPS URLs.
791 // For non-proxied prefetches, other URLs (notably localhost HTTP) is also
792 // acceptable. This is common during development.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49793 const bool is_secure_http =
794 params.IsProxyRequired()
795 ? params.url.SchemeIs(url::kHttpsScheme)
796 : (params.url.SchemeIsHTTPOrHTTPS() &&
797 network::IsUrlPotentiallyTrustworthy(params.url));
Max Curran146bf442022-03-28 23:22:14798 if (!is_secure_http) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49799 std::move(params).Finish(PreloadingEligibility::kSchemeIsNotHttps);
Max Curran146bf442022-03-28 23:22:14800 return;
801 }
802
Kouhei Uenoea2fcff862023-11-15 09:08:45803 // Fail the prefetch (or more precisely, PrefetchContainer::SinglePrefetch)
804 // early if it is going to go through a proxy, and we know that it is not
805 // available.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49806 if (params.IsProxyRequired() &&
Max Curranead64a62022-06-22 01:10:52807 (!prefetch_proxy_configurator_ ||
808 !prefetch_proxy_configurator_->IsPrefetchProxyAvailable())) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49809 std::move(params).Finish(PreloadingEligibility::kPrefetchProxyNotAvailable);
Max Curran18a6f2b2022-05-02 23:13:24810 return;
811 }
Max Curran146bf442022-03-28 23:22:14812
813 // Only the default storage partition is supported since that is where we
814 // check for service workers and existing cookies.
815 StoragePartition* default_storage_partition =
816 browser_context_->GetDefaultStoragePartition();
817 if (default_storage_partition !=
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49818 browser_context_->GetStoragePartitionForUrl(params.url,
Max Curran146bf442022-03-28 23:22:14819 /*can_create=*/false)) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49820 std::move(params).Finish(
821 PreloadingEligibility::kNonDefaultStoragePartition);
Max Curran146bf442022-03-28 23:22:14822 return;
823 }
824
Max Curranead64a62022-06-22 01:10:52825 // If we have recently received a "retry-after" for the origin, then don't
826 // send new prefetches.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49827 if (delegate_ && !delegate_->IsOriginOutsideRetryAfterWindow(params.url)) {
828 std::move(params).Finish(PreloadingEligibility::kRetryAfter);
Max Curranead64a62022-06-22 01:10:52829 return;
830 }
Max Curran146bf442022-03-28 23:22:14831
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49832 CheckHasServiceWorker(std::move(params));
Liviu Tintaf7d62a72023-08-29 21:48:39833}
834
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49835void PrefetchService::CheckHasServiceWorker(CheckEligibilityParams params) {
836 const auto prefetch_container = params.prefetch_container;
Liviu Tintaf7d62a72023-08-29 21:48:39837 CHECK(prefetch_container);
Etienne Pierre-doraye0fb4b162025-08-07 16:44:48838 TRACE_EVENT_BEGIN("loading", "PrefetchService::CheckHasServiceWorker",
839 perfetto::Track::FromPointer(this));
Hiroshige Hayashizaki0830e182025-03-14 02:03:06840
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49841 if (params.is_redirect) {
Hiroshige Hayashizaki0830e182025-03-14 02:03:06842 switch (prefetch_container->service_worker_state()) {
843 case PrefetchServiceWorkerState::kDisallowed:
844 break;
845
846 case PrefetchServiceWorkerState::kAllowed:
847 // Should have been transitioned out already.
848 NOTREACHED();
849
850 case PrefetchServiceWorkerState::kControlled:
Hiroshige Hayashizakib4af4c2d2025-03-17 18:59:10851 // Currently we disallow redirects from ServiceWorker-controlled
Hiroshige Hayashizaki0830e182025-03-14 02:03:06852 // prefetches.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49853 std::move(params).Finish(
854 PreloadingEligibility::kRedirectFromServiceWorker);
Hiroshige Hayashizaki0830e182025-03-14 02:03:06855 return;
856 }
857 } else {
858 switch (prefetch_container->service_worker_state()) {
859 case PrefetchServiceWorkerState::kDisallowed:
860 break;
861
862 case PrefetchServiceWorkerState::kAllowed:
863 // The controlling ServiceWorker will be checked by
864 // `ServiceWorkerMainResourceLoaderInterceptor` from
865 // `PrefetchStreamingURLLoader`, not here during eligibility check.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49866 OnGotServiceWorkerResult(std::move(params), base::Time::Now(),
Hiroshige Hayashizaki0830e182025-03-14 02:03:06867 ServiceWorkerCapability::NO_SERVICE_WORKER);
868 return;
869
870 case PrefetchServiceWorkerState::kControlled:
871 NOTREACHED();
872 }
873 }
874
Liviu Tintaf7d62a72023-08-29 21:48:39875 // This service worker check assumes that the prefetch will only ever be
876 // performed in a first-party context (main frame prefetch). At the moment
877 // that is true but if it ever changes then the StorageKey will need to be
878 // constructed with the top-level site to ensure correct partitioning.
879 ServiceWorkerContext* service_worker_context =
880 g_service_worker_context_for_testing
881 ? g_service_worker_context_for_testing
882 : browser_context_->GetDefaultStoragePartition()
883 ->GetServiceWorkerContext();
Liviu Tinta91202302023-09-26 17:49:11884 CHECK(service_worker_context);
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49885 auto key =
886 blink::StorageKey::CreateFirstParty(url::Origin::Create(params.url));
Liviu Tintaf7d62a72023-08-29 21:48:39887 // Check `MaybeHasRegistrationForStorageKey` first as it is much faster than
888 // calling `CheckHasServiceWorker`.
Liviu Tinta91202302023-09-26 17:49:11889 auto has_registration_for_storage_key =
890 service_worker_context->MaybeHasRegistrationForStorageKey(key);
891 if (prefetch_container->HasPreloadingAttempt()) {
892 auto* preloading_attempt = static_cast<PreloadingAttemptImpl*>(
893 prefetch_container->preloading_attempt().get());
894 CHECK(preloading_attempt);
895 preloading_attempt->SetServiceWorkerRegisteredCheck(
896 has_registration_for_storage_key
897 ? PreloadingAttemptImpl::ServiceWorkerRegisteredCheck::kPath
898 : PreloadingAttemptImpl::ServiceWorkerRegisteredCheck::kOriginOnly);
899 }
900 if (!has_registration_for_storage_key) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49901 OnGotServiceWorkerResult(std::move(params), base::Time::Now(),
Liviu Tintaf7d62a72023-08-29 21:48:39902 ServiceWorkerCapability::NO_SERVICE_WORKER);
903 return;
904 }
Liviu Tinta91202302023-09-26 17:49:11905 // Start recording here the start of the check for Service Worker registration
906 // for url.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49907 // `url` is needed to avoid use-after-move.
908 const GURL url = params.url;
Liviu Tintaf7d62a72023-08-29 21:48:39909 service_worker_context->CheckHasServiceWorker(
910 url, key,
911 base::BindOnce(&PrefetchService::OnGotServiceWorkerResult,
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49912 weak_method_factory_.GetWeakPtr(), std::move(params),
913 base::Time::Now()));
Liviu Tintaf7d62a72023-08-29 21:48:39914}
915
916void PrefetchService::OnGotServiceWorkerResult(
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49917 CheckEligibilityParams params,
Liviu Tinta91202302023-09-26 17:49:11918 base::Time check_has_service_worker_start_time,
kenoss0c7bbc72024-07-24 08:52:40919 ServiceWorkerCapability service_worker_capability) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49920 const auto prefetch_container = params.prefetch_container;
921
Etienne Pierre-doraye0fb4b162025-08-07 16:44:48922 // End "PrefetchService::CheckHasServiceWorker" trace event.
923 TRACE_EVENT_END("loading", perfetto::Track::FromPointer(this));
Taiyo Mizuhashi3697ede2025-04-01 01:34:20924 TRACE_EVENT1("loading", "PrefetchService::OnGotServiceWorkerResult",
925 "prefetch_url",
926 prefetch_container ? prefetch_container->GetURL().spec() : "");
Liviu Tinta8f750bf2023-10-13 21:49:22927 if (!prefetch_container) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49928 std::move(params).Finish(PreloadingEligibility::kEligible);
Liviu Tinta8f750bf2023-10-13 21:49:22929 return;
930 }
931 CHECK(prefetch_container);
932 if (prefetch_container->HasPreloadingAttempt()) {
Liviu Tinta91202302023-09-26 17:49:11933 const auto duration =
934 base::Time::Now() - check_has_service_worker_start_time;
935 auto* preloading_attempt = static_cast<PreloadingAttemptImpl*>(
936 prefetch_container->preloading_attempt().get());
937 CHECK(preloading_attempt);
938 preloading_attempt->SetServiceWorkerRegisteredCheckDuration(duration);
939 }
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31940 // Note that after ServiceWorker+Prefetch support is implemented,
941 // - For ServiceWorker-eligible prefetches,
942 // `ServiceWorkerCapability::NO_SERVICE_WORKER` is passed here and thus the
943 // ServiceWorker-related ineligibility values here are not used.
944 // - For ServiceWorker-ineligible prefetches (e.g. cross-site prefetches),
945 // they still goes through the checks below and the ServiceWorker-related
946 // ineligibility values here are still valid and used.
Liviu Tintaf7d62a72023-08-29 21:48:39947 switch (service_worker_capability) {
948 case ServiceWorkerCapability::NO_SERVICE_WORKER:
Hiroshige Hayashizaki13aac4c2024-11-22 21:03:19949 break;
Liviu Tintaf7d62a72023-08-29 21:48:39950 case ServiceWorkerCapability::SERVICE_WORKER_NO_FETCH_HANDLER:
Hiroshige Hayashizaki13aac4c2024-11-22 21:03:19951 if (base::FeatureList::IsEnabled(
952 features::kPrefetchServiceWorkerNoFetchHandlerFix)) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49953 std::move(params).Finish(
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31954 PreloadingEligibility::kUserHasServiceWorkerNoFetchHandler);
Hiroshige Hayashizaki13aac4c2024-11-22 21:03:19955 return;
956 }
Liviu Tintaf7d62a72023-08-29 21:48:39957 break;
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31958 case ServiceWorkerCapability::SERVICE_WORKER_WITH_FETCH_HANDLER: {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49959 std::move(params).Finish(
960 params.is_redirect ? PreloadingEligibility::kRedirectToServiceWorker
961 : PreloadingEligibility::kUserHasServiceWorker);
Liviu Tintaf7d62a72023-08-29 21:48:39962 return;
Hiroshige Hayashizaki5fcc71e2025-03-06 21:50:31963 }
Liviu Tintaf7d62a72023-08-29 21:48:39964 }
Liviu Tinta631f96ef2023-10-03 00:05:16965 // This blocks same-site cross-origin prefetches that require the prefetch
966 // proxy. Same-site prefetches are made using the default network context, and
967 // the prefetch request cannot be configured to use the proxy in that network
968 // context.
Alison Gale770f3fc2024-04-27 00:39:58969 // TODO(crbug.com/40265797): Allow same-site cross-origin prefetches
Liviu Tinta631f96ef2023-10-03 00:05:16970 // that require the prefetch proxy to be made.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49971 if (params.IsProxyRequired() &&
Liviu Tinta631f96ef2023-10-03 00:05:16972 !prefetch_container
973 ->IsIsolatedNetworkContextRequiredForCurrentPrefetch()) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49974 std::move(params).Finish(
kenoss0c7bbc72024-07-24 08:52:40975 PreloadingEligibility::kSameSiteCrossOriginPrefetchRequiredProxy);
Liviu Tinta631f96ef2023-10-03 00:05:16976 return;
977 }
978 // We do not need to check the cookies of prefetches that do not need an
979 // isolated network context.
980 if (!prefetch_container
981 ->IsIsolatedNetworkContextRequiredForCurrentPrefetch()) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49982 std::move(params).Finish(PreloadingEligibility::kEligible);
Liviu Tinta631f96ef2023-10-03 00:05:16983 return;
984 }
Jeremy Romane561b412024-02-15 18:31:34985
Liviu Tintaf7d62a72023-08-29 21:48:39986 StoragePartition* default_storage_partition =
987 browser_context_->GetDefaultStoragePartition();
988 CHECK(default_storage_partition);
Etienne Pierre-doraye0fb4b162025-08-07 16:44:48989 TRACE_EVENT_BEGIN("loading", "PrefetchService::CheckCookies",
990 perfetto::Track::FromPointer(this));
Max Curran146bf442022-03-28 23:22:14991 net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
992 options.set_return_excluded_cookies();
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49993 // `url` is needed to avoid use-after-move.
994 const GURL url = params.url;
Max Curran146bf442022-03-28 23:22:14995 default_storage_partition->GetCookieManagerForBrowserProcess()->GetCookieList(
Max Curran5d4da4b42023-03-10 23:41:46996 url, options, net::CookiePartitionKeyCollection::Todo(),
Max Curran146bf442022-03-28 23:22:14997 base::BindOnce(&PrefetchService::OnGotCookiesForEligibilityCheck,
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:49998 weak_method_factory_.GetWeakPtr(), std::move(params)));
Max Curran146bf442022-03-28 23:22:14999}
1000
1001void PrefetchService::OnGotCookiesForEligibilityCheck(
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491002 CheckEligibilityParams params,
Max Curran146bf442022-03-28 23:22:141003 const net::CookieAccessResultList& cookie_list,
kenoss0c7bbc72024-07-24 08:52:401004 const net::CookieAccessResultList& excluded_cookies) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491005 const auto prefetch_container = params.prefetch_container;
1006
Etienne Pierre-doraye0fb4b162025-08-07 16:44:481007 // End "PrefetchService::CheckCookies" trace event.
1008 TRACE_EVENT_END("loading", perfetto::Track::FromPointer(this));
Taiyo Mizuhashi3697ede2025-04-01 01:34:201009 TRACE_EVENT1("loading", "PrefetchService::OnGotCookiesForEligibilityCheck",
1010 "prefetch_url",
1011 prefetch_container ? prefetch_container->GetURL().spec() : "");
Max Curran146bf442022-03-28 23:22:141012 if (!prefetch_container) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491013 std::move(params).Finish(PreloadingEligibility::kEligible);
Max Curran146bf442022-03-28 23:22:141014 return;
1015 }
1016
1017 if (!cookie_list.empty()) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491018 std::move(params).Finish(PreloadingEligibility::kUserHasCookies);
Max Curran146bf442022-03-28 23:22:141019 return;
1020 }
1021
Jeremy Romane561b412024-02-15 18:31:341022 if (base::FeatureList::IsEnabled(
1023 features::kPrefetchStateContaminationMitigation)) {
1024 // The cookie eligibility check just happened, and we might proceed anyway.
1025 // We might therefore need to delay further processing to the extent
1026 // required to obscure the outcome of this check from the current site.
1027 auto* initiator_rfh = RenderFrameHost::FromID(
1028 prefetch_container->GetReferringRenderFrameHostId());
Taiyo Mizuhashi1eaa2dab2025-03-18 16:08:411029 const bool is_contamination_exempt = [&] {
1030 if (!prefetch_container->IsRendererInitiated()) {
1031 // When browser-initiated prefetches, we can calculates prefetch's
1032 // contamination exemption from the referring origin. Currently CCT
1033 // prefetch is only the case hitting this, so the callee will check
1034 // whether it is behind the feature flag tentatively.
1035 // TODO(crbug.com/40946257): Migrate to use this in all cases.
1036 return delegate_ &&
1037 prefetch_container->GetReferringOrigin().has_value() &&
1038 delegate_->IsContaminationExemptPerOrigin(
1039 prefetch_container->GetReferringOrigin().value());
1040 } else {
1041 return delegate_ && initiator_rfh &&
1042 delegate_->IsContaminationExempt(
1043 initiator_rfh->GetLastCommittedURL());
1044 }
1045 }();
1046
Jeremy Romane561b412024-02-15 18:31:341047 if (!is_contamination_exempt) {
1048 prefetch_container->MarkCrossSiteContaminated();
1049 }
1050 }
1051
Max Curran146bf442022-03-28 23:22:141052 // Cookies are tricky because cookies for different paths or a higher level
1053 // domain (e.g.: m.foo.com and foo.com) may not show up in |cookie_list|, but
1054 // they will show up in |excluded_cookies|. To check for any cookies for a
1055 // domain, compare the domains of the prefetched |url| and the domains of all
1056 // the returned cookies.
1057 bool excluded_cookie_has_tld = false;
1058 for (const auto& cookie_result : excluded_cookies) {
1059 if (cookie_result.cookie.IsExpired(base::Time::Now())) {
1060 // Expired cookies don't count.
1061 continue;
1062 }
1063
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491064 if (params.url.DomainIs(cookie_result.cookie.DomainWithoutDot())) {
Max Curran146bf442022-03-28 23:22:141065 excluded_cookie_has_tld = true;
1066 break;
1067 }
1068 }
1069
1070 if (excluded_cookie_has_tld) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491071 std::move(params).Finish(PreloadingEligibility::kUserHasCookies);
Max Curran146bf442022-03-28 23:22:141072 return;
1073 }
1074
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491075 StartProxyLookupCheck(std::move(params));
Max Currancc1ab0c2022-09-12 22:03:111076}
1077
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491078void PrefetchService::StartProxyLookupCheck(CheckEligibilityParams params) {
1079 const auto prefetch_container = params.prefetch_container;
Max Currancc1ab0c2022-09-12 22:03:111080 // Same origin prefetches (which use the default network context and cannot
1081 // use the prefetch proxy) can use the existing proxy settings.
Alison Gale81f4f2c72024-04-22 19:33:311082 // TODO(crbug.com/40231580): Copy proxy settings over to the isolated
Max Currancc1ab0c2022-09-12 22:03:111083 // network context for the prefetch in order to allow non-private cross origin
1084 // prefetches to be made using the existing proxy settings.
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581085 if (!prefetch_container
1086 ->IsIsolatedNetworkContextRequiredForCurrentPrefetch()) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491087 std::move(params).Finish(PreloadingEligibility::kEligible);
Max Currancc1ab0c2022-09-12 22:03:111088 return;
1089 }
1090
Etienne Pierre-doraye0fb4b162025-08-07 16:44:481091 TRACE_EVENT_BEGIN("loading", "PrefetchService::ProxyCheck",
1092 perfetto::Track::FromPointer(this));
Max Currancc1ab0c2022-09-12 22:03:111093 // Start proxy check for this prefetch, and give ownership of the
1094 // |ProxyLookupClientImpl| to |prefetch_container|.
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491095 // `url` is needed to avoid use-after-move.
1096 const GURL url = params.url;
Max Currancc1ab0c2022-09-12 22:03:111097 prefetch_container->TakeProxyLookupClient(
1098 std::make_unique<ProxyLookupClientImpl>(
Lingqi Chia8e39ed52024-08-29 05:20:371099 url,
Max Currancc1ab0c2022-09-12 22:03:111100 base::BindOnce(&PrefetchService::OnGotProxyLookupResult,
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491101 weak_method_factory_.GetWeakPtr(), std::move(params)),
Max Currancc1ab0c2022-09-12 22:03:111102 g_network_context_for_proxy_lookup_for_testing
1103 ? g_network_context_for_proxy_lookup_for_testing
1104 : browser_context_->GetDefaultStoragePartition()
1105 ->GetNetworkContext()));
1106}
1107
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491108void PrefetchService::OnGotProxyLookupResult(CheckEligibilityParams params,
1109 bool has_proxy) {
1110 const auto prefetch_container = params.prefetch_container;
Etienne Pierre-doraye0fb4b162025-08-07 16:44:481111 // End "PrefetchService::ProxyCheck" trace event.
1112 TRACE_EVENT_END("loading", perfetto::Track::FromPointer(this));
Taiyo Mizuhashi3697ede2025-04-01 01:34:201113 TRACE_EVENT1("loading", "PrefetchService::OnGotProxyLookupResult",
1114 "prefetch_url",
1115 prefetch_container ? prefetch_container->GetURL().spec() : "");
Max Currancc1ab0c2022-09-12 22:03:111116 if (!prefetch_container) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491117 std::move(params).Finish(PreloadingEligibility::kEligible);
Max Currancc1ab0c2022-09-12 22:03:111118 return;
1119 }
1120
1121 prefetch_container->ReleaseProxyLookupClient();
1122 if (has_proxy) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491123 std::move(params).Finish(PreloadingEligibility::kExistingProxy);
Max Currancc1ab0c2022-09-12 22:03:111124 return;
1125 }
kenoss0c7bbc72024-07-24 08:52:401126
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491127 std::move(params).Finish(PreloadingEligibility::kEligible);
kenoss0c7bbc72024-07-24 08:52:401128}
1129
1130void PrefetchService::OnGotEligibilityForNonRedirect(
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491131 CheckEligibilityParams params,
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211132 PreloadingEligibility eligibility) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491133 const auto prefetch_container = params.prefetch_container;
Etienne Pierre-doraye0fb4b162025-08-07 16:44:481134 // End "PrefetchService::CheckEligibility" trace event.
1135 TRACE_EVENT_END("loading", perfetto::Track::FromPointer(this));
Taiyo Mizuhashi3697ede2025-04-01 01:34:201136 TRACE_EVENT1("loading", "PrefetchService::OnGotEligibilityForNonRedirect",
1137 "prefetch_url",
1138 prefetch_container ? prefetch_container->GetURL().spec() : "");
William Liu77089052022-12-15 18:53:351139 if (!prefetch_container) {
Melissa Zhang455fa15e2022-12-15 04:01:501140 return;
Max Curran146bf442022-03-28 23:22:141141 }
1142
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211143 const bool eligible = eligibility == PreloadingEligibility::kEligible;
William Liu77089052022-12-15 18:53:351144 bool is_decoy = false;
1145 if (!eligible) {
William Liu77089052022-12-15 18:53:351146 is_decoy =
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491147 params.IsProxyRequired() &&
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211148 ShouldConsiderDecoyRequestForStatus(eligibility) &&
William Liu77089052022-12-15 18:53:351149 PrefetchServiceSendDecoyRequestForIneligblePrefetch(
1150 delegate_ ? delegate_->DisableDecoysBasedOnUserSettings() : false);
1151 }
1152 // The prefetch decoy is pushed onto the queue and the network request will be
1153 // dispatched, but the response will not be used. Thus it is eligible but a
1154 // failure.
1155 prefetch_container->SetIsDecoy(is_decoy);
1156 if (is_decoy) {
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211157 prefetch_container->OnEligibilityCheckComplete(
1158 PreloadingEligibility::kEligible);
William Liu77089052022-12-15 18:53:351159 } else {
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211160 prefetch_container->OnEligibilityCheckComplete(eligibility);
William Liu77089052022-12-15 18:53:351161 }
1162
1163 if (!eligible && !is_decoy) {
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211164 DVLOG(1)
1165 << *prefetch_container
1166 << ": not prefetched (not eligible nor decoy. PreloadingEligibility="
1167 << static_cast<int>(eligibility) << ")";
William Liu77089052022-12-15 18:53:351168 return;
1169 }
1170
1171 if (!is_decoy) {
1172 prefetch_container->SetPrefetchStatus(PrefetchStatus::kPrefetchNotStarted);
Max Curran146bf442022-03-28 23:22:141173
William Liu77089052022-12-15 18:53:351174 // Registers a cookie listener for this prefetch if it is using an isolated
1175 // network context. If the cookies in the default partition associated with
1176 // this URL change after this point, then the prefetched resources should
1177 // not be served.
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581178 if (prefetch_container
1179 ->IsIsolatedNetworkContextRequiredForCurrentPrefetch()) {
William Liu77089052022-12-15 18:53:351180 prefetch_container->RegisterCookieListener(
Hiroshige Hayashizakieec97ed12023-05-26 06:38:321181 browser_context_->GetDefaultStoragePartition()
1182 ->GetCookieManagerForBrowserProcess());
William Liu77089052022-12-15 18:53:351183 }
Max Curranc4445fc2022-06-02 18:43:431184 }
Max Curran9e88b792023-05-11 22:12:581185
kenoss1b8ebc22025-04-04 00:11:351186 if (!UsePrefetchScheduler()) {
1187 prefetch_queue_.push_back(std::move(prefetch_container));
1188 Prefetch();
1189 } else {
kenoss0a567362025-05-16 11:18:551190 if (features::kPrefetchSchedulerProgressSyncBestEffort.Get()) {
1191 ScheduleAndProgress(std::move(prefetch_container));
1192 } else {
1193 ScheduleAndProgressAsync(std::move(prefetch_container));
1194 }
kenoss1b8ebc22025-04-04 00:11:351195 }
Max Curran18a6f2b2022-05-02 23:13:241196}
1197
kenoss0c7bbc72024-07-24 08:52:401198void PrefetchService::OnGotEligibilityForRedirect(
kenossf8d9f35a2024-06-25 07:05:371199 net::RedirectInfo redirect_info,
Hiroshige Hayashizaki1ae6168b2023-06-13 18:15:421200 network::mojom::URLResponseHeadPtr redirect_head,
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491201 CheckEligibilityParams params,
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211202 PreloadingEligibility eligibility) {
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491203 const auto prefetch_container = params.prefetch_container;
Etienne Pierre-doraye0fb4b162025-08-07 16:44:481204 // End "PrefetchService::CheckEligibility" trace event.
1205 TRACE_EVENT_END("loading", perfetto::Track::FromPointer(this));
Taiyo Mizuhashi3697ede2025-04-01 01:34:201206 TRACE_EVENT1("loading", "PrefetchService::OnGotEligibilityForRedirect",
1207 "prefetch_url",
1208 prefetch_container ? prefetch_container->GetURL().spec() : "");
Max Curran5d4da4b42023-03-10 23:41:461209 if (!prefetch_container) {
1210 return;
1211 }
1212
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211213 const bool eligible = eligibility == PreloadingEligibility::kEligible;
Max Curran7d2578b2023-04-12 19:19:281214 RecordRedirectResult(eligible
1215 ? PrefetchRedirectResult::kSuccessRedirectFollowed
1216 : PrefetchRedirectResult::kFailedIneligible);
1217
Max Curran5d4da4b42023-03-10 23:41:461218 // If the redirect is ineligible, the prefetch may change into a decoy.
1219 bool is_decoy = false;
1220 if (!eligible) {
Max Curran5d4da4b42023-03-10 23:41:461221 is_decoy =
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491222 params.IsProxyRequired() &&
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211223 ShouldConsiderDecoyRequestForStatus(eligibility) &&
Max Curran5d4da4b42023-03-10 23:41:461224 PrefetchServiceSendDecoyRequestForIneligblePrefetch(
1225 delegate_ ? delegate_->DisableDecoysBasedOnUserSettings() : false);
1226 }
1227 prefetch_container->SetIsDecoy(prefetch_container->IsDecoy() || is_decoy);
1228
Max Currana84311e2023-05-16 20:40:251229 // Inform the prefetch container of the result of the eligibility check
Max Curran5d4da4b42023-03-10 23:41:461230 if (prefetch_container->IsDecoy()) {
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211231 prefetch_container->OnEligibilityCheckComplete(
1232 PreloadingEligibility::kEligible);
Max Currana84311e2023-05-16 20:40:251233 } else {
Hiroshige Hayashizaki939c5ed42023-11-01 03:29:211234 prefetch_container->OnEligibilityCheckComplete(eligibility);
Max Currana84311e2023-05-16 20:40:251235 if (eligible &&
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581236 prefetch_container
1237 ->IsIsolatedNetworkContextRequiredForCurrentPrefetch()) {
Max Currana84311e2023-05-16 20:40:251238 prefetch_container->RegisterCookieListener(
Hiroshige Hayashizakieec97ed12023-05-26 06:38:321239 browser_context_->GetDefaultStoragePartition()
1240 ->GetCookieManagerForBrowserProcess());
Max Currana84311e2023-05-16 20:40:251241 }
1242 }
1243
Taiyo Mizuhashi08c47d12025-03-05 23:46:071244 // TODO(crbug.com/396133768): Consider setting appropriate PrefetchStatus.
1245 auto streaming_url_loader = prefetch_container->GetStreamingURLLoader();
1246 if (!streaming_url_loader) {
kenoss1b8ebc22025-04-04 00:11:351247 if (!UsePrefetchScheduler()) {
1248 if (active_prefetch_ == prefetch_container->key()) {
1249 active_prefetch_ = std::nullopt;
1250 Prefetch();
1251 }
1252 } else {
kenoss0a567362025-05-16 11:18:551253 // TODO(crbug.com/400761083): Use
1254 // `ResetPrefetchContainerAndProgressAsync()` instead.
1255 RemoveFromSchedulerAndProgressAsync(*prefetch_container);
Taiyo Mizuhashi08c47d12025-03-05 23:46:071256 }
1257 return;
1258 }
1259
Max Currana84311e2023-05-16 20:40:251260 // If the redirect is not eligible and the prefetch is not a decoy, then stop
1261 // the prefetch.
1262 if (!eligible && !prefetch_container->IsDecoy()) {
kenossf7cea3932025-03-28 03:47:371263 CHECK(IsPrefetchContainerInActiveSet(*prefetch_container));
Max Currana84311e2023-05-16 20:40:251264
kenoss1b8ebc22025-04-04 00:11:351265 if (!UsePrefetchScheduler()) {
1266 active_prefetch_ = std::nullopt;
1267 streaming_url_loader->HandleRedirect(PrefetchRedirectStatus::kFail,
1268 redirect_info,
1269 std::move(redirect_head));
Max Currana84311e2023-05-16 20:40:251270
kenoss1b8ebc22025-04-04 00:11:351271 Prefetch();
1272 } else {
1273 // Remove first as it requires that `PrefetchContainer` is available.
kenoss0a567362025-05-16 11:18:551274 RemoveFromSchedulerAndProgressAsync(*prefetch_container);
kenoss1b8ebc22025-04-04 00:11:351275
1276 streaming_url_loader->HandleRedirect(PrefetchRedirectStatus::kFail,
1277 redirect_info,
1278 std::move(redirect_head));
1279
kenoss0a567362025-05-16 11:18:551280 // TODO(crbug.com/400761083): Use
1281 // `ResetPrefetchContainerAndProgressAsync()` instead.
kenoss1b8ebc22025-04-04 00:11:351282 }
Max Curran5d4da4b42023-03-10 23:41:461283 return;
1284 }
1285
kenoss1ce36df592024-12-03 01:04:351286 prefetch_container->NotifyPrefetchRequestWillBeSent(&redirect_head);
Jeremy Roman45c89d012023-08-30 21:22:021287
Max Currana84311e2023-05-16 20:40:251288 // If the redirect requires a change in network contexts, then stop the
1289 // current streaming URL loader and start a new streaming URL loader for the
1290 // redirect URL.
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581291 if (prefetch_container
1292 ->IsIsolatedNetworkContextRequiredForCurrentPrefetch() !=
Max Currana84311e2023-05-16 20:40:251293 prefetch_container
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581294 ->IsIsolatedNetworkContextRequiredForPreviousRedirectHop()) {
Taiyo Mizuhashi08c47d12025-03-05 23:46:071295 streaming_url_loader->HandleRedirect(
Hiroshige Hayashizakiae8c5602023-08-29 00:42:001296 PrefetchRedirectStatus::kSwitchNetworkContext, redirect_info,
1297 std::move(redirect_head));
Hiroshige Hayashizaki73e42892023-06-26 16:48:481298 // The new ResponseReader is associated with the new streaming URL loader at
1299 // the PrefetchStreamingURLLoader constructor.
Jeremy Roman45c89d012023-08-30 21:22:021300 SendPrefetchRequest(prefetch_container);
Max Currana84311e2023-05-16 20:40:251301
1302 return;
Max Curran5d4da4b42023-03-10 23:41:461303 }
Max Currana84311e2023-05-16 20:40:251304
1305 // Otherwise, follow the redirect in the same streaming URL loader.
Taiyo Mizuhashi08c47d12025-03-05 23:46:071306 streaming_url_loader->HandleRedirect(PrefetchRedirectStatus::kFollow,
1307 redirect_info, std::move(redirect_head));
Hiroshige Hayashizaki73e42892023-06-26 16:48:481308 // Associate the new ResponseReader with the current streaming URL loader.
Taiyo Mizuhashi08c47d12025-03-05 23:46:071309 streaming_url_loader->SetResponseReader(
Hiroshige Hayashizaki73e42892023-06-26 16:48:481310 prefetch_container->GetResponseReaderForCurrentPrefetch());
Max Curran5d4da4b42023-03-10 23:41:461311}
1312
Max Curran18a6f2b2022-05-02 23:13:241313void PrefetchService::Prefetch() {
kenoss1b8ebc22025-04-04 00:11:351314 CHECK(!UsePrefetchScheduler());
1315
Max Curran18a6f2b2022-05-02 23:13:241316 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1317
Adithya Srinivasand5e37cb2023-07-05 14:45:231318// Asserts that re-entrancy doesn't happen.
1319#if DCHECK_IS_ON()
1320 DCHECK(!prefetch_reentrancy_guard_);
Jeremy Roman84e6e102023-08-15 16:06:141321 base::AutoReset reset_guard(&prefetch_reentrancy_guard_, true);
Adithya Srinivasand5e37cb2023-07-05 14:45:231322#endif
1323
Max Curran18a6f2b2022-05-02 23:13:241324 if (PrefetchCloseIdleSockets()) {
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:561325 for (const auto& iter : owned_prefetches()) {
Max Currana84311e2023-05-16 20:40:251326 if (iter.second) {
1327 iter.second->CloseIdleConnections();
Max Curran18a6f2b2022-05-02 23:13:241328 }
1329 }
1330 }
1331
1332 base::WeakPtr<PrefetchContainer> next_prefetch = nullptr;
Adithya Srinivasand5e37cb2023-07-05 14:45:231333 base::WeakPtr<PrefetchContainer> prefetch_to_evict = nullptr;
1334 while ((std::tie(next_prefetch, prefetch_to_evict) =
1335 PopNextPrefetchContainer()) !=
1336 std::make_tuple(nullptr, nullptr)) {
kenoss03cdaca32025-03-28 03:05:141337 if (prefetch_to_evict) {
1338 EvictPrefetch(std::move(prefetch_to_evict));
1339 }
1340 StartSinglePrefetch(next_prefetch);
Max Curran18a6f2b2022-05-02 23:13:241341 }
1342}
1343
Adithya Srinivasand5e37cb2023-07-05 14:45:231344std::tuple<base::WeakPtr<PrefetchContainer>, base::WeakPtr<PrefetchContainer>>
1345PrefetchService::PopNextPrefetchContainer() {
kenoss1b8ebc22025-04-04 00:11:351346 CHECK(!UsePrefetchScheduler());
1347
Max Curran18a6f2b2022-05-02 23:13:241348 auto new_end = std::remove_if(
1349 prefetch_queue_.begin(), prefetch_queue_.end(),
Max Curran7c6210c82022-09-07 00:19:051350 [&](const base::WeakPtr<PrefetchContainer>& prefetch_container) {
1351 // Remove all prefetches from queue that no longer exist.
keno8ae912c2023-04-28 06:01:541352 return !prefetch_container;
Max Curran18a6f2b2022-05-02 23:13:241353 });
1354 prefetch_queue_.erase(new_end, prefetch_queue_.end());
1355
Adithya Srinivasan9d1b6fa2024-08-29 15:04:351356 // Don't start any new prefetches if we are currently running one.
1357 if (active_prefetch_.has_value()) {
1358 DVLOG(1) << "PrefetchService::PopNextPrefetchContainer: already running a "
1359 "prefetch.";
Adithya Srinivasand5e37cb2023-07-05 14:45:231360 return std::make_tuple(nullptr, nullptr);
Max Curran18a6f2b2022-05-02 23:13:241361 }
1362
Adithya Srinivasand5e37cb2023-07-05 14:45:231363 base::WeakPtr<PrefetchContainer> prefetch_to_evict;
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371364 // Get the first prefetch can be prefetched currently. For the triggers
1365 // managed by PrefetchDocumentManager, this depends on the state of the
1366 // initiating document, and the number of completed prefetches (this can also
1367 // result in previously completed prefetches being evicted).
Peter Kasting1557e5f2025-01-28 01:14:081368 auto prefetch_iter = std::ranges::find_if(
Peter Kasting837e2ccb2022-09-07 14:13:171369 prefetch_queue_,
Adithya Srinivasand5e37cb2023-07-05 14:45:231370 [&](const base::WeakPtr<PrefetchContainer>& prefetch_container) {
kenoss1b8ebc22025-04-04 00:11:351371 // Keep this method as similar as much as possible to
1372 // `IsReadyToStartLoading` in
1373 // //content/browser/preloading/prefetch/prefetch_scheduler.cc.
1374
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371375 if (!prefetch_container->IsRendererInitiated()) {
1376 // TODO(crbug.com/40946257): Revisit the resource limits and
1377 // conditions for starting browser-initiated prefetch.
1378 return true;
1379 }
1380
1381 auto* prefetch_document_manager =
1382 prefetch_container->GetPrefetchDocumentManager();
kenoss1b8ebc22025-04-04 00:11:351383 // If there is no manager in renderer-initiated prefetch (can happen
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371384 // only in tests), just bypass the check.
1385 if (!prefetch_document_manager) {
kenoss1b8ebc22025-04-04 00:11:351386 CHECK_IS_TEST();
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371387 return true;
1388 }
Adithya Srinivasand5e37cb2023-07-05 14:45:231389 bool can_prefetch_now = false;
1390 std::tie(can_prefetch_now, prefetch_to_evict) =
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371391 prefetch_document_manager->CanPrefetchNow(prefetch_container.get());
Adithya Srinivasand5e37cb2023-07-05 14:45:231392 // |prefetch_to_evict| should only be set if |can_prefetch_now| is true.
1393 DCHECK(!prefetch_to_evict || can_prefetch_now);
1394 return can_prefetch_now;
Max Curran18a6f2b2022-05-02 23:13:241395 });
1396 if (prefetch_iter == prefetch_queue_.end()) {
Adithya Srinivasand5e37cb2023-07-05 14:45:231397 return std::make_tuple(nullptr, nullptr);
Max Curran18a6f2b2022-05-02 23:13:241398 }
1399
1400 base::WeakPtr<PrefetchContainer> next_prefetch_container = *prefetch_iter;
1401 prefetch_queue_.erase(prefetch_iter);
Adithya Srinivasand5e37cb2023-07-05 14:45:231402 return std::make_tuple(next_prefetch_container, prefetch_to_evict);
Max Curran18a6f2b2022-05-02 23:13:241403}
1404
Adithya Srinivasand5e37cb2023-07-05 14:45:231405void PrefetchService::OnPrefetchTimeout(
1406 base::WeakPtr<PrefetchContainer> prefetch_container) {
Adithya Srinivasan006b3652023-12-14 14:52:121407 prefetch_container->SetPrefetchStatus(PrefetchStatus::kPrefetchIsStale);
kenoss1b8ebc22025-04-04 00:11:351408 if (!UsePrefetchScheduler()) {
1409 ResetPrefetchContainer(prefetch_container);
Adithya Srinivasand5e37cb2023-07-05 14:45:231410
kenoss1b8ebc22025-04-04 00:11:351411 if (!active_prefetch_) {
1412 Prefetch();
1413 }
1414 } else {
kenoss0a567362025-05-16 11:18:551415 ResetPrefetchContainerAndProgressAsync(std::move(prefetch_container));
Adithya Srinivasand5e37cb2023-07-05 14:45:231416 }
1417}
1418
Taiyo Mizuhashi94ce74f2025-01-28 04:20:141419void PrefetchService::MayReleasePrefetch(
1420 base::WeakPtr<PrefetchContainer> prefetch_container) {
1421 if (!prefetch_container) {
1422 return;
1423 }
1424
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:561425 if (!base::Contains(owned_prefetches(), prefetch_container->key())) {
Taiyo Mizuhashi94ce74f2025-01-28 04:20:141426 return;
1427 }
1428
kenoss1b8ebc22025-04-04 00:11:351429 if (!UsePrefetchScheduler()) {
1430 ResetPrefetchContainer(prefetch_container);
Taiyo Mizuhashi4e624a22025-05-15 11:28:541431 if (base::FeatureList::IsEnabled(
1432 features::kPrefetchQueueingPartialFixWithoutScheduler) &&
1433 !active_prefetch_) {
1434 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
1435 FROM_HERE, base::BindOnce(&PrefetchService::Prefetch,
1436 weak_method_factory_.GetWeakPtr()));
1437 }
kenoss1b8ebc22025-04-04 00:11:351438 } else {
kenoss0a567362025-05-16 11:18:551439 ResetPrefetchContainerAndProgressAsync(std::move(prefetch_container));
kenoss1b8ebc22025-04-04 00:11:351440 }
Taiyo Mizuhashi94ce74f2025-01-28 04:20:141441}
1442
1443void PrefetchService::ResetPrefetchContainer(
kenosse8d8bbb52025-05-13 12:35:041444 base::WeakPtr<PrefetchContainer> prefetch_container,
1445 bool should_progress) {
Hiroshige Hayashizakif59d9022023-11-08 00:08:091446 CHECK(prefetch_container);
kenoss1b8ebc22025-04-04 00:11:351447
1448 if (!UsePrefetchScheduler()) {
1449 if (active_prefetch_ == prefetch_container->key()) {
1450 active_prefetch_ = std::nullopt;
1451 }
1452 } else {
1453 // Remove before calling `PrefetchContainer::dtor()` as `PrefetchScheduler`
1454 // manages them with weak pointers.
kenosse8d8bbb52025-05-13 12:35:041455 scheduler_->RemoveAndProgressAsync(*prefetch_container, should_progress);
kenoss1b8ebc22025-04-04 00:11:351456 }
1457
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:561458 auto it = owned_prefetches().find(prefetch_container->key());
1459 CHECK(it != owned_prefetches().end());
Hiroshige Hayashizakif59d9022023-11-08 00:08:091460 CHECK_EQ(it->second.get(), prefetch_container.get());
Hiroshige Hayashizakif59d9022023-11-08 00:08:091461 owned_prefetches_.erase(it);
Max Curran18a6f2b2022-05-02 23:13:241462}
1463
kenoss1b8ebc22025-04-04 00:11:351464void PrefetchService::ScheduleAndProgress(
1465 base::WeakPtr<PrefetchContainer> prefetch_container) {
1466 CHECK(UsePrefetchScheduler());
1467 CHECK(prefetch_container);
1468
kenoss0a567362025-05-16 11:18:551469 scheduler_->PushAndProgress(*prefetch_container);
1470}
1471
1472void PrefetchService::ScheduleAndProgressAsync(
1473 base::WeakPtr<PrefetchContainer> prefetch_container) {
1474 CHECK(UsePrefetchScheduler());
1475 CHECK(prefetch_container);
1476
kenoss1b8ebc22025-04-04 00:11:351477 scheduler_->PushAndProgressAsync(*prefetch_container);
1478
1479 // `PrefetchScheduler::Progress()` will be called asynchronously.
1480}
1481
kenoss0a567362025-05-16 11:18:551482void PrefetchService::ResetPrefetchContainerAndProgressAsync(
kenoss1b8ebc22025-04-04 00:11:351483 base::WeakPtr<PrefetchContainer> prefetch_container) {
1484 CHECK(UsePrefetchScheduler());
1485
1486 ResetPrefetchContainer(std::move(prefetch_container));
1487
1488 // `PrefetchScheduler::Progress()` will be called asynchronously.
1489}
1490
kenoss0a567362025-05-16 11:18:551491void PrefetchService::ResetPrefetchContainersAndProgressAsync(
kenoss1b8ebc22025-04-04 00:11:351492 std::vector<base::WeakPtr<PrefetchContainer>> prefetch_containers) {
1493 CHECK(UsePrefetchScheduler());
1494
1495 for (auto& prefetch_container : prefetch_containers) {
1496 ResetPrefetchContainer(std::move(prefetch_container));
kenossea7ac6a22025-04-03 09:05:541497 }
kenoss1b8ebc22025-04-04 00:11:351498
1499 // `PrefetchScheduler::Progress()` will be called asynchronously.
1500}
1501
kenoss0a567362025-05-16 11:18:551502void PrefetchService::RemoveFromSchedulerAndProgressAsync(
kenoss1b8ebc22025-04-04 00:11:351503 PrefetchContainer& prefetch_container) {
1504 CHECK(UsePrefetchScheduler());
1505
1506 scheduler_->RemoveAndProgressAsync(prefetch_container);
1507
1508 // `PrefetchScheduler::Progress()` will be called asynchronously.
1509}
1510
1511void PrefetchService::OnCandidatesUpdated() {
1512 if (!UsePrefetchScheduler()) {
1513 if (!active_prefetch_) {
1514 Prefetch();
1515 }
1516 } else {
1517 // Before `kPrefetchScheduler`, calling `Prefetch()` here was necessary to
1518 // progress scheduling as modifying `PrefetchService::queue_` doesn't set
1519 // `active_prefetch_`.
1520 //
1521 // After `kPrefetchScheduler`, `PrefetchScheduler` ensures that modifying
1522 // `PrefetchQueue` triggers `PrefetchScheduler::Progress()` eventually. So,
1523 // we believe that this explicit `Progress()` call is not necessary. But we
1524 // keep it because 1. It's safe (as it's not reentrancy) and noop if not
1525 // necessary. 2. We should another experiment to remove the call as we are
1526 // using `PrefetchScheduler` in some experiments.
1527 //
1528 // TODO(crbug.com/406754449): Consider to remove it.
1529 scheduler_->Progress();
1530 }
1531}
1532
1533void PrefetchService::EvictPrefetch(
1534 base::PassKey<PrefetchScheduler>,
1535 base::WeakPtr<PrefetchContainer> prefetch_container) {
1536 EvictPrefetch(std::move(prefetch_container));
kenossea7ac6a22025-04-03 09:05:541537}
1538
1539void PrefetchService::EvictPrefetch(
kenoss03cdaca32025-03-28 03:05:141540 base::WeakPtr<PrefetchContainer> prefetch_container) {
1541 CHECK(prefetch_container);
1542
1543 prefetch_container->SetPrefetchStatus(
1544 PrefetchStatus::kPrefetchEvictedForNewerPrefetch);
1545 ResetPrefetchContainer(std::move(prefetch_container));
1546}
1547
kenoss1b8ebc22025-04-04 00:11:351548bool PrefetchService::StartSinglePrefetch(
1549 base::PassKey<PrefetchScheduler>,
1550 base::WeakPtr<PrefetchContainer> prefetch_container) {
1551 return StartSinglePrefetch(std::move(prefetch_container));
1552}
1553
1554bool PrefetchService::StartSinglePrefetch(
kenoss03cdaca32025-03-28 03:05:141555 base::WeakPtr<PrefetchContainer> prefetch_container) {
Max Curran18a6f2b2022-05-02 23:13:241556 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Hiroshige Hayashizaki4ce0f4292023-11-07 19:24:581557 CHECK(prefetch_container);
1558 CHECK_EQ(prefetch_container->GetLoadState(),
1559 PrefetchContainer::LoadState::kEligible);
Max Curran18a6f2b2022-05-02 23:13:241560
William Liu77089052022-12-15 18:53:351561 // Do not prefetch for a Holdback control group. Called after the checks in
1562 // `PopNextPrefetchContainer` because we want to compare against the
1563 // prefetches that would have been dispatched.
1564 if (CheckAndSetPrefetchHoldbackStatus(prefetch_container)) {
Hiroshige Hayashizaki3876eee2023-03-27 04:42:041565 DVLOG(1) << *prefetch_container
1566 << ": not prefetched (holdback control group)";
kenoss1b8ebc22025-04-04 00:11:351567 return false;
William Liu77089052022-12-15 18:53:351568 }
1569
Wayne Jackson Jr.03a15fa62024-09-16 14:42:151570 prefetch_container->OnPrefetchStarted();
Hiroshige Hayashizaki4ce0f4292023-11-07 19:24:581571
elabadysayedf7d6b00f2025-02-05 11:27:001572 // Checks if the `PrefetchContainer` has a specific TTL (Time-to-Live)
1573 // configured. If a TTL is configured, the prefetch container will be eligible
1574 // for removal after the TTL expires. Otherwise, it will remain alive
1575 // indefinitely.
1576 //
1577 // The default TTL is determined by
1578 // `PrefetchContainerDefaultTtlInPrefetchService()`, which may return a zero
1579 // or negative value, indicating an indefinite TTL.
1580 prefetch_container->StartTimeoutTimerIfNeeded(
1581 base::BindOnce(&PrefetchService::OnPrefetchTimeout,
1582 weak_method_factory_.GetWeakPtr(), prefetch_container));
Hiroshige Hayashizakid2161a2e2023-10-23 16:22:501583
kenoss1b8ebc22025-04-04 00:11:351584 if (!UsePrefetchScheduler()) {
1585 active_prefetch_.emplace(prefetch_container->key());
1586 }
keno8ae912c2023-04-28 06:01:541587
Max Curran18a6f2b2022-05-02 23:13:241588 if (!prefetch_container->IsDecoy()) {
1589 // The status is updated to be successful or failed when it finishes.
1590 prefetch_container->SetPrefetchStatus(
1591 PrefetchStatus::kPrefetchNotFinishedInTime);
1592 }
1593
Jeremy Roman45c89d012023-08-30 21:22:021594 net::HttpRequestHeaders additional_headers;
1595 additional_headers.SetHeader(
1596 net::HttpRequestHeaders::kAccept,
1597 FrameAcceptHeaderValue(/*allow_sxg_responses=*/true, browser_context_));
1598 prefetch_container->MakeResourceRequest(additional_headers);
1599
kenoss1ce36df592024-12-03 01:04:351600 prefetch_container->NotifyPrefetchRequestWillBeSent(
1601 /*redirect_head=*/nullptr);
Jeremy Roman45c89d012023-08-30 21:22:021602
1603 SendPrefetchRequest(prefetch_container);
Max Currana84311e2023-05-16 20:40:251604
Max Currana84311e2023-05-16 20:40:251605 PrefetchDocumentManager* prefetch_document_manager =
1606 prefetch_container->GetPrefetchDocumentManager();
Max Currane6f63dca2023-05-17 01:16:221607 if (prefetch_container->GetPrefetchType().IsProxyRequiredWhenCrossOrigin() &&
1608 !prefetch_container->IsDecoy() &&
Max Currana84311e2023-05-16 20:40:251609 (!prefetch_document_manager ||
1610 !prefetch_document_manager->HaveCanaryChecksStarted())) {
Taiyo Mizuhashi1b23ca62024-03-28 22:07:371611 // TODO(crbug.com/40946257): Currently browser-initiated prefetch will
1612 // always perform canary checks since there is no PrefetchDocumentManager.
1613 // Revisit and add proper handlings.
1614
Max Currana84311e2023-05-16 20:40:251615 // Make sure canary checks have run so we know the result by the time we
1616 // want to use the prefetch. Checking the canary cache can be a slow and
1617 // blocking operation (see crbug.com/1266018), so we only do this for the
1618 // first non-decoy prefetch we make on the page.
Alison Gale81f4f2c72024-04-22 19:33:311619 // TODO(crbug.com/40801832): once this bug is fixed, fire off canary check
Max Currana84311e2023-05-16 20:40:251620 // regardless of whether the request is a decoy or not.
1621 origin_prober_->RunCanaryChecksIfNeeded();
1622
1623 if (prefetch_document_manager) {
1624 prefetch_document_manager->OnCanaryChecksStarted();
1625 }
1626 }
1627
1628 // Start a spare renderer now so that it will be ready by the time it is
1629 // useful to have.
1630 if (ShouldStartSpareRenderer()) {
Patrick Monettea45111342024-09-24 00:33:291631 SpareRenderProcessHostManager::Get().WarmupSpare(browser_context_);
Max Currana84311e2023-05-16 20:40:251632 }
kenoss1b8ebc22025-04-04 00:11:351633
1634 return true;
Max Currana84311e2023-05-16 20:40:251635}
1636
Jeremy Roman45c89d012023-08-30 21:22:021637void PrefetchService::SendPrefetchRequest(
1638 base::WeakPtr<PrefetchContainer> prefetch_container) {
Yoshisato Yanagisawad2c11f72025-02-03 10:49:351639 TRACE_EVENT("loading", "PrefetchService::SendPrefetchRequest");
Max Curran18a6f2b2022-05-02 23:13:241640 net::NetworkTrafficAnnotationTag traffic_annotation =
1641 net::DefineNetworkTrafficAnnotation("speculation_rules_prefetch",
1642 R"(
1643 semantics {
1644 sender: "Speculation Rules Prefetch Loader"
1645 description:
1646 "Prefetches the mainframe HTML of a page specified via "
1647 "speculation rules. This is done out-of-band of normal "
1648 "prefetches to allow total isolation of this request from the "
1649 "rest of browser traffic and user state like cookies and cache."
1650 trigger:
1651 "Used only when this feature and speculation rules feature are "
1652 "enabled."
1653 data: "None."
1654 destination: WEBSITE
1655 }
1656 policy {
1657 cookies_allowed: NO
1658 setting:
1659 "Users can control this via a setting specific to each content "
1660 "embedder."
1661 policy_exception_justification: "Not implemented."
1662 })");
1663
Hiroshige Hayashizakic2560e62023-09-19 01:03:361664 auto streaming_loader = PrefetchStreamingURLLoader::CreateAndStart(
Jeremy Roman45c89d012023-08-30 21:22:021665 GetURLLoaderFactoryForCurrentPrefetch(prefetch_container),
1666 *prefetch_container->GetResourceRequest(), traffic_annotation,
1667 PrefetchTimeoutDuration(),
Hiroshige Hayashizaki64b91822023-08-29 05:15:521668 base::BindOnce(&PrefetchService::OnPrefetchResponseStarted,
1669 base::Unretained(this), prefetch_container),
Hiroshige Hayashizaki64b91822023-08-29 05:15:521670 base::BindRepeating(&PrefetchService::OnPrefetchRedirect,
1671 base::Unretained(this), prefetch_container),
Hiroshige Hayashizaki0830e182025-03-14 02:03:061672 prefetch_container->GetResponseReaderForCurrentPrefetch(),
1673 prefetch_container->service_worker_state(), browser_context_,
1674 base::BindOnce(&PrefetchContainer::OnServiceWorkerStateDetermined,
1675 prefetch_container));
Hiroshige Hayashizaki638018f2023-09-12 17:05:451676 prefetch_container->SetStreamingURLLoader(std::move(streaming_loader));
Max Curran892ca5422022-12-12 20:55:341677
Hiroshige Hayashizaki3876eee2023-03-27 04:42:041678 DVLOG(1) << *prefetch_container << ": PrefetchStreamingURLLoader is created.";
Max Curran18a6f2b2022-05-02 23:13:241679}
1680
Hiroshige Hayashizakie51e2f262025-03-04 23:14:381681scoped_refptr<network::SharedURLLoaderFactory>
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581682PrefetchService::GetURLLoaderFactoryForCurrentPrefetch(
1683 base::WeakPtr<PrefetchContainer> prefetch_container) {
Max Curran18a6f2b2022-05-02 23:13:241684 DCHECK(prefetch_container);
1685 if (g_url_loader_factory_for_testing) {
Hiroshige Hayashizakie51e2f262025-03-04 23:14:381686 return base::WrapRefCounted(g_url_loader_factory_for_testing);
Max Curran18a6f2b2022-05-02 23:13:241687 }
Kouhei Ueno4442db92023-11-13 06:38:131688 return prefetch_container->GetOrCreateNetworkContextForCurrentPrefetch()
1689 ->GetURLLoaderFactory(this);
Max Curran18a6f2b2022-05-02 23:13:241690}
1691
Max Currana84311e2023-05-16 20:40:251692void PrefetchService::OnPrefetchRedirect(
Max Curran18a6f2b2022-05-02 23:13:241693 base::WeakPtr<PrefetchContainer> prefetch_container,
1694 const net::RedirectInfo& redirect_info,
Hiroshige Hayashizaki1ae6168b2023-06-13 18:15:421695 network::mojom::URLResponseHeadPtr redirect_head) {
Taiyo Mizuhashi3697ede2025-04-01 01:34:201696 TRACE_EVENT1("loading", "PrefetchService::OnPrefetchRedirect", "prefetch_url",
1697 prefetch_container ? prefetch_container->GetURL().spec() : "");
Max Curran18a6f2b2022-05-02 23:13:241698 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Max Curran18a6f2b2022-05-02 23:13:241699
Max Currand7c05692023-03-30 19:33:121700 if (!prefetch_container) {
Max Curran7d2578b2023-04-12 19:19:281701 RecordRedirectResult(PrefetchRedirectResult::kFailedNullPrefetch);
Max Currana84311e2023-05-16 20:40:251702 return;
Max Curran5d4da4b42023-03-10 23:41:461703 }
Max Curran18a6f2b2022-05-02 23:13:241704
kenossf7cea3932025-03-28 03:47:371705 CHECK(IsPrefetchContainerInActiveSet(*prefetch_container));
Max Currane0444942022-09-06 23:55:231706
Devlin Cronin7f318c12023-06-09 00:57:011707 // Update the prefetch's referrer in case a redirect requires a change in
1708 // network context and a new request needs to be started.
Hiroshige Hayashizaki1919c7c2023-10-13 21:46:421709 const auto new_referrer_policy =
Devlin Cronin7f318c12023-06-09 00:57:011710 blink::ReferrerUtils::NetToMojoReferrerPolicy(
Hiroshige Hayashizaki1919c7c2023-10-13 21:46:421711 redirect_info.new_referrer_policy);
Devlin Cronin7f318c12023-06-09 00:57:011712
Arthur Sonzognic686e8f2024-01-11 08:36:371713 std::optional<PrefetchRedirectResult> failure;
Jeremy Roman337e4952024-07-04 20:36:491714 if (redirect_info.new_method != "GET") {
Hiroshige Hayashizaki1ae6168b2023-06-13 18:15:421715 failure = PrefetchRedirectResult::kFailedInvalidMethod;
1716 } else if (!redirect_head->headers ||
1717 redirect_head->headers->response_code() < 300 ||
1718 redirect_head->headers->response_code() >= 400) {
1719 failure = PrefetchRedirectResult::kFailedInvalidResponseCode;
Charlie Harrison15329a922025-03-07 14:36:181720 } else if (!net::SchemefulSite::IsSameSite(
1721 prefetch_container->GetCurrentURL(), redirect_info.new_url) &&
Hiroshige Hayashizaki1919c7c2023-10-13 21:46:421722 !IsReferrerPolicySufficientlyStrict(new_referrer_policy)) {
1723 // The new referrer policy is not sufficiently strict to allow cross-site
1724 // redirects.
Hiroshige Hayashizaki1ae6168b2023-06-13 18:15:421725 failure = PrefetchRedirectResult::kFailedInsufficientReferrerPolicy;
1726 }
1727
1728 if (failure) {
kenossf7cea3932025-03-28 03:47:371729 CHECK(IsPrefetchContainerInActiveSet(*prefetch_container));
Igor Kraskevichdd52f572025-04-03 11:02:411730
kenoss1b8ebc22025-04-04 00:11:351731 if (!UsePrefetchScheduler()) {
1732 active_prefetch_ = std::nullopt;
1733 prefetch_container->SetPrefetchStatus(
1734 PrefetchStatus::kPrefetchFailedInvalidRedirect);
1735 if (auto streaming_url_loader =
1736 prefetch_container->GetStreamingURLLoader()) {
1737 streaming_url_loader->HandleRedirect(PrefetchRedirectStatus::kFail,
1738 redirect_info,
1739 std::move(redirect_head));
1740 }
1741
1742 Prefetch();
1743 RecordRedirectResult(*failure);
1744 } else {
1745 RecordRedirectResult(*failure);
1746
1747 prefetch_container->SetPrefetchStatus(
1748 PrefetchStatus::kPrefetchFailedInvalidRedirect);
1749
1750 // Remove first as it requires that `PrefetchContainer` is available.
kenoss0a567362025-05-16 11:18:551751 RemoveFromSchedulerAndProgressAsync(*prefetch_container);
kenoss1b8ebc22025-04-04 00:11:351752
1753 if (auto streaming_url_loader =
1754 prefetch_container->GetStreamingURLLoader()) {
1755 streaming_url_loader->HandleRedirect(PrefetchRedirectStatus::kFail,
1756 redirect_info,
1757 std::move(redirect_head));
1758 }
1759
kenoss0a567362025-05-16 11:18:551760 // TODO(crbug.com/400761083): Use
1761 // `ResetPrefetchContainerAndProgressAsync()` instead.
kenoss1b8ebc22025-04-04 00:11:351762 }
Max Currana84311e2023-05-16 20:40:251763 return;
Iman Saboori65c356d2022-07-15 14:47:471764 }
1765
Hiroshige Hayashizaki1919c7c2023-10-13 21:46:421766 prefetch_container->AddRedirectHop(redirect_info);
1767 prefetch_container->UpdateReferrer(GURL(redirect_info.new_referrer),
1768 new_referrer_policy);
1769
Max Currana84311e2023-05-16 20:40:251770 RecordRedirectNetworkContextTransition(
Max Curran48700962023-05-15 18:35:521771 prefetch_container
Hiroshige Hayashizakib8c4bf602023-05-26 01:34:581772 ->IsIsolatedNetworkContextRequiredForPreviousRedirectHop(),
1773 prefetch_container->IsIsolatedNetworkContextRequiredForCurrentPrefetch());
Max Curran5d4da4b42023-03-10 23:41:461774
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491775 auto params = CheckEligibilityParams(
1776 {.prefetch_container = prefetch_container,
1777 .url = redirect_info.new_url,
1778 .is_redirect = true,
1779 .callback = base::BindOnce(&PrefetchService::OnGotEligibilityForRedirect,
1780 weak_method_factory_.GetWeakPtr(),
1781 redirect_info, std::move(redirect_head))});
kenossb68c9e82024-10-10 10:11:151782
1783 if (GetDelayEligibilityCheckForTesting()) {
1784 GetDelayEligibilityCheckForTesting().Run( // IN-TEST
1785 base::BindOnce(&PrefetchService::CheckEligibilityOfPrefetch,
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491786 weak_method_factory_.GetWeakPtr(), std::move(params)));
kenossb68c9e82024-10-10 10:11:151787 return;
1788 }
1789
Hiroshige Hayashizaki692e3eb2025-07-18 21:03:491790 CheckEligibilityOfPrefetch(std::move(params));
Max Curran18a6f2b2022-05-02 23:13:241791}
1792
Arthur Sonzognic686e8f2024-01-11 08:36:371793std::optional<PrefetchErrorOnResponseReceived>
Hiroshige Hayashizaki556ac552023-10-24 01:36:321794PrefetchService::OnPrefetchResponseStarted(
Max Curran892ca5422022-12-12 20:55:341795 base::WeakPtr<PrefetchContainer> prefetch_container,
1796 network::mojom::URLResponseHead* head) {
Taiyo Mizuhashi3697ede2025-04-01 01:34:201797 TRACE_EVENT1("loading", "PrefetchService::OnPrefetchResponseStarted",
1798 "prefetch_url",
1799 prefetch_container ? prefetch_container->GetURL().spec() : "");
Max Curran892ca5422022-12-12 20:55:341800 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Max Curran892ca5422022-12-12 20:55:341801
1802 if (!prefetch_container || prefetch_container->IsDecoy()) {
Hiroshige Hayashizaki556ac552023-10-24 01:36:321803 return PrefetchErrorOnResponseReceived::kPrefetchWasDecoy;
Max Curran892ca5422022-12-12 20:55:341804 }
1805
1806 if (!head) {
Hiroshige Hayashizaki556ac552023-10-24 01:36:321807 return PrefetchErrorOnResponseReceived::kFailedInvalidHead;
Max Curran892ca5422022-12-12 20:55:341808 }
1809
Jeremy Romane561b412024-02-15 18:31:341810 if (prefetch_container && prefetch_container->IsCrossSiteContaminated()) {
1811 head->is_prefetch_with_cross_site_contamination = true;
1812 }
1813
kenoss1ce36df592024-12-03 01:04:351814 prefetch_container->NotifyPrefetchResponseReceived(*head);
Max Curran892ca5422022-12-12 20:55:341815
1816 if (!head->headers) {
Hiroshige Hayashizaki556ac552023-10-24 01:36:321817 return PrefetchErrorOnResponseReceived::kFailedInvalidHeaders;
Max Curran892ca5422022-12-12 20:55:341818 }
1819
1820 RecordPrefetchProxyPrefetchMainframeTotalTime(head);
1821 RecordPrefetchProxyPrefetchMainframeConnectTime(head);
1822
1823 int response_code = head->headers->response_code();
1824 RecordPrefetchProxyPrefetchMainframeRespCode(response_code);
1825 if (response_code < 200 || response_code >= 300) {
1826 prefetch_container->SetPrefetchStatus(
1827 PrefetchStatus::kPrefetchFailedNon2XX);
1828
1829 if (response_code == net::HTTP_SERVICE_UNAVAILABLE) {
1830 base::TimeDelta retry_after;
1831 std::string retry_after_string;
1832 if (head->headers->EnumerateHeader(nullptr, "Retry-After",
1833 &retry_after_string) &&
1834 net::HttpUtil::ParseRetryAfterHeader(
1835 retry_after_string, base::Time::Now(), &retry_after) &&
1836 delegate_) {
Max Curran14dd7872023-03-20 19:57:361837 // Cap the retry after value to a maximum.
Kouhei Ueno5ad21962023-11-16 04:38:541838 static constexpr base::TimeDelta max_retry_after = base::Days(7);
Kouhei Ueno824da492023-11-16 02:09:201839 if (retry_after > max_retry_after) {
1840 retry_after = max_retry_after;
Max Curran14dd7872023-03-20 19:57:361841 }
1842
Max Curran892ca5422022-12-12 20:55:341843 delegate_->ReportOriginRetryAfter(prefetch_container->GetURL(),
1844 retry_after);
1845 }
1846 }
Hiroshige Hayashizaki556ac552023-10-24 01:36:321847 return PrefetchErrorOnResponseReceived::kFailedNon2XX;
Max Curran892ca5422022-12-12 20:55:341848 }
1849
1850 if (PrefetchServiceHTMLOnly() && head->mime_type != "text/html") {
1851 prefetch_container->SetPrefetchStatus(
1852 PrefetchStatus::kPrefetchFailedMIMENotSupported);
Hiroshige Hayashizaki556ac552023-10-24 01:36:321853 return PrefetchErrorOnResponseReceived::kFailedMIMENotSupported;
Max Curran892ca5422022-12-12 20:55:341854 }
Arthur Sonzognic686e8f2024-01-11 08:36:371855 return std::nullopt;
Max Curran892ca5422022-12-12 20:55:341856}
1857
Hiroshige Hayashizaki70db51b2025-07-14 23:09:491858void PrefetchService::OnWillBeDestroyed(PrefetchContainer& prefetch_container) {
1859}
1860
1861void PrefetchService::OnGotInitialEligibility(
1862 PrefetchContainer& prefetch_container,
1863 PreloadingEligibility eligibility) {}
1864
1865void PrefetchService::OnDeterminedHead(PrefetchContainer& prefetch_container) {}
1866
1867void PrefetchService::OnPrefetchCompletedOrFailed(
1868 PrefetchContainer& prefetch_container,
1869 const network::URLLoaderCompletionStatus& completion_status,
Hiroshige Hayashizaki12ab6d72025-07-15 00:27:451870 const std::optional<int>& response_code) {
1871 TRACE_EVENT2("loading", "PrefetchService::OnPrefetchCompletedOrFailed",
1872 "prefetch_url", prefetch_container.GetURL().spec(),
Taiyo Mizuhashi3697ede2025-04-01 01:34:201873 "completion_status.error_code", completion_status.error_code);
Max Curran892ca5422022-12-12 20:55:341874 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Hiroshige Hayashizaki12ab6d72025-07-15 00:27:451875 CHECK(IsPrefetchContainerInActiveSet(prefetch_container));
Max Curran892ca5422022-12-12 20:55:341876
kenoss1b8ebc22025-04-04 00:11:351877 if (!UsePrefetchScheduler()) {
1878 active_prefetch_ = std::nullopt;
kenoss1b8ebc22025-04-04 00:11:351879 Prefetch();
1880 } else {
Hiroshige Hayashizaki12ab6d72025-07-15 00:27:451881 RemoveFromSchedulerAndProgressAsync(prefetch_container);
kenoss1b8ebc22025-04-04 00:11:351882 }
Max Curran892ca5422022-12-12 20:55:341883}
1884
Max Curranc4445fc2022-06-02 18:43:431885void PrefetchService::CopyIsolatedCookies(
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571886 const PrefetchServingHandle& serving_handle) {
1887 DCHECK(serving_handle);
Max Curranc4445fc2022-06-02 18:43:431888
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571889 if (!serving_handle.GetCurrentNetworkContextToServe()) {
Max Curranc4445fc2022-06-02 18:43:431890 // Not set in unit tests.
1891 return;
1892 }
1893
1894 // We only need to copy cookies if the prefetch used an isolated network
1895 // context.
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571896 if (!serving_handle.IsIsolatedNetworkContextRequiredToServe()) {
Max Curranc4445fc2022-06-02 18:43:431897 return;
1898 }
1899
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571900 serving_handle.OnIsolatedCookieCopyStart();
Max Curranc4445fc2022-06-02 18:43:431901 net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571902 serving_handle.GetCurrentNetworkContextToServe()
1903 ->GetCookieManager()
1904 ->GetCookieList(
1905 serving_handle.GetCurrentURLToServe(), options,
1906 net::CookiePartitionKeyCollection::Todo(),
1907 base::BindOnce(&PrefetchService::OnGotIsolatedCookiesForCopy,
1908 weak_method_factory_.GetWeakPtr(),
1909 serving_handle.Clone()));
Max Curranc4445fc2022-06-02 18:43:431910}
1911
1912void PrefetchService::OnGotIsolatedCookiesForCopy(
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571913 PrefetchServingHandle serving_handle,
Max Curranc4445fc2022-06-02 18:43:431914 const net::CookieAccessResultList& cookie_list,
1915 const net::CookieAccessResultList& excluded_cookies) {
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571916 serving_handle.OnIsolatedCookiesReadCompleteAndWriteStart();
Max Curranc4445fc2022-06-02 18:43:431917 RecordPrefetchProxyPrefetchMainframeCookiesToCopy(cookie_list.size());
1918
1919 if (cookie_list.empty()) {
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571920 serving_handle.OnIsolatedCookieCopyComplete();
Max Curranc4445fc2022-06-02 18:43:431921 return;
1922 }
1923
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571924 const auto current_url = serving_handle.GetCurrentURLToServe();
Hiroshige Hayashizaki00b3f002023-07-15 00:32:481925
Max Curranc4445fc2022-06-02 18:43:431926 base::RepeatingClosure barrier = base::BarrierClosure(
1927 cookie_list.size(),
Hiroshige Hayashizaki16b6e54f2025-08-12 06:56:571928 base::BindOnce(&OnIsolatedCookieCopyComplete, std::move(serving_handle)));
Max Curranc4445fc2022-06-02 18:43:431929
1930 net::CookieOptions options = net::CookieOptions::MakeAllInclusive();
1931 for (const net::CookieWithAccessResult& cookie : cookie_list) {
1932 browser_context_->GetDefaultStoragePartition()
1933 ->GetCookieManagerForBrowserProcess()
Hiroshige Hayashizaki00b3f002023-07-15 00:32:481934 ->SetCanonicalCookie(cookie.cookie, current_url, options,
1935 base::BindOnce(&CookieSetHelper, barrier));
Max Curranc4445fc2022-06-02 18:43:431936 }
1937}
1938
kenoss1b8ebc22025-04-04 00:11:351939// TODO(crbug.com/406754449): Inline this function when removing the feature
1940// flag.
kenossf7cea3932025-03-28 03:47:371941bool PrefetchService::IsPrefetchContainerInActiveSet(
1942 const PrefetchContainer& prefetch_container) {
kenoss1b8ebc22025-04-04 00:11:351943 if (!UsePrefetchScheduler()) {
1944 return active_prefetch_ == prefetch_container.key();
1945 } else {
1946 return scheduler_->IsInActiveSet(prefetch_container);
1947 }
kenossf7cea3932025-03-28 03:47:371948}
1949
Hiroshige Hayashizaki3876eee2023-03-27 04:42:041950void PrefetchService::DumpPrefetchesForDebug() const {
1951#if DCHECK_IS_ON()
1952 std::ostringstream ss;
1953 ss << "PrefetchService[" << this << "]:" << std::endl;
1954
1955 ss << "Owned:" << std::endl;
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:561956 for (const auto& entry : owned_prefetches()) {
Hiroshige Hayashizakid2161a2e2023-10-23 16:22:501957 ss << *entry.second << std::endl;
Hiroshige Hayashizaki3876eee2023-03-27 04:42:041958 }
1959
Hiroshige Hayashizaki3876eee2023-03-27 04:42:041960 DVLOG(1) << ss.str();
1961#endif // DCHECK_IS_ON()
1962}
1963
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:081964std::pair<std::vector<PrefetchContainer*>,
1965 base::flat_map<PrefetchContainer::Key, PrefetchServableState>>
kenoss4dc068662024-10-04 05:03:501966PrefetchService::CollectMatchCandidates(
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:111967 const PrefetchContainer::Key& key,
kenoss804a72dc2024-10-11 12:43:491968 bool is_nav_prerender,
Hiroshige Hayashizaki6a2bc752023-10-31 19:08:111969 base::WeakPtr<PrefetchServingPageMetricsContainer>
1970 serving_page_metrics_container) {
kenosse0135122024-09-25 05:21:341971 return CollectMatchCandidatesGeneric(
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:561972 owned_prefetches(), key, is_nav_prerender,
kenoss804a72dc2024-10-11 12:43:491973 std::move(serving_page_metrics_container));
Liviu Tintad608e012023-05-10 19:16:411974}
1975
Hiroshige Hayashizakia00e4c82023-10-31 20:20:171976base::WeakPtr<PrefetchContainer> PrefetchService::MatchUrl(
1977 const PrefetchContainer::Key& key) const {
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:561978 return no_vary_search::MatchUrl(key, owned_prefetches());
Hiroshige Hayashizakia00e4c82023-10-31 20:20:171979}
1980
1981std::vector<std::pair<GURL, base::WeakPtr<PrefetchContainer>>>
1982PrefetchService::GetAllForUrlWithoutRefAndQueryForTesting(
1983 const PrefetchContainer::Key& key) const {
1984 return no_vary_search::GetAllForUrlWithoutRefAndQueryForTesting(
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:561985 key, owned_prefetches());
Hiroshige Hayashizakia00e4c82023-10-31 20:20:171986}
1987
Max Curran146bf442022-03-28 23:22:141988// static
1989void PrefetchService::SetServiceWorkerContextForTesting(
1990 ServiceWorkerContext* context) {
1991 g_service_worker_context_for_testing = context;
1992}
1993
1994// static
1995void PrefetchService::SetHostNonUniqueFilterForTesting(
Md Hasibul Hasana963a9342024-04-03 10:15:141996 bool (*filter)(std::string_view)) {
Max Curran146bf442022-03-28 23:22:141997 g_host_non_unique_filter = filter;
Max Curran6c2835ea2022-03-07 19:52:381998}
1999
Max Curran18a6f2b2022-05-02 23:13:242000// static
2001void PrefetchService::SetURLLoaderFactoryForTesting(
Hiroshige Hayashizakie51e2f262025-03-04 23:14:382002 network::SharedURLLoaderFactory* url_loader_factory) {
Max Curran18a6f2b2022-05-02 23:13:242003 g_url_loader_factory_for_testing = url_loader_factory;
2004}
2005
Max Currancc1ab0c2022-09-12 22:03:112006// static
2007void PrefetchService::SetNetworkContextForProxyLookupForTesting(
2008 network::mojom::NetworkContext* network_context) {
2009 g_network_context_for_proxy_lookup_for_testing = network_context;
2010}
2011
Taiyo Mizuhashi9395b7c82024-05-16 20:30:262012// static
kenossb68c9e82024-10-10 10:11:152013void PrefetchService::SetDelayEligibilityCheckForTesting(
2014 DelayEligibilityCheckForTesting callback) {
2015 GetDelayEligibilityCheckForTesting() = // IN-TEST
2016 std::move(callback);
2017}
2018
2019// static
kenoss4858a802024-10-11 06:50:562020void PrefetchService::SetForceIneligibilityForTesting(
2021 PreloadingEligibility eligibility) {
2022 GetForceIneligibilityForTesting() = // IN-TEST
2023 eligibility;
2024}
2025
Hiroshige Hayashizakia00e4c82023-10-31 20:20:172026base::WeakPtr<PrefetchService> PrefetchService::GetWeakPtr() {
2027 return weak_method_factory_.GetWeakPtr();
2028}
2029
Max Curran679a3d52022-07-11 23:03:312030void PrefetchService::RecordExistingPrefetchWithMatchingURL(
Hiroshige Hayashizaki70db51b2025-07-14 23:09:492031 const PrefetchContainer& prefetch_container) const {
Max Curran679a3d52022-07-11 23:03:312032 bool matching_prefetch = false;
Max Curran4f4c802a2022-10-27 23:52:042033 int num_matching_prefetches = 0;
2034
2035 int num_matching_eligible_prefetch = 0;
2036 int num_matching_servable_prefetch = 0;
2037 int num_matching_prefetch_same_referrer = 0;
2038 int num_matching_prefetch_same_rfh = 0;
2039
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:562040 for (const auto& prefetch_iter : owned_prefetches()) {
Max Curran080b6002022-07-14 19:36:332041 if (prefetch_iter.second &&
Hiroshige Hayashizaki70db51b2025-07-14 23:09:492042 prefetch_iter.second->GetURL() == prefetch_container.GetURL()) {
Max Curran679a3d52022-07-11 23:03:312043 matching_prefetch = true;
Max Curran4f4c802a2022-10-27 23:52:042044 num_matching_prefetches++;
2045
Hiroshige Hayashizaki7a197802025-07-18 21:05:362046 switch (prefetch_iter.second->GetLoadState()) {
2047 case PrefetchContainer::LoadState::kNotStarted:
2048 case PrefetchContainer::LoadState::kFailedIneligible:
2049 break;
2050 case PrefetchContainer::LoadState::kEligible:
2051 case PrefetchContainer::LoadState::kFailedHeldback:
2052 case PrefetchContainer::LoadState::kStarted:
Hiroshige Hayashizaki6b7035c02025-07-18 21:06:322053 case PrefetchContainer::LoadState::kDeterminedHead:
2054 case PrefetchContainer::LoadState::kCompletedOrFailed:
Hiroshige Hayashizaki7a197802025-07-18 21:05:362055 num_matching_eligible_prefetch++;
2056 break;
Max Curran4f4c802a2022-10-27 23:52:042057 }
2058
Hiroshige Hayashizakie556eb02023-09-13 00:20:482059 switch (
2060 prefetch_iter.second->GetServableState(PrefetchCacheableDuration())) {
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:082061 case PrefetchServableState::kNotServable:
2062 case PrefetchServableState::kShouldBlockUntilHeadReceived:
2063 case PrefetchServableState::kShouldBlockUntilEligibilityGot:
Hiroshige Hayashizakie556eb02023-09-13 00:20:482064 break;
Hiroshige Hayashizakib3ff61d2025-08-12 06:28:082065 case PrefetchServableState::kServable:
kenoss8596b682025-07-01 10:35:462066 num_matching_servable_prefetch++;
Hiroshige Hayashizakie556eb02023-09-13 00:20:482067 break;
Max Curran4f4c802a2022-10-27 23:52:042068 }
2069
Kevin McNee06824c72024-02-06 18:59:522070 if (prefetch_iter.second->HasSameReferringURLForMetrics(
Hiroshige Hayashizaki70db51b2025-07-14 23:09:492071 prefetch_container)) {
Max Curran4f4c802a2022-10-27 23:52:042072 num_matching_prefetch_same_referrer++;
2073 }
2074
2075 if (prefetch_iter.second->GetReferringRenderFrameHostId() ==
Hiroshige Hayashizaki70db51b2025-07-14 23:09:492076 prefetch_container.GetReferringRenderFrameHostId()) {
Max Curran4f4c802a2022-10-27 23:52:042077 num_matching_prefetch_same_rfh++;
2078 }
Max Curran679a3d52022-07-11 23:03:312079 }
2080 }
2081
2082 base::UmaHistogramBoolean(
2083 "PrefetchProxy.Prefetch.ExistingPrefetchWithMatchingURL",
2084 matching_prefetch);
Max Curran4f4c802a2022-10-27 23:52:042085 base::UmaHistogramCounts100(
2086 "PrefetchProxy.Prefetch.NumExistingPrefetchWithMatchingURL",
2087 num_matching_prefetches);
2088
2089 if (matching_prefetch) {
2090 base::UmaHistogramCounts100(
2091 "PrefetchProxy.Prefetch.NumExistingEligiblePrefetchWithMatchingURL",
2092 num_matching_eligible_prefetch);
2093 base::UmaHistogramCounts100(
2094 "PrefetchProxy.Prefetch.NumExistingServablePrefetchWithMatchingURL",
2095 num_matching_servable_prefetch);
2096 base::UmaHistogramCounts100(
2097 "PrefetchProxy.Prefetch.NumExistingPrefetchWithMatchingURLAndReferrer",
2098 num_matching_prefetch_same_referrer);
2099 base::UmaHistogramCounts100(
2100 "PrefetchProxy.Prefetch."
2101 "NumExistingPrefetchWithMatchingURLAndRenderFrameHost",
2102 num_matching_prefetch_same_rfh);
2103 }
Max Curran679a3d52022-07-11 23:03:312104}
2105
Steven Weie829c85f72025-03-17 18:57:592106void PrefetchService::EvictPrefetchesForBrowsingDataRemoval(
2107 const StoragePartition::StorageKeyMatcherFunction& storage_key_filter,
2108 PrefetchStatus status) {
Steven Weie829c85f72025-03-17 18:57:592109 std::vector<base::WeakPtr<PrefetchContainer>> prefetches_to_reset;
Hiroshige Hayashizaki72ea9b92025-07-05 02:47:562110 for (const auto& prefetch_iter : owned_prefetches()) {
Steven Weie829c85f72025-03-17 18:57:592111 base::WeakPtr<PrefetchContainer> prefetch_container =
2112 prefetch_iter.second->GetWeakPtr();
2113 CHECK(prefetch_container);
Taiyo Mizuhashiaf23c432025-06-23 07:41:372114
2115 // If `referring_origin` is std::nullopt (e.g some browser-initiated
2116 // prefetch), use the origin of the prefetch URL itself, since we generally
2117 // handle no referring origin prefetches as a same-origin prefetch fashion.
2118 const url::Origin target_origin =
2119 prefetch_container->GetReferringOrigin().value_or(
2120 url::Origin::Create(prefetch_container->GetURL()));
2121 if (storage_key_filter.Run(
2122 blink::StorageKey::CreateFirstParty(target_origin))) {
Steven Weie829c85f72025-03-17 18:57:592123 prefetch_container->SetPrefetchStatus(status);
2124 prefetches_to_reset.push_back(prefetch_container);
2125 }
2126 }
2127
kenoss1b8ebc22025-04-04 00:11:352128 if (!UsePrefetchScheduler()) {
2129 for (const auto& prefetch_container : prefetches_to_reset) {
2130 ResetPrefetchContainer(prefetch_container);
2131 }
2132 } else {
kenoss0a567362025-05-16 11:18:552133 ResetPrefetchContainersAndProgressAsync(std::move(prefetches_to_reset));
Steven Weie829c85f72025-03-17 18:57:592134 }
2135}
2136
Max Curran6c2835ea2022-03-07 19:52:382137} // namespace content