blob: 46507bec7dca0998259636c78006097927129c7e [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2020 The Chromium Authors
Hiroki Nakagawa968139e22020-10-22 15:03:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Sreeja Kamishettyc227f7a2022-07-08 22:33:155#include "content/browser/preloading/prerender/prerender_host_registry.h"
Hiroki Nakagawa968139e22020-10-22 15:03:566
7#include "base/check.h"
Yoshiki Tanioka87aab3f2022-09-13 06:25:378#include "base/check_op.h"
Lei Zhangde197672021-04-29 08:11:249#include "base/containers/contains.h"
Hiroki Nakagawa968139e22020-10-22 15:03:5610#include "base/feature_list.h"
Yoshiki Tanioka80b78fb2022-10-27 23:40:5411#include "base/functional/bind.h"
Avi Drissmanadac21992023-01-11 23:46:3912#include "base/functional/callback_helpers.h"
Hiroki Nakagawa34e5d672023-04-21 05:00:0913#include "base/memory/memory_pressure_monitor.h"
Matt Falkenhagen9373d4e2021-08-11 16:50:2214#include "base/metrics/field_trial_params.h"
Yoshiki Tanioka75decd92022-10-26 12:23:0615#include "base/notreached.h"
David Sandersd4bf5eb2022-03-17 07:12:0516#include "base/observer_list.h"
Matt Falkenhagenf884d0f2021-02-18 06:31:3517#include "base/system/sys_info.h"
Sean Maher52fa5a72022-11-14 15:53:2518#include "base/task/sequenced_task_runner.h"
Sean Mahere672a662023-01-09 21:42:2819#include "base/task/single_thread_task_runner.h"
Matt Falkenhagen9373d4e2021-08-11 16:50:2220#include "build/build_config.h"
Andrey Kosyakov6e82c3b2022-09-26 22:43:4621#include "content/browser/devtools/devtools_instrumentation.h"
Robert Lin56cb9c42022-11-22 07:57:3022#include "content/browser/devtools/render_frame_devtools_agent_host.h"
Kouhei Ueno3f37992b2023-11-09 23:29:0223#include "content/browser/preloading/preloading_trigger_type_impl.h"
kenod930e48032023-05-18 11:38:4024#include "content/browser/preloading/prerender/devtools_prerender_attempt.h"
Taiyo Mizuhashi92119792023-09-13 07:18:0325#include "content/browser/preloading/prerender/prerender_features.h"
Yoshiki Tanioka49b4cfb2022-10-20 09:25:3126#include "content/browser/preloading/prerender/prerender_final_status.h"
Sreeja Kamishettyc227f7a2022-07-08 22:33:1527#include "content/browser/preloading/prerender/prerender_metrics.h"
Robert Lin20d947ed2022-08-25 06:01:2828#include "content/browser/preloading/prerender/prerender_navigation_utils.h"
Hiroki Nakagawa3e2527042022-11-24 06:46:4229#include "content/browser/preloading/prerender/prerender_new_tab_handle.h"
Hiroki Nakagawaeec217f2020-12-09 08:41:1330#include "content/browser/renderer_host/frame_tree_node.h"
Harkiran Bolariaba823e42021-05-21 18:30:3631#include "content/browser/renderer_host/navigation_request.h"
Hiroki Nakagawaeec217f2020-12-09 08:41:1332#include "content/browser/renderer_host/render_frame_host_impl.h"
Lei Zhang5686e522023-03-02 17:33:1033#include "content/browser/web_contents/web_contents_impl.h"
34#include "content/common/frame.mojom.h"
Domenic Denicolabb5d843c2023-07-18 01:47:3335#include "content/public/browser/preloading.h"
Lei Zhang5686e522023-03-02 17:33:1036#include "content/public/browser/preloading_data.h"
Yoshiki Tanioka87aab3f2022-09-13 06:25:3737#include "content/public/browser/render_frame_host.h"
Yoshiki Taniokab6a6d722022-10-20 07:58:5038#include "content/public/browser/visibility.h"
Robert Lin39aaced2021-09-22 10:23:3539#include "content/public/browser/web_contents.h"
Sreeja Kamishettyc94a0d22022-07-06 20:43:0640#include "content/public/browser/web_contents_delegate.h"
Kevin McNee097680d2023-04-05 22:14:5141#include "net/base/load_flags.h"
42#include "services/network/public/cpp/simple_url_loader.h"
Lei Zhang5686e522023-03-02 17:33:1043#include "services/resource_coordinator/public/cpp/memory_instrumentation/global_memory_dump.h"
Asami Doi3b6840212022-07-28 02:39:1944#include "services/resource_coordinator/public/cpp/memory_instrumentation/memory_instrumentation.h"
Hiroki Nakagawa968139e22020-10-22 15:03:5645#include "third_party/blink/public/common/features.h"
Lei Zhang5686e522023-03-02 17:33:1046#include "url/gurl.h"
Hiroki Nakagawa968139e22020-10-22 15:03:5647
48namespace content {
49
Matt Falkenhagen9373d4e2021-08-11 16:50:2250namespace {
51
Hiroki Nakagawa98af8c42023-03-14 05:36:3852bool IsBackground(Visibility visibility) {
53 // PrerenderHostRegistry treats HIDDEN and OCCLUDED as background.
54 switch (visibility) {
55 case Visibility::HIDDEN:
56 case Visibility::OCCLUDED:
57 return true;
58 case Visibility::VISIBLE:
59 return false;
60 }
61}
62
Matt Falkenhagen9373d4e2021-08-11 16:50:2263bool DeviceHasEnoughMemoryForPrerender() {
64 // This method disallows prerendering on low-end devices if the
65 // kPrerender2MemoryControls feature is enabled.
66 if (!base::FeatureList::IsEnabled(blink::features::kPrerender2MemoryControls))
67 return true;
68
Max "👨200d💻" Coplan0bdabf3f2023-09-08 13:04:5369 // On Android, Prerender2 is only enabled for 2GB+ high memory devices. The
70 // default threshold value is set to 1700 MB to account for all 2GB devices
71 // which report lower RAM due to carveouts.
72 // Previously used the same default threshold as the back/forward cache. See
73 // comments in DeviceHasEnoughMemoryForBackForwardCache().
74 // TODO(https://crbug.com/1470820): experiment with 1200 MB threshold like
75 // back/forward cache.
Matt Falkenhagen9373d4e2021-08-11 16:50:2276 static constexpr int kDefaultMemoryThresholdMb =
Xiaohan Wang1e4ebde2022-01-15 17:29:1277#if BUILDFLAG(IS_ANDROID)
Matt Falkenhagen9373d4e2021-08-11 16:50:2278 1700;
79#else
80 0;
81#endif
82
83 // The default is overridable by field trial param.
84 int memory_threshold_mb = base::GetFieldTrialParamByFeatureAsInt(
85 blink::features::kPrerender2MemoryControls,
86 blink::features::kPrerender2MemoryThresholdParamName,
87 kDefaultMemoryThresholdMb);
88
89 return base::SysInfo::AmountOfPhysicalMemoryMB() > memory_threshold_mb;
90}
91
Hiroki Nakagawa34e5d672023-04-21 05:00:0992base::MemoryPressureListener::MemoryPressureLevel
93GetCurrentMemoryPressureLevel() {
94 // Ignore the memory pressure event if the memory control is disabled.
95 if (!base::FeatureList::IsEnabled(
96 blink::features::kPrerender2MemoryControls)) {
97 return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
98 }
99
100 auto* monitor = base::MemoryPressureMonitor::Get();
101 if (!monitor) {
102 return base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE;
103 }
104 return monitor->GetCurrentPressureLevel();
105}
106
Kevin McNee097680d2023-04-05 22:14:51107// Create a resource request for `back_url` that only checks whether the
108// resource is in the HTTP cache.
109std::unique_ptr<network::SimpleURLLoader> CreateHttpCacheQueryingResourceLoad(
110 const GURL& back_url) {
111 url::Origin origin = url::Origin::Create(back_url);
112 net::IsolationInfo isolation_info = net::IsolationInfo::Create(
113 net::IsolationInfo::RequestType::kMainFrame, origin, origin,
114 net::SiteForCookies::FromOrigin(origin));
115 network::ResourceRequest::TrustedParams trusted_params;
116 trusted_params.isolation_info = isolation_info;
117
118 std::unique_ptr<network::ResourceRequest> request =
119 std::make_unique<network::ResourceRequest>();
120 request->url = back_url;
121 request->load_flags =
122 net::LOAD_ONLY_FROM_CACHE | net::LOAD_SKIP_CACHE_VALIDATION;
123 request->trusted_params = trusted_params;
124 request->site_for_cookies = trusted_params.isolation_info.site_for_cookies();
125 request->credentials_mode = network::mojom::CredentialsMode::kOmit;
126 request->skip_service_worker = true;
127 request->do_not_prompt_for_login = true;
128
129 CHECK(!request->SendsCookies());
130 CHECK(!request->SavesCookies());
131 constexpr net::NetworkTrafficAnnotationTag traffic_annotation =
132 net::DefineNetworkTrafficAnnotation("back_navigation_cache_query",
133 R"(
134 semantics {
135 sender: "Prerender"
136 description:
137 "This is not actually a network request. It is used internally "
138 "by the browser to determine if the HTTP cache would be used if "
139 "the user were to navigate back in session history. It only "
140 "checks the cache and does not hit the network."
141 trigger:
142 "When the user performs an action that would suggest that they "
143 "intend to navigate back soon. Examples include hovering the "
144 "mouse over the back button and the start of a gestural back "
145 "navigation."
146 user_data {
147 type: NONE
148 }
149 data: "None. The request doesn't hit the network."
150 destination: LOCAL
151 internal {
152 contacts {
153 email: "chrome-brapp-loading@chromium.org"
154 }
155 }
156 last_reviewed: "2023-03-24"
157 }
158 policy {
159 cookies_allowed: NO
160 setting:
161 "This is not controlled by a setting."
162 policy_exception_justification: "This is not a network request."
163 })");
164
165 return network::SimpleURLLoader::Create(std::move(request),
166 traffic_annotation);
167}
168
169// Returns true if the given navigation is meant to be predicted by a predictor
170// related to session history (e.g. hovering over the back button could have
171// predicted the navigation).
172bool IsNavigationInSessionHistoryPredictorDomain(NavigationHandle* handle) {
173 CHECK(handle->IsInPrimaryMainFrame());
174 CHECK(!handle->IsSameDocument());
175
176 if (handle->IsRendererInitiated()) {
177 return false;
178 }
179
180 // Note that currently the only predictors are for back navigations of a
181 // single step, however we still include all session history navigations in
182 // the domain. The preloading of back navigations could generalize to session
183 // history navigations of other offsets, but we haven't explored this due to
184 // the higher usage of the back button compared to the forward button or
185 // history menu.
186 if (!(handle->GetPageTransition() & ui::PAGE_TRANSITION_FORWARD_BACK)) {
187 return false;
188 }
189
190 if (handle->IsPost()) {
191 return false;
192 }
193
194 if (handle->IsServedFromBackForwardCache()) {
195 return false;
196 }
197
198 if (!handle->GetURL().SchemeIsHTTPOrHTTPS()) {
199 return false;
200 }
201
202 // Note that even though the current predictors do not handle session history
203 // navigations that are same-site or which don't use the HTTP cache, they are
204 // still included in the domain.
205 return true;
206}
207
Hiroki Nakagawa3c252282023-04-18 10:31:35208bool IsDevToolsOpen(WebContents& web_contents) {
209 return DevToolsAgentHost::HasFor(&web_contents);
210}
211
keno85004bc2023-05-18 09:45:41212PreloadingEligibility ToEligibility(PrerenderFinalStatus status) {
213 switch (status) {
214 case PrerenderFinalStatus::kActivated:
215 case PrerenderFinalStatus::kDestroyed:
216 NOTREACHED_NORETURN();
217 case PrerenderFinalStatus::kLowEndDevice:
218 return PreloadingEligibility::kLowMemory;
219 case PrerenderFinalStatus::kInvalidSchemeRedirect:
Hiroki Nakagawafc7fd702023-08-28 15:47:56220 NOTREACHED_NORETURN();
keno85004bc2023-05-18 09:45:41221 case PrerenderFinalStatus::kInvalidSchemeNavigation:
Hiroki Nakagawafc7fd702023-08-28 15:47:56222 return PreloadingEligibility::kHttpOrHttpsOnly;
keno85004bc2023-05-18 09:45:41223 case PrerenderFinalStatus::kNavigationRequestBlockedByCsp:
224 case PrerenderFinalStatus::kMainFrameNavigation:
225 case PrerenderFinalStatus::kMojoBinderPolicy:
226 case PrerenderFinalStatus::kRendererProcessCrashed:
227 case PrerenderFinalStatus::kRendererProcessKilled:
228 case PrerenderFinalStatus::kDownload:
229 case PrerenderFinalStatus::kTriggerDestroyed:
230 case PrerenderFinalStatus::kNavigationNotCommitted:
231 case PrerenderFinalStatus::kNavigationBadHttpStatus:
232 case PrerenderFinalStatus::kClientCertRequested:
233 case PrerenderFinalStatus::kNavigationRequestNetworkError:
keno85004bc2023-05-18 09:45:41234 case PrerenderFinalStatus::kCancelAllHostsForTesting:
235 case PrerenderFinalStatus::kDidFailLoad:
236 case PrerenderFinalStatus::kStop:
237 case PrerenderFinalStatus::kSslCertificateError:
238 case PrerenderFinalStatus::kLoginAuthRequested:
239 case PrerenderFinalStatus::kUaChangeRequiresReload:
240 case PrerenderFinalStatus::kBlockedByClient:
keno85004bc2023-05-18 09:45:41241 case PrerenderFinalStatus::kMixedContent:
242 NOTREACHED_NORETURN();
243 case PrerenderFinalStatus::kTriggerBackgrounded:
244 return PreloadingEligibility::kHidden;
keno85004bc2023-05-18 09:45:41245 case PrerenderFinalStatus::kMemoryLimitExceeded:
keno85004bc2023-05-18 09:45:41246 NOTREACHED_NORETURN();
247 case PrerenderFinalStatus::kDataSaverEnabled:
248 return PreloadingEligibility::kDataSaverEnabled;
Hiroki Nakagawad77864c2023-09-20 22:34:03249 case PrerenderFinalStatus::kTriggerUrlHasEffectiveUrl:
keno85004bc2023-05-18 09:45:41250 return PreloadingEligibility::kHasEffectiveUrl;
251 case PrerenderFinalStatus::kActivatedBeforeStarted:
252 case PrerenderFinalStatus::kInactivePageRestriction:
253 case PrerenderFinalStatus::kStartFailed:
254 case PrerenderFinalStatus::kTimeoutBackgrounded:
255 case PrerenderFinalStatus::kCrossSiteRedirectInInitialNavigation:
256 NOTREACHED_NORETURN();
257 case PrerenderFinalStatus::kCrossSiteNavigationInInitialNavigation:
258 return PreloadingEligibility::kCrossOrigin;
259 case PrerenderFinalStatus::
260 kSameSiteCrossOriginRedirectNotOptInInInitialNavigation:
261 case PrerenderFinalStatus::
262 kSameSiteCrossOriginNavigationNotOptInInInitialNavigation:
263 case PrerenderFinalStatus::kActivationNavigationParameterMismatch:
264 case PrerenderFinalStatus::kActivatedInBackground:
265 case PrerenderFinalStatus::kEmbedderHostDisallowed:
266 case PrerenderFinalStatus::kActivationNavigationDestroyedBeforeSuccess:
267 case PrerenderFinalStatus::kTabClosedByUserGesture:
268 case PrerenderFinalStatus::kTabClosedWithoutUserGesture:
269 case PrerenderFinalStatus::kPrimaryMainFrameRendererProcessCrashed:
270 case PrerenderFinalStatus::kPrimaryMainFrameRendererProcessKilled:
271 case PrerenderFinalStatus::kActivationFramePolicyNotCompatible:
272 NOTREACHED_NORETURN();
273 case PrerenderFinalStatus::kPreloadingDisabled:
274 return PreloadingEligibility::kPreloadingDisabled;
275 case PrerenderFinalStatus::kBatterySaverEnabled:
276 return PreloadingEligibility::kBatterySaverEnabled;
277 case PrerenderFinalStatus::kActivatedDuringMainFrameNavigation:
278 NOTREACHED_NORETURN();
279 case PrerenderFinalStatus::kPreloadingUnsupportedByWebContents:
280 return PreloadingEligibility::kPreloadingUnsupportedByWebContents;
281 case PrerenderFinalStatus::kCrossSiteRedirectInMainFrameNavigation:
282 case PrerenderFinalStatus::kCrossSiteNavigationInMainFrameNavigation:
283 case PrerenderFinalStatus::
284 kSameSiteCrossOriginRedirectNotOptInInMainFrameNavigation:
285 case PrerenderFinalStatus::
286 kSameSiteCrossOriginNavigationNotOptInInMainFrameNavigation:
287 NOTREACHED_NORETURN();
288 case PrerenderFinalStatus::kMemoryPressureOnTrigger:
289 return PreloadingEligibility::kMemoryPressure;
290 case PrerenderFinalStatus::kMemoryPressureAfterTriggered:
291 NOTREACHED_NORETURN();
keno8480d579f2023-05-25 11:34:17292 case PrerenderFinalStatus::kPrerenderingDisabledByDevTools:
293 return PreloadingEligibility::kPreloadingDisabledByDevTools;
Hiroki Nakagawa5f7d2db2023-07-12 22:54:06294 case PrerenderFinalStatus::kSpeculationRuleRemoved:
Hiroki Nakagawa2f427342023-07-19 23:27:04295 case PrerenderFinalStatus::kActivatedWithAuxiliaryBrowsingContexts:
Taiyo Mizuhashi459a6b22023-09-13 07:56:48296 case PrerenderFinalStatus::kMaxNumOfRunningEagerPrerendersExceeded:
297 case PrerenderFinalStatus::kMaxNumOfRunningNonEagerPrerendersExceeded:
298 case PrerenderFinalStatus::kMaxNumOfRunningEmbedderPrerendersExceeded:
Hiroki Nakagawa5f7d2db2023-07-12 22:54:06299 NOTREACHED_NORETURN();
Hiroki Nakagawad77864c2023-09-20 22:34:03300 case PrerenderFinalStatus::kPrerenderingUrlHasEffectiveUrl:
301 case PrerenderFinalStatus::kRedirectedPrerenderingUrlHasEffectiveUrl:
302 case PrerenderFinalStatus::kActivationUrlHasEffectiveUrl:
303 return PreloadingEligibility::kHasEffectiveUrl;
keno85004bc2023-05-18 09:45:41304 }
305
306 NOTREACHED_NORETURN();
307}
308
309// Represents a contract and ensures that the given prerender attempt is started
310// as a PrerenderHost or rejected with a reason. It is allowed to use it only in
311// PrerenderHostRegistry::CreateAndStartHost.
312//
313// TODO(kenoss): Add emits of Preload.prerenderStatusUpdated.
314class PrerenderHostBuilder {
315 public:
316 explicit PrerenderHostBuilder(PreloadingAttempt* attempt);
317 ~PrerenderHostBuilder();
318
319 PrerenderHostBuilder(const PrerenderHostBuilder&) = delete;
320 PrerenderHostBuilder& operator=(const PrerenderHostBuilder&) = delete;
321 PrerenderHostBuilder(PrerenderHostBuilder&&) = delete;
322 PrerenderHostBuilder& operator=(PrerenderHostBuilder&&) = delete;
323
keno85004bc2023-05-18 09:45:41324 // The following methods consumes this class.
325 std::unique_ptr<PrerenderHost> Build(const PrerenderAttributes& attributes,
326 WebContentsImpl& prerender_web_contents);
327 void RejectAsNotEligible(const PrerenderAttributes& attributes,
328 PrerenderFinalStatus status);
keno85004bc2023-05-18 09:45:41329 void RejectAsDuplicate();
330 void RejectAsFailure(const PrerenderAttributes& attributes,
331 PrerenderFinalStatus status);
Simon Pelchatefc06192023-06-28 02:04:44332 void RejectDueToHoldback();
333
Domenic Denicolabb5d843c2023-07-18 01:47:33334 void SetHoldbackOverride(PreloadingHoldbackStatus status);
Simon Pelchatefc06192023-06-28 02:04:44335 bool CheckIfShouldHoldback();
336
kenoc9782d92023-05-19 06:28:28337 // Public only for exceptional case.
338 // TODO(https://crbug.com/1435376): Make this private again.
keno85004bc2023-05-18 09:45:41339 void Drop();
kenoa8f95712023-05-19 08:09:22340 bool IsDropped();
keno85004bc2023-05-18 09:45:41341
kenoc9782d92023-05-19 06:28:28342 private:
keno85004bc2023-05-18 09:45:41343 // Use raw pointer as PrerenderHostBuilder is alive only during
344 // PrerenderHostRegistry::CreateAndStartHost(), and PreloadingAttempt should
345 // outlive the function.
346 raw_ptr<PreloadingAttempt> attempt_;
kenod930e48032023-05-18 11:38:40347 std::unique_ptr<DevToolsPrerenderAttempt> devtools_attempt_;
keno85004bc2023-05-18 09:45:41348};
349
350PrerenderHostBuilder::PrerenderHostBuilder(PreloadingAttempt* attempt)
kenod930e48032023-05-18 11:38:40351 : attempt_(attempt),
352 devtools_attempt_(std::make_unique<DevToolsPrerenderAttempt>()) {}
keno85004bc2023-05-18 09:45:41353
354PrerenderHostBuilder::~PrerenderHostBuilder() {
kenoa8f95712023-05-19 08:09:22355 CHECK(IsDropped());
keno85004bc2023-05-18 09:45:41356}
357
358void PrerenderHostBuilder::Drop() {
359 attempt_ = nullptr;
kenod930e48032023-05-18 11:38:40360 devtools_attempt_.reset();
keno85004bc2023-05-18 09:45:41361}
362
kenoa8f95712023-05-19 08:09:22363bool PrerenderHostBuilder::IsDropped() {
364 return devtools_attempt_ == nullptr;
365}
366
keno85004bc2023-05-18 09:45:41367std::unique_ptr<PrerenderHost> PrerenderHostBuilder::Build(
368 const PrerenderAttributes& attributes,
369 WebContentsImpl& prerender_web_contents) {
kenoa8f95712023-05-19 08:09:22370 CHECK(!IsDropped());
371
keno85004bc2023-05-18 09:45:41372 auto prerender_host = std::make_unique<PrerenderHost>(
373 attributes, prerender_web_contents,
kenod930e48032023-05-18 11:38:40374 attempt_ ? attempt_->GetWeakPtr() : nullptr,
375 std::move(devtools_attempt_));
keno85004bc2023-05-18 09:45:41376
377 Drop();
378
379 return prerender_host;
380}
381
382void PrerenderHostBuilder::RejectAsNotEligible(
383 const PrerenderAttributes& attributes,
384 PrerenderFinalStatus status) {
kenoa8f95712023-05-19 08:09:22385 CHECK(!IsDropped());
386
keno85004bc2023-05-18 09:45:41387 if (attempt_) {
388 attempt_->SetEligibility(ToEligibility(status));
389 }
390
kenod930e48032023-05-18 11:38:40391 devtools_attempt_->SetFailureReason(attributes, status);
392
keno85004bc2023-05-18 09:45:41393 RecordFailedPrerenderFinalStatus(PrerenderCancellationReason(status),
394 attributes);
395
396 Drop();
397}
398
Simon Pelchatefc06192023-06-28 02:04:44399bool PrerenderHostBuilder::CheckIfShouldHoldback() {
400 CHECK(!IsDropped());
401
402 // Assigns the holdback status in the attempt it was not overridden earlier.
403 return attempt_ && attempt_->ShouldHoldback();
404}
405
406void PrerenderHostBuilder::RejectDueToHoldback() {
407 CHECK(!IsDropped());
408
409 // If DevTools is opened, holdbacks are force-disabled. So, we don't need to
410 // report this case to DevTools.
411
412 Drop();
413}
414
keno85004bc2023-05-18 09:45:41415void PrerenderHostBuilder::RejectAsDuplicate() {
kenoa8f95712023-05-19 08:09:22416 CHECK(!IsDropped());
417
keno85004bc2023-05-18 09:45:41418 if (attempt_) {
419 attempt_->SetTriggeringOutcome(PreloadingTriggeringOutcome::kDuplicate);
420 }
421
kenod930e48032023-05-18 11:38:40422 // No need to report DevTools nor UMA; just removing duplicates.
423
keno85004bc2023-05-18 09:45:41424 Drop();
425}
426
Domenic Denicolabb5d843c2023-07-18 01:47:33427void PrerenderHostBuilder::SetHoldbackOverride(
428 PreloadingHoldbackStatus status) {
Simon Pelchatefc06192023-06-28 02:04:44429 if (!attempt_) {
430 return;
keno85004bc2023-05-18 09:45:41431 }
Domenic Denicolabb5d843c2023-07-18 01:47:33432 attempt_->SetHoldbackStatus(status);
keno85004bc2023-05-18 09:45:41433}
434
435void PrerenderHostBuilder::RejectAsFailure(
436 const PrerenderAttributes& attributes,
437 PrerenderFinalStatus status) {
kenoa8f95712023-05-19 08:09:22438 CHECK(!IsDropped());
439
keno85004bc2023-05-18 09:45:41440 if (attempt_) {
441 attempt_->SetFailureReason(ToPreloadingFailureReason(status));
442 }
443
kenod930e48032023-05-18 11:38:40444 devtools_attempt_->SetFailureReason(attributes, status);
445
keno85004bc2023-05-18 09:45:41446 RecordFailedPrerenderFinalStatus(PrerenderCancellationReason(status),
447 attributes);
448
449 Drop();
450}
451
Matt Falkenhagen9373d4e2021-08-11 16:50:22452} // namespace
453
Taiyo Mizuhashi748071f2023-08-01 03:46:01454const char kMaxNumOfRunningSpeculationRulesEagerPrerenders[] =
455 "max_num_of_running_speculation_rules_eager_prerenders";
456const char kMaxNumOfRunningSpeculationRulesNonEagerPrerenders[] =
457 "max_num_of_running_speculation_rules_non_eager_prerenders";
458const char kMaxNumOfRunningEmbedderPrerenders[] =
459 "max_num_of_running_embedder_prerenders";
Taiyo Mizuhashi7bf79242023-07-25 06:30:59460
Hiroki Nakagawa34e5d672023-04-21 05:00:09461PrerenderHostRegistry::PrerenderHostRegistry(WebContents& web_contents)
462 : memory_pressure_listener_(
463 FROM_HERE,
464 base::BindRepeating(&PrerenderHostRegistry::OnMemoryPressure,
465 base::Unretained(this))) {
Yoshiki Taniokab6a6d722022-10-20 07:58:50466 Observe(&web_contents);
Lingqi Chi22f3c2d2023-04-27 15:55:50467 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Hiroki Nakagawa968139e22020-10-22 15:03:56468}
469
Matt Falkenhagenf884d0f2021-02-18 06:31:35470PrerenderHostRegistry::~PrerenderHostRegistry() {
lingqi42ab9f452022-12-30 16:56:49471 // This function is called by WebContentsImpl's dtor, so web_contents() should
472 // not be a null ptr at this moment.
Ho Cheung13d432c22023-03-28 08:39:42473 CHECK(web_contents());
Lingqi Chi22f3c2d2023-04-27 15:55:50474 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
lingqi42ab9f452022-12-30 16:56:49475
476 PrerenderFinalStatus final_status =
477 web_contents()->GetClosedByUserGesture()
478 ? PrerenderFinalStatus::kTabClosedByUserGesture
479 : PrerenderFinalStatus::kTabClosedWithoutUserGesture;
480
481 // Here we have to delete the prerender hosts synchronously, to ensure the
482 // FrameTrees would not access the WebContents.
483 CancelAllHosts(final_status);
Yoshiki Taniokab6a6d722022-10-20 07:58:50484 Observe(nullptr);
Matt Falkenhagenf884d0f2021-02-18 06:31:35485 for (Observer& obs : observers_)
486 obs.OnRegistryDestroyed();
487}
488
489void PrerenderHostRegistry::AddObserver(Observer* observer) {
490 observers_.AddObserver(observer);
491}
492
493void PrerenderHostRegistry::RemoveObserver(Observer* observer) {
494 observers_.RemoveObserver(observer);
495}
Hiroki Nakagawa968139e22020-10-22 15:03:56496
Lingqi Chi4e06ae72021-01-28 13:48:35497int PrerenderHostRegistry::CreateAndStartHost(
Lingqi Chica31e242021-09-24 03:54:16498 const PrerenderAttributes& attributes,
Sreeja Kamishettyac12140e2022-07-14 22:16:51499 PreloadingAttempt* attempt) {
Lingqi Chi22f3c2d2023-04-27 15:55:50500 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Robert Linc37fb582021-11-11 03:18:47501 std::string recorded_url =
502 attributes.initiator_origin.has_value()
503 ? attributes.initiator_origin.value().GetURL().spec()
504 : "(empty_url)";
505
Robert Lind75062bf2021-10-28 12:50:55506 TRACE_EVENT2("navigation", "PrerenderHostRegistry::CreateAndStartHost",
Robert Linc37fb582021-11-11 03:18:47507 "attributes", attributes, "initiator_origin", recorded_url);
Hiroki Nakagawa8f6a95d82020-12-04 02:56:38508
Hiroki Nakagawab930733e2023-01-04 16:40:52509 // The initiator WebContents can be different from the WebContents that will
510 // host a prerendered page only when the prerender-in-new-tab runs.
Ho Cheung13d432c22023-03-28 08:39:42511 CHECK(attributes.initiator_web_contents);
Hiroki Nakagawab930733e2023-01-04 16:40:52512 auto& initiator_web_contents =
513 static_cast<WebContentsImpl&>(*attributes.initiator_web_contents);
514 auto& prerender_web_contents = static_cast<WebContentsImpl&>(*web_contents());
Ho Cheung13d432c22023-03-28 08:39:42515 CHECK(&initiator_web_contents == &prerender_web_contents ||
516 base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab));
Hiroki Nakagawab930733e2023-01-04 16:40:52517
David Bokan5e9db7b2021-11-02 12:29:04518 int frame_tree_node_id = RenderFrameHost::kNoFrameTreeNodeId;
Matt Falkenhagenf884d0f2021-02-18 06:31:35519
David Bokan5e9db7b2021-11-02 12:29:04520 {
keno8480d579f2023-05-25 11:34:17521 RenderFrameHostImpl* initiator_rfh =
522 attributes.IsBrowserInitiated()
523 ? nullptr
524 : RenderFrameHostImpl::FromFrameToken(
525 attributes.initiator_process_id,
526 attributes.initiator_frame_token.value());
527
David Bokan5e9db7b2021-11-02 12:29:04528 // Ensure observers are notified that a trigger occurred.
529 base::ScopedClosureRunner notify_trigger(
530 base::BindOnce(&PrerenderHostRegistry::NotifyTrigger,
531 base::Unretained(this), attributes.prerendering_url));
532
keno85004bc2023-05-18 09:45:41533 auto builder = PrerenderHostBuilder(attempt);
534
kenoc9782d92023-05-19 06:28:28535 // We don't know the root cause, but there is a case this is null.
536 //
537 // TODO(https://crbug.com/1435376): Continue investigation and fix the root
538 // cause.
539 if (initiator_web_contents.GetDelegate() == nullptr) {
540 // Note that return without consuming `builder` is exceptional.
541 builder.Drop();
542 return RenderFrameHost::kNoFrameTreeNodeId;
543 }
544
Johanna0b3e9b2023-01-19 23:23:35545 // Check whether preloading is enabled. If it is not enabled, report the
546 // reason.
keno85004bc2023-05-18 09:45:41547 switch (initiator_web_contents.GetDelegate()->IsPrerender2Supported(
548 initiator_web_contents)) {
549 case PreloadingEligibility::kEligible:
550 // nop
551 break;
552 case PreloadingEligibility::kPreloadingDisabled:
553 builder.RejectAsNotEligible(attributes,
554 PrerenderFinalStatus::kPreloadingDisabled);
555 return RenderFrameHost::kNoFrameTreeNodeId;
556 case PreloadingEligibility::kDataSaverEnabled:
557 builder.RejectAsNotEligible(attributes,
558 PrerenderFinalStatus::kDataSaverEnabled);
559 return RenderFrameHost::kNoFrameTreeNodeId;
560 case PreloadingEligibility::kBatterySaverEnabled:
561 builder.RejectAsNotEligible(attributes,
562 PrerenderFinalStatus::kBatterySaverEnabled);
563 return RenderFrameHost::kNoFrameTreeNodeId;
564 case PreloadingEligibility::kPreloadingUnsupportedByWebContents:
565 builder.RejectAsNotEligible(
566 attributes,
567 PrerenderFinalStatus::kPreloadingUnsupportedByWebContents);
568 return RenderFrameHost::kNoFrameTreeNodeId;
569 default:
570 NOTREACHED_NORETURN();
Sreeja Kamishettyc94a0d22022-07-06 20:43:06571 }
572
Taiyo Mizuhashid82e4332023-05-09 11:25:16573 // Don't prerender when the initiator is in the background and its type is
574 // `kEmbedder`, as current implementation doesn't use `pending_prerenders_`
575 // when kEmbedder.
576 // If the trigger type is speculation rules, nothing should be done here and
577 // then prerender host will be created and its id will be enqueued to
578 // `pending_prerenders_`. The visibility of the initiator will be considered
579 // when trying to pop from `pending_prerenders_` on `StartPrerendering()`.
Kouhei Ueno3f37992b2023-11-09 23:29:02580 if (attributes.trigger_type == PreloadingTriggerType::kEmbedder &&
Taiyo Mizuhashid82e4332023-05-09 11:25:16581 initiator_web_contents.GetVisibility() == Visibility::HIDDEN) {
keno85004bc2023-05-18 09:45:41582 builder.RejectAsNotEligible(attributes,
583 PrerenderFinalStatus::kTriggerBackgrounded);
David Bokan5e9db7b2021-11-02 12:29:04584 return RenderFrameHost::kNoFrameTreeNodeId;
585 }
586
587 // Don't prerender on low-end devices.
David Bokan5e9db7b2021-11-02 12:29:04588 if (!DeviceHasEnoughMemoryForPrerender()) {
keno85004bc2023-05-18 09:45:41589 builder.RejectAsNotEligible(attributes,
590 PrerenderFinalStatus::kLowEndDevice);
David Bokan5e9db7b2021-11-02 12:29:04591 return RenderFrameHost::kNoFrameTreeNodeId;
592 }
593
Hiroki Nakagawa34e5d672023-04-21 05:00:09594 // Don't prerender under critical memory pressure.
595 switch (GetCurrentMemoryPressureLevel()) {
596 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
597 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
598 break;
599 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
keno85004bc2023-05-18 09:45:41600 builder.RejectAsNotEligible(
601 attributes, PrerenderFinalStatus::kMemoryPressureOnTrigger);
Hiroki Nakagawa34e5d672023-04-21 05:00:09602 return RenderFrameHost::kNoFrameTreeNodeId;
603 }
604
Hiroki Nakagawa1500e7a2023-01-31 07:44:55605 // Allow prerendering only for same-site. The initiator origin is nullopt
606 // when prerendering is initiated by the browser (not by a renderer using
607 // Speculation Rules API). In that case, skip this same-site check.
Robert Lin20d947ed2022-08-25 06:01:28608 // TODO(crbug.com/1176054): Support cross-site prerendering.
Hiroki Nakagawa1500e7a2023-01-31 07:44:55609 if (!attributes.IsBrowserInitiated() &&
610 !prerender_navigation_utils::IsSameSite(
611 attributes.prerendering_url, attributes.initiator_origin.value())) {
keno85004bc2023-05-18 09:45:41612 builder.RejectAsNotEligible(
613 attributes,
614 PrerenderFinalStatus::kCrossSiteNavigationInInitialNavigation);
Hiroki Nakagawa1500e7a2023-01-31 07:44:55615 return RenderFrameHost::kNoFrameTreeNodeId;
David Bokan5e9db7b2021-11-02 12:29:04616 }
617
Hiroki Nakagawaef90206c2023-08-25 05:03:27618 // Allow prerendering only HTTP(S) scheme URLs. For redirection, this will
619 // be checked in PrerenderNavigationThrottle::WillStartOrRedirectRequest().
620 if (!attributes.prerendering_url.SchemeIsHTTPOrHTTPS()) {
621 builder.RejectAsNotEligible(
622 attributes, PrerenderFinalStatus::kInvalidSchemeNavigation);
623 return RenderFrameHost::kNoFrameTreeNodeId;
624 }
625
Hiroki Nakagawab930733e2023-01-04 16:40:52626 // Disallow all pages that have an effective URL like hosted apps and NTP.
Hiroki Nakagawad77864c2023-09-20 22:34:03627 auto* browser_context = prerender_web_contents.GetBrowserContext();
628 if (SiteInstanceImpl::HasEffectiveURL(browser_context,
629 initiator_web_contents.GetURL())) {
630 builder.RejectAsNotEligible(
631 attributes, PrerenderFinalStatus::kTriggerUrlHasEffectiveUrl);
632 return RenderFrameHost::kNoFrameTreeNodeId;
633 }
634 if (SiteInstanceImpl::HasEffectiveURL(browser_context,
635 attributes.prerendering_url)) {
636 builder.RejectAsNotEligible(
637 attributes, PrerenderFinalStatus::kPrerenderingUrlHasEffectiveUrl);
Miyoung Shin551df1cb2022-08-25 14:14:58638 return RenderFrameHost::kNoFrameTreeNodeId;
639 }
640
keno8480d579f2023-05-25 11:34:17641 if (initiator_rfh && initiator_rfh->frame_tree() &&
642 !devtools_instrumentation::IsPrerenderAllowed(
643 *initiator_rfh->frame_tree())) {
644 builder.RejectAsNotEligible(
645 attributes, PrerenderFinalStatus::kPrerenderingDisabledByDevTools);
646 return RenderFrameHost::kNoFrameTreeNodeId;
647 }
648
Sreeja Kamishettyac12140e2022-07-14 22:16:51649 // Once all eligibility checks are completed, set the status to kEligible.
650 if (attempt)
651 attempt->SetEligibility(PreloadingEligibility::kEligible);
652
Domenic Denicolabb5d843c2023-07-18 01:47:33653 // Normally CheckIfShouldHoldback() computes the holdback status based on
654 // PreloadingConfig. In special cases, we call SetHoldbackOverride() to
655 // override that processing.
656 bool has_devtools_open =
Robert Lin56cb9c42022-11-22 07:57:30657 initiator_rfh &&
658 RenderFrameDevToolsAgentHost::GetFor(initiator_rfh) != nullptr;
Domenic Denicolabb5d843c2023-07-18 01:47:33659
660 if (has_devtools_open) {
661 // Never holdback when DevTools is opened, to avoid web developer
662 // frustration.
663 builder.SetHoldbackOverride(PreloadingHoldbackStatus::kAllowed);
Domenic Denicolabb5d843c2023-07-18 01:47:33664 } else if (attributes.holdback_status_override !=
665 PreloadingHoldbackStatus::kUnspecified) {
666 // The caller (e.g. from chrome/) is allowed to specify a holdback that
667 // overrides the default logic.
668 builder.SetHoldbackOverride(attributes.holdback_status_override);
Simon Pelchatefc06192023-06-28 02:04:44669 }
Domenic Denicolabb5d843c2023-07-18 01:47:33670
Simon Pelchatefc06192023-06-28 02:04:44671 // Check if the attempt is held back either due to the check above or via
672 // PreloadingConfig.
673 if (builder.CheckIfShouldHoldback()) {
keno85004bc2023-05-18 09:45:41674 builder.RejectDueToHoldback();
Sreeja Kamishettyac12140e2022-07-14 22:16:51675 return RenderFrameHost::kNoFrameTreeNodeId;
676 }
Sreeja Kamishettyac12140e2022-07-14 22:16:51677
David Bokan5e9db7b2021-11-02 12:29:04678 // Ignore prerendering requests for the same URL.
679 for (auto& iter : prerender_host_by_frame_tree_node_id_) {
Sreeja Kamishettyac12140e2022-07-14 22:16:51680 if (iter.second->GetInitialUrl() == attributes.prerendering_url) {
keno85004bc2023-05-18 09:45:41681 builder.RejectAsDuplicate();
Hiroki Nakagawad9f772d2021-11-11 07:50:26682 return RenderFrameHost::kNoFrameTreeNodeId;
Sreeja Kamishettyac12140e2022-07-14 22:16:51683 }
David Bokan5e9db7b2021-11-02 12:29:04684 }
685
Taiyo Mizuhashi3a4c3d42023-11-09 10:27:14686 // Under kPrerender2InNewTab, CreateAndStartHost will be called in
687 // the newly created WebContents’s PrerenderHostRegistry for new tab
688 // triggers, rather than in initiator WebContents’s registry, while
689 // it is called in initiator ones for normal triggers. In either
690 // case, we want to control the limit based on the initiator
691 // WebContents.
692 //
Yoshiki Tanioka58b8fefa2022-09-16 02:05:10693 // TODO(crbug.com/1355151): Enqueue the request exceeding the number limit
694 // until the forerunners are cancelled, and suspend starting a new prerender
695 // when the number reaches the limit.
Taiyo Mizuhashi3a4c3d42023-11-09 10:27:14696 if (!initiator_web_contents.GetPrerenderHostRegistry()
697 ->IsAllowedToStartPrerenderingForTrigger(attributes.trigger_type,
698 attributes.eagerness)) {
keno85004bc2023-05-18 09:45:41699 // The reason we don't consider limit exceeded as an ineligibility
700 // reason is because we can't replicate the behavior in our other
701 // experiment groups for analysis. To prevent this we set
702 // TriggeringOutcome to kFailure and look into the failure reason to
703 // learn more.
Taiyo Mizuhashi459a6b22023-09-13 07:56:48704 PrerenderFinalStatus final_status;
705 switch (GetPrerenderLimitGroup(attributes.trigger_type,
706 attributes.eagerness)) {
707 case PrerenderLimitGroup::kSpeculationRulesEager:
708 final_status =
709 PrerenderFinalStatus::kMaxNumOfRunningEagerPrerendersExceeded;
710 break;
711 case PrerenderLimitGroup::kSpeculationRulesNonEager:
712 final_status =
713 PrerenderFinalStatus::kMaxNumOfRunningNonEagerPrerendersExceeded;
714 break;
715 case PrerenderLimitGroup::kEmbedder:
716 final_status =
717 PrerenderFinalStatus::kMaxNumOfRunningEmbedderPrerendersExceeded;
718 break;
719 }
720 builder.RejectAsFailure(attributes, final_status);
David Bokan5e9db7b2021-11-02 12:29:04721 return RenderFrameHost::kNoFrameTreeNodeId;
722 }
723
keno85004bc2023-05-18 09:45:41724 auto prerender_host = builder.Build(attributes, prerender_web_contents);
David Bokan5e9db7b2021-11-02 12:29:04725 frame_tree_node_id = prerender_host->frame_tree_node_id();
726
727 CHECK(!base::Contains(prerender_host_by_frame_tree_node_id_,
728 frame_tree_node_id));
Yoshiki Tanioka07915d82022-09-15 06:07:51729 prerender_host_by_frame_tree_node_id_[frame_tree_node_id] =
730 std::move(prerender_host);
Taiyo Mizuhashicf7fd302023-08-29 04:38:09731
Taiyo Mizuhashi92119792023-09-13 07:18:03732 if (base::FeatureList::IsEnabled(
733 features::kPrerender2NewLimitAndScheduler)) {
Taiyo Mizuhashicf7fd302023-08-29 04:38:09734 if (GetPrerenderLimitGroup(attributes.trigger_type,
735 attributes.eagerness) ==
736 PrerenderLimitGroup::kSpeculationRulesNonEager) {
737 non_eager_prerender_host_id_by_arrival_order_.push_back(
738 frame_tree_node_id);
739 }
740 }
Yoshiki Taniokac72b7b722022-09-06 05:38:38741 }
742
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03743 switch (attributes.trigger_type) {
Kouhei Ueno3f37992b2023-11-09 23:29:02744 case PreloadingTriggerType::kSpeculationRule:
745 case PreloadingTriggerType::kSpeculationRuleFromIsolatedWorld:
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03746 pending_prerenders_.push_back(frame_tree_node_id);
747 // Start the initial prerendering navigation of the pending request in
Taiyo Mizuhashid82e4332023-05-09 11:25:16748 // the head of the queue if there's no running prerender and the initiator
749 // is in the foreground.
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03750 if (running_prerender_host_id_ == RenderFrameHost::kNoFrameTreeNodeId) {
Taiyo Mizuhashid82e4332023-05-09 11:25:16751 // No running prerender means that either no other prerenders are in the
752 // pending queue or the initiator continues to be in the background.
753 // Skip starting prerendering in the latter case.
754 if (IsBackground(initiator_web_contents.GetVisibility())) {
755 break;
756 }
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03757 CHECK_EQ(pending_prerenders_.size(), 1u);
758 int started_frame_tree_node_id =
759 StartPrerendering(RenderFrameHost::kNoFrameTreeNodeId);
760 CHECK(started_frame_tree_node_id == frame_tree_node_id ||
761 started_frame_tree_node_id ==
762 RenderFrameHost::kNoFrameTreeNodeId);
763 frame_tree_node_id = started_frame_tree_node_id;
764 }
765 break;
Kouhei Ueno3f37992b2023-11-09 23:29:02766 case PreloadingTriggerType::kEmbedder:
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03767 // The prerendering request from embedder should have high-priority
768 // because embedder prediction is more likely for the user to visit.
769 // Hold the return value of `StartPrerendering` because the requested
770 // prerender might be cancelled due to some restrictions and
771 // `kNoFrameTreeNodeId` should be returned in that case.
772 frame_tree_node_id = StartPrerendering(frame_tree_node_id);
773 break;
Yoshiki Tanioka87aab3f2022-09-13 06:25:37774 }
775
776 return frame_tree_node_id;
777}
778
Hiroki Nakagawa3e2527042022-11-24 06:46:42779int PrerenderHostRegistry::CreateAndStartHostForNewTab(
Hiroki Nakagawa4c4e62b2023-09-12 08:54:29780 const PrerenderAttributes& attributes,
781 PreloadingPredictor preloading_predictor) {
Ho Cheung13d432c22023-03-28 08:39:42782 CHECK(base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab));
Takashi Toyoshima43dcf4312023-04-21 09:33:05783 CHECK(IsSpeculationRuleType(attributes.trigger_type));
Hiroki Nakagawa3e2527042022-11-24 06:46:42784 std::string recorded_url =
785 attributes.initiator_origin.has_value()
786 ? attributes.initiator_origin.value().GetURL().spec()
787 : "(empty_url)";
788 TRACE_EVENT2("navigation",
789 "PrerenderHostRegistry::CreateAndStartHostForNewTab",
790 "attributes", attributes, "initiator_origin", recorded_url);
791
792 auto handle = std::make_unique<PrerenderNewTabHandle>(
Hiroki Nakagawaa000bb82022-11-29 06:01:02793 attributes, *web_contents()->GetBrowserContext());
Hiroki Nakagawa4c4e62b2023-09-12 08:54:29794 int prerender_host_id = handle->StartPrerendering(preloading_predictor);
Hiroki Nakagawa3e2527042022-11-24 06:46:42795 if (prerender_host_id == RenderFrameHost::kNoFrameTreeNodeId)
796 return RenderFrameHost::kNoFrameTreeNodeId;
797 prerender_new_tab_handle_by_frame_tree_node_id_[prerender_host_id] =
798 std::move(handle);
Taiyo Mizuhashi3a4c3d42023-11-09 10:27:14799
800 if (base::FeatureList::IsEnabled(features::kPrerender2NewLimitAndScheduler)) {
801 if (GetPrerenderLimitGroup(attributes.trigger_type, attributes.eagerness) ==
802 PrerenderLimitGroup::kSpeculationRulesNonEager) {
803 non_eager_prerender_host_id_by_arrival_order_.push_back(
804 prerender_host_id);
805 }
806 }
Hiroki Nakagawa3e2527042022-11-24 06:46:42807 return prerender_host_id;
808}
809
Yoshiki Tanioka87aab3f2022-09-13 06:25:37810int PrerenderHostRegistry::StartPrerendering(int frame_tree_node_id) {
Hiroki Nakagawa34e5d672023-04-21 05:00:09811 // TODO(https://crbug.com/1424425): Don't start prerendering if the current
812 // memory pressure level is critical, and then retry prerendering when the
813 // memory pressure level goes down.
814
Yoshiki Tanioka87aab3f2022-09-13 06:25:37815 if (frame_tree_node_id == RenderFrameHost::kNoFrameTreeNodeId) {
Ho Cheung13d432c22023-03-28 08:39:42816 CHECK_EQ(running_prerender_host_id_, RenderFrameHost::kNoFrameTreeNodeId);
Yoshiki Tanioka07915d82022-09-15 06:07:51817
Taiyo Mizuhashi8fe56d062023-08-28 15:13:43818 while (!pending_prerenders_.empty()) {
819 int host_id = pending_prerenders_.front();
Yoshiki Taniokab6a6d722022-10-20 07:58:50820
Hiroki Nakagawab930733e2023-01-04 16:40:52821 // Skip a cancelled request.
822 auto found = prerender_host_by_frame_tree_node_id_.find(host_id);
823 if (found == prerender_host_by_frame_tree_node_id_.end()) {
824 // Remove the cancelled request from the pending queue.
Taiyo Mizuhashi8fe56d062023-08-28 15:13:43825 pending_prerenders_.pop_front();
Hiroki Nakagawab930733e2023-01-04 16:40:52826 continue;
Yoshiki Tanioka07915d82022-09-15 06:07:51827 }
Hiroki Nakagawab930733e2023-01-04 16:40:52828 PrerenderHost* prerender_host = found->second.get();
829
830 // The initiator WebContents should be alive as it cancels all the
831 // prerendering requests during destruction.
Ho Cheung13d432c22023-03-28 08:39:42832 CHECK(prerender_host->initiator_web_contents());
Hiroki Nakagawab930733e2023-01-04 16:40:52833
834 // Don't start the pending prerender triggered by the background tab.
Hiroki Nakagawa98af8c42023-03-14 05:36:38835 if (IsBackground(
836 prerender_host->initiator_web_contents()->GetVisibility())) {
Hiroki Nakagawab930733e2023-01-04 16:40:52837 return RenderFrameHost::kNoFrameTreeNodeId;
838 }
839
840 // Found the request to run.
Taiyo Mizuhashi8fe56d062023-08-28 15:13:43841 pending_prerenders_.pop_front();
Hiroki Nakagawab930733e2023-01-04 16:40:52842 frame_tree_node_id = host_id;
843 break;
Yoshiki Tanioka87aab3f2022-09-13 06:25:37844 }
845
Yoshiki Tanioka07915d82022-09-15 06:07:51846 if (frame_tree_node_id == RenderFrameHost::kNoFrameTreeNodeId) {
847 return RenderFrameHost::kNoFrameTreeNodeId;
848 }
Yoshiki Tanioka87aab3f2022-09-13 06:25:37849 }
850
Andrey Kosyakov6e82c3b2022-09-26 22:43:46851 auto prerender_host_it =
852 prerender_host_by_frame_tree_node_id_.find(frame_tree_node_id);
Ho Cheung13d432c22023-03-28 08:39:42853 CHECK(prerender_host_it != prerender_host_by_frame_tree_node_id_.end());
Andrey Kosyakov6e82c3b2022-09-26 22:43:46854 PrerenderHost& prerender_host = *prerender_host_it->second;
855 devtools_instrumentation::WillInitiatePrerender(
856 prerender_host.GetPrerenderFrameTree());
857 if (!prerender_host.StartPrerendering()) {
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31858 CancelHost(frame_tree_node_id, PrerenderFinalStatus::kStartFailed);
Joel Hockeye7e03ca72022-09-08 07:35:24859 return RenderFrameHost::kNoFrameTreeNodeId;
Harkiran Bolariaba823e42021-05-21 18:30:36860 }
Lingqi Chi0a5636f2021-01-26 07:47:25861
Yoshiki Tanioka87aab3f2022-09-13 06:25:37862 switch (prerender_host_by_frame_tree_node_id_[frame_tree_node_id]
863 ->trigger_type()) {
Kouhei Ueno3f37992b2023-11-09 23:29:02864 case PreloadingTriggerType::kSpeculationRule:
865 case PreloadingTriggerType::kSpeculationRuleFromIsolatedWorld:
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03866 // Check the current memory usage and destroy a prerendering if the entire
867 // browser uses excessive memory. This occurs asynchronously.
Asami Doi3b6840212022-07-28 02:39:19868 DestroyWhenUsingExcessiveMemory(frame_tree_node_id);
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03869
870 // Update the `running_prerender_host_id` to the starting prerender's id.
871 running_prerender_host_id_ = frame_tree_node_id;
Asami Doi3b6840212022-07-28 02:39:19872 break;
Kouhei Ueno3f37992b2023-11-09 23:29:02873 case PreloadingTriggerType::kEmbedder:
Asami Doi3b6840212022-07-28 02:39:19874 // We don't check the memory usage for embedder triggered prerenderings
875 // for now.
Asami Doi3b6840212022-07-28 02:39:19876
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03877 // `running_prerender_host_id` only tracks the id for speculation rules
878 // trigger, so we also don't update it in the case of embedder.
879 break;
Yoshiki Taniokaac78c70f2022-09-20 01:28:29880 }
881
Yoshiki Tanioka87aab3f2022-09-13 06:25:37882 RecordPrerenderTriggered(
883 prerender_host_by_frame_tree_node_id_[frame_tree_node_id]
884 ->initiator_ukm_id());
Joel Hockeye7e03ca72022-09-08 07:35:24885 return frame_tree_node_id;
Hiroki Nakagawa968139e22020-10-22 15:03:56886}
887
Hiroki Nakagawa640dd712022-12-08 04:05:08888std::set<int> PrerenderHostRegistry::CancelHosts(
Yoshiki Tanioka12e60062022-10-05 08:13:55889 const std::vector<int>& frame_tree_node_ids,
Hiroki Nakagawa640dd712022-12-08 04:05:08890 const PrerenderCancellationReason& reason) {
Yoshiki Tanioka12e60062022-10-05 08:13:55891 TRACE_EVENT1("navigation", "PrerenderHostRegistry::CancelHosts",
892 "frame_tree_node_ids", frame_tree_node_ids);
893
Yoshiki Tanioka75decd92022-10-26 12:23:06894 // Cancel must not be requested during activation.
895 CHECK(!reserved_prerender_host_);
Yoshiki Tanioka12e60062022-10-05 08:13:55896
Hiroki Nakagawa640dd712022-12-08 04:05:08897 std::set<int> cancelled_ids;
898
Yoshiki Tanioka75decd92022-10-26 12:23:06899 for (int host_id : frame_tree_node_ids) {
Yoshiki Tanioka12e60062022-10-05 08:13:55900 // Look up the id in the non-reserved host map.
Hiroki Nakagawa3e2527042022-11-24 06:46:42901 if (auto iter = prerender_host_by_frame_tree_node_id_.find(host_id);
902 iter != prerender_host_by_frame_tree_node_id_.end()) {
903 if (running_prerender_host_id_ == host_id)
904 running_prerender_host_id_ = RenderFrameHost::kNoFrameTreeNodeId;
Yoshiki Tanioka12e60062022-10-05 08:13:55905
Hiroki Nakagawa3e2527042022-11-24 06:46:42906 // Remove the prerender host from the host map so that it's not used for
907 // activation during asynchronous deletion.
908 std::unique_ptr<PrerenderHost> prerender_host = std::move(iter->second);
909 prerender_host_by_frame_tree_node_id_.erase(iter);
Yoshiki Tanioka12e60062022-10-05 08:13:55910
Hiroki Nakagawa640dd712022-12-08 04:05:08911 reason.ReportMetrics(prerender_host->trigger_type(),
912 prerender_host->embedder_histogram_suffix());
913
Taiyo Mizuhashi5694b822023-09-26 16:10:30914 NotifyCancel(prerender_host->frame_tree_node_id(), reason);
Taiyo Mizuhashi295ccbdf2023-09-13 09:17:02915
Hiroki Nakagawa3e2527042022-11-24 06:46:42916 // Asynchronously delete the prerender host.
Hiroki Nakagawa640dd712022-12-08 04:05:08917 ScheduleToDeleteAbandonedHost(std::move(prerender_host), reason);
918 cancelled_ids.insert(host_id);
Hiroki Nakagawa3e2527042022-11-24 06:46:42919 }
Yoshiki Tanioka12e60062022-10-05 08:13:55920
Hiroki Nakagawa3e2527042022-11-24 06:46:42921 if (base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)) {
922 // Look up the id in the prerender-in-new-tab handle map.
923 if (auto iter =
924 prerender_new_tab_handle_by_frame_tree_node_id_.find(host_id);
925 iter != prerender_new_tab_handle_by_frame_tree_node_id_.end()) {
926 // The host should be driven by PrerenderHostRegistry associated with
927 // the new tab.
Ho Cheung13d432c22023-03-28 08:39:42928 CHECK_NE(running_prerender_host_id_, host_id);
Hiroki Nakagawa3e2527042022-11-24 06:46:42929
930 std::unique_ptr<PrerenderNewTabHandle> handle = std::move(iter->second);
931 prerender_new_tab_handle_by_frame_tree_node_id_.erase(iter);
Taiyo Mizuhashi1ef962b12023-10-31 08:10:10932 NotifyCancel(host_id, reason);
Hiroki Nakagawa640dd712022-12-08 04:05:08933 handle->CancelPrerendering(reason);
934 cancelled_ids.insert(host_id);
Hiroki Nakagawa3e2527042022-11-24 06:46:42935 }
936 } else {
Ho Cheung13d432c22023-03-28 08:39:42937 CHECK(prerender_new_tab_handle_by_frame_tree_node_id_.empty());
Hiroki Nakagawa3e2527042022-11-24 06:46:42938 }
Yoshiki Tanioka12e60062022-10-05 08:13:55939 }
940
941 // Start another prerender if the running prerender is cancelled.
Taiyo Mizuhashi31bcb1992023-04-27 05:03:03942 if (running_prerender_host_id_ == RenderFrameHost::kNoFrameTreeNodeId) {
Yoshiki Tanioka12e60062022-10-05 08:13:55943 StartPrerendering(RenderFrameHost::kNoFrameTreeNodeId);
944 }
Hiroki Nakagawa640dd712022-12-08 04:05:08945
946 return cancelled_ids;
Yoshiki Tanioka12e60062022-10-05 08:13:55947}
948
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31949bool PrerenderHostRegistry::CancelHost(int frame_tree_node_id,
950 PrerenderFinalStatus final_status) {
Lingqi Chi8e192ba2022-11-22 04:34:25951 return CancelHost(frame_tree_node_id,
952 PrerenderCancellationReason(final_status));
953}
954
955bool PrerenderHostRegistry::CancelHost(
956 int frame_tree_node_id,
957 const PrerenderCancellationReason& reason) {
Hiroki Nakagawaec54daf2021-06-08 06:12:31958 TRACE_EVENT1("navigation", "PrerenderHostRegistry::CancelHost",
Hiroki Nakagawa9ba66692021-03-09 07:26:55959 "frame_tree_node_id", frame_tree_node_id);
Hiroki Nakagawa640dd712022-12-08 04:05:08960 std::set<int> cancelled_ids = CancelHosts({frame_tree_node_id}, reason);
961 return !cancelled_ids.empty();
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:36962}
Hiroki Nakagawaa1778302021-06-29 08:44:37963
Takashi Toyoshima43dcf4312023-04-21 09:33:05964void PrerenderHostRegistry::CancelHostsForTriggers(
Kouhei Ueno3f37992b2023-11-09 23:29:02965 std::vector<PreloadingTriggerType> trigger_types,
Hiroki Nakagawa640dd712022-12-08 04:05:08966 const PrerenderCancellationReason& reason) {
Asami Doi714d4342022-12-07 08:28:05967 TRACE_EVENT1("navigation", "PrerenderHostRegistry::CancelHostsForTrigger",
Takashi Toyoshima43dcf4312023-04-21 09:33:05968 "trigger_type", trigger_types[0]);
Asami Doi714d4342022-12-07 08:28:05969
970 std::vector<int> ids_to_be_deleted;
971
972 for (auto& iter : prerender_host_by_frame_tree_node_id_) {
Takashi Toyoshima43dcf4312023-04-21 09:33:05973 if (base::Contains(trigger_types, iter.second->trigger_type())) {
Asami Doi714d4342022-12-07 08:28:05974 ids_to_be_deleted.push_back(iter.first);
975 }
976 }
Asami Doi714d4342022-12-07 08:28:05977 if (base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)) {
Takashi Toyoshima43dcf4312023-04-21 09:33:05978 for (auto& iter : prerender_new_tab_handle_by_frame_tree_node_id_) {
979 if (base::Contains(trigger_types, iter.second->trigger_type())) {
980 // Prerendering into a new tab can be triggered by speculation rules
981 // only.
982 CHECK(IsSpeculationRuleType(iter.second->trigger_type()));
983 ids_to_be_deleted.push_back(iter.first);
984 }
Asami Doi714d4342022-12-07 08:28:05985 }
986 } else {
Ho Cheung13d432c22023-03-28 08:39:42987 CHECK(prerender_new_tab_handle_by_frame_tree_node_id_.empty());
Asami Doi714d4342022-12-07 08:28:05988 }
989
Hiroki Nakagawa640dd712022-12-08 04:05:08990 CancelHosts(ids_to_be_deleted, reason);
Asami Doi714d4342022-12-07 08:28:05991}
992
Yoshiki Tanioka49b4cfb2022-10-20 09:25:31993void PrerenderHostRegistry::CancelAllHosts(PrerenderFinalStatus final_status) {
Yoshiki Tanioka75decd92022-10-26 12:23:06994 // Cancel must not be requested during activation.
995 CHECK(!reserved_prerender_host_);
Takashi Toyoshimadc3f7472021-07-21 08:02:32996
Hiroki Nakagawa640dd712022-12-08 04:05:08997 PrerenderCancellationReason reason(final_status);
998
Takashi Toyoshimadc3f7472021-07-21 08:02:32999 auto prerender_host_map = std::move(prerender_host_by_frame_tree_node_id_);
1000 for (auto& iter : prerender_host_map) {
1001 std::unique_ptr<PrerenderHost> prerender_host = std::move(iter.second);
Hiroki Nakagawa640dd712022-12-08 04:05:081002 ScheduleToDeleteAbandonedHost(std::move(prerender_host), reason);
Takashi Toyoshimadc3f7472021-07-21 08:02:321003 }
Yoshiki Tanioka87aab3f2022-09-13 06:25:371004
Hiroki Nakagawa640dd712022-12-08 04:05:081005 if (base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)) {
1006 auto prerender_new_tab_handle_map =
1007 std::move(prerender_new_tab_handle_by_frame_tree_node_id_);
1008 for (auto& iter : prerender_new_tab_handle_map)
1009 iter.second->CancelPrerendering(reason);
1010 } else {
Ho Cheung13d432c22023-03-28 08:39:421011 CHECK(prerender_new_tab_handle_by_frame_tree_node_id_.empty());
Hiroki Nakagawa640dd712022-12-08 04:05:081012 }
Hiroki Nakagawa3e2527042022-11-24 06:46:421013
Yoshiki Tanioka07915d82022-09-15 06:07:511014 pending_prerenders_.clear();
Takashi Toyoshimadc3f7472021-07-21 08:02:321015}
1016
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361017int PrerenderHostRegistry::FindPotentialHostToActivate(
1018 NavigationRequest& navigation_request) {
1019 TRACE_EVENT2(
1020 "navigation", "PrerenderHostRegistry::FindPotentialHostToActivate",
1021 "navigation_url", navigation_request.GetURL().spec(), "render_frame_host",
1022 navigation_request.frame_tree_node()->current_frame_host());
1023 return FindHostToActivateInternal(navigation_request);
Hiroki Nakagawa968139e22020-10-22 15:03:561024}
1025
Hiroki Nakagawa0064a9b2021-02-15 11:42:091026int PrerenderHostRegistry::ReserveHostToActivate(
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361027 NavigationRequest& navigation_request,
1028 int expected_host_id) {
Harkiran Bolariaba823e42021-05-21 18:30:361029 RenderFrameHostImpl* render_frame_host =
1030 navigation_request.frame_tree_node()->current_frame_host();
Hiroki Nakagawa0064a9b2021-02-15 11:42:091031 TRACE_EVENT2("navigation", "PrerenderHostRegistry::ReserveHostToActivate",
Harkiran Bolariaba823e42021-05-21 18:30:361032 "navigation_url", navigation_request.GetURL().spec(),
1033 "render_frame_host", render_frame_host);
Lingqi Chi22f3c2d2023-04-27 15:55:501034 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Hiroki Nakagawaeec217f2020-12-09 08:41:131035
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361036 // Find an available host for the navigation request.
1037 int host_id = FindHostToActivateInternal(navigation_request);
1038 if (host_id == RenderFrameHost::kNoFrameTreeNodeId)
Hiroki Nakagawa0064a9b2021-02-15 11:42:091039 return RenderFrameHost::kNoFrameTreeNodeId;
Hiroki Nakagawa040a2142021-01-13 16:43:511040
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361041 // Check if the host is what the NavigationRequest expects. The host can be
1042 // different when a trigger page removes the existing prerender and then
1043 // re-adds a new prerender for the same URL.
1044 //
1045 // NavigationRequest makes sure that the prerender is ready for activation by
1046 // waiting for PrerenderCommitDeferringCondition before this point. Without
1047 // this check, if the prerender is changed during the period,
1048 // NavigationRequest may attempt to activate the new prerender that is not
1049 // ready.
1050 if (host_id != expected_host_id)
Hiroki Nakagawa0064a9b2021-02-15 11:42:091051 return RenderFrameHost::kNoFrameTreeNodeId;
Hiroki Nakagawaeec217f2020-12-09 08:41:131052
Asami Doi39651732023-01-31 05:25:261053 // Disallow activation when ongoing navigations exist. It can happen when the
1054 // main frame navigation starts after PrerenderCommitDeferringCondition posts
1055 // a task to resume activation and before the activation is completed.
1056 auto& prerender_frame_tree = prerender_host_by_frame_tree_node_id_[host_id]
1057 .get()
1058 ->GetPrerenderFrameTree();
1059 if (prerender_frame_tree.root()->HasNavigation()) {
1060 CancelHost(host_id,
1061 PrerenderFinalStatus::kActivatedDuringMainFrameNavigation);
1062 return RenderFrameHost::kNoFrameTreeNodeId;
1063 }
1064
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361065 // Remove the host from the map of non-reserved hosts.
1066 std::unique_ptr<PrerenderHost> host =
1067 std::move(prerender_host_by_frame_tree_node_id_[host_id]);
1068 prerender_host_by_frame_tree_node_id_.erase(host_id);
Ho Cheung13d432c22023-03-28 08:39:421069 CHECK_EQ(host_id, host->frame_tree_node_id());
Harkiran Bolariaba823e42021-05-21 18:30:361070
Hiroki Nakagawa0064a9b2021-02-15 11:42:091071 // Reserve the host for activation.
Ho Cheung13d432c22023-03-28 08:39:421072 CHECK(!reserved_prerender_host_);
Yoshiki Tanioka75decd92022-10-26 12:23:061073 reserved_prerender_host_ = std::move(host);
Hiroki Nakagawa0064a9b2021-02-15 11:42:091074
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361075 return host_id;
Hiroki Nakagawa0064a9b2021-02-15 11:42:091076}
1077
Harkiran Bolaria59290d62021-03-17 01:53:011078RenderFrameHostImpl* PrerenderHostRegistry::GetRenderFrameHostForReservedHost(
1079 int frame_tree_node_id) {
Yoshiki Tanioka75decd92022-10-26 12:23:061080 if (!reserved_prerender_host_)
Harkiran Bolaria59290d62021-03-17 01:53:011081 return nullptr;
Yoshiki Tanioka75decd92022-10-26 12:23:061082
Ho Cheung13d432c22023-03-28 08:39:421083 CHECK_EQ(frame_tree_node_id, reserved_prerender_host_->frame_tree_node_id());
Yoshiki Tanioka75decd92022-10-26 12:23:061084
1085 return reserved_prerender_host_->GetPrerenderedMainFrameHost();
Harkiran Bolaria59290d62021-03-17 01:53:011086}
1087
Yuzu Saijo68390b992021-07-27 06:17:201088std::unique_ptr<StoredPage> PrerenderHostRegistry::ActivateReservedHost(
Harkiran Bolaria59290d62021-03-17 01:53:011089 int frame_tree_node_id,
Harkiran Bolaria59290d62021-03-17 01:53:011090 NavigationRequest& navigation_request) {
Lingqi Chi22f3c2d2023-04-27 15:55:501091 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Yoshiki Tanioka75decd92022-10-26 12:23:061092 CHECK(reserved_prerender_host_);
1093 CHECK_EQ(frame_tree_node_id, reserved_prerender_host_->frame_tree_node_id());
1094
1095 std::unique_ptr<PrerenderHost> prerender_host =
1096 std::move(reserved_prerender_host_);
Hiroki Nakagawadf55bac2021-04-26 06:56:571097 return prerender_host->Activate(navigation_request);
Hiroki Nakagawa0064a9b2021-02-15 11:42:091098}
1099
Hiroki Nakagawaec54daf2021-06-08 06:12:311100void PrerenderHostRegistry::OnActivationFinished(int frame_tree_node_id) {
1101 // OnActivationFinished() should not be called for non-reserved hosts.
Ho Cheung13d432c22023-03-28 08:39:421102 CHECK(!base::Contains(prerender_host_by_frame_tree_node_id_,
1103 frame_tree_node_id));
Yoshiki Tanioka75decd92022-10-26 12:23:061104
lingqi8a718232022-12-21 03:58:511105 if (!reserved_prerender_host_) {
1106 // The activation finished successfully and has already activated the
1107 // reserved host.
Yoshiki Tanioka75decd92022-10-26 12:23:061108 return;
lingqi8a718232022-12-21 03:58:511109 }
Yoshiki Tanioka75decd92022-10-26 12:23:061110
lingqi8a718232022-12-21 03:58:511111 // The activation navigation is cancelled before activating the prerendered
1112 // page, which means the activation failed.
Ho Cheung13d432c22023-03-28 08:39:421113 CHECK_EQ(frame_tree_node_id, reserved_prerender_host_->frame_tree_node_id());
lingqi8a718232022-12-21 03:58:511114
1115 // TODO(https://crbug.com/1378151): Monitor the final status metric and see
1116 // whether it could be possible.
1117 ScheduleToDeleteAbandonedHost(
1118 std::move(reserved_prerender_host_),
1119 PrerenderCancellationReason(
1120 PrerenderFinalStatus::kActivationNavigationDestroyedBeforeSuccess));
Hiroki Nakagawa5034e1962020-11-12 09:11:301121}
1122
Matt Falkenhagen21ee4f82021-04-13 08:14:281123PrerenderHost* PrerenderHostRegistry::FindNonReservedHostById(
1124 int frame_tree_node_id) {
Hiroki Nakagawa9ba66692021-03-09 07:26:551125 auto id_iter = prerender_host_by_frame_tree_node_id_.find(frame_tree_node_id);
1126 if (id_iter == prerender_host_by_frame_tree_node_id_.end())
1127 return nullptr;
1128 return id_iter->second.get();
1129}
1130
Hiroki Nakagawada88a822023-11-13 02:39:241131bool PrerenderHostRegistry::HasReservedHost() const {
1132 return !!reserved_prerender_host_;
Matt Falkenhagen21ee4f82021-04-13 08:14:281133}
1134
Hiroki Nakagawa3e2527042022-11-24 06:46:421135std::unique_ptr<WebContentsImpl>
1136PrerenderHostRegistry::TakePreCreatedWebContentsForNewTabIfExists(
1137 const mojom::CreateNewWindowParams& create_new_window_params,
1138 const WebContents::CreateParams& web_contents_create_params) {
Ho Cheung13d432c22023-03-28 08:39:421139 CHECK(base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab));
Hiroki Nakagawa3e2527042022-11-24 06:46:421140
1141 // Don't serve a prerendered page if the window needs the opener or is created
1142 // for non-regular navigations.
1143 if (!create_new_window_params.opener_suppressed ||
1144 create_new_window_params.is_form_submission ||
1145 create_new_window_params.pip_options) {
1146 return nullptr;
1147 }
1148
1149 for (auto& iter : prerender_new_tab_handle_by_frame_tree_node_id_) {
1150 std::unique_ptr<WebContentsImpl> web_contents =
1151 iter.second->TakeWebContentsIfAvailable(create_new_window_params,
1152 web_contents_create_params);
1153 if (web_contents) {
1154 prerender_new_tab_handle_by_frame_tree_node_id_.erase(iter);
1155 return web_contents;
1156 }
1157 }
1158 return nullptr;
1159}
1160
Kevin McNee53f0b2d2021-11-02 18:00:451161std::vector<FrameTree*> PrerenderHostRegistry::GetPrerenderFrameTrees() {
1162 std::vector<FrameTree*> result;
Kevin McNeec9d0fda2021-05-19 15:55:171163 for (auto& i : prerender_host_by_frame_tree_node_id_) {
Kevin McNee53f0b2d2021-11-02 18:00:451164 result.push_back(&i.second->GetPrerenderFrameTree());
Kevin McNeec9d0fda2021-05-19 15:55:171165 }
Yoshiki Tanioka75decd92022-10-26 12:23:061166 if (reserved_prerender_host_)
1167 result.push_back(&reserved_prerender_host_->GetPrerenderFrameTree());
1168
Kevin McNeec9d0fda2021-05-19 15:55:171169 return result;
1170}
1171
Hiroki Nakagawa5034e1962020-11-12 09:11:301172PrerenderHost* PrerenderHostRegistry::FindHostByUrlForTesting(
Hiroki Nakagawa968139e22020-10-22 15:03:561173 const GURL& prerendering_url) {
Hiroki Nakagawaeb39fce2021-05-20 05:38:231174 for (auto& iter : prerender_host_by_frame_tree_node_id_) {
Hiroki Nakagawaae141ca2023-10-19 06:01:241175 if (iter.second->IsUrlMatch(prerendering_url)) {
Hiroki Nakagawaeb39fce2021-05-20 05:38:231176 return iter.second.get();
Hiroki Nakagawaae141ca2023-10-19 06:01:241177 }
Hiroki Nakagawaeb39fce2021-05-20 05:38:231178 }
Hiroki Nakagawaeb39fce2021-05-20 05:38:231179 return nullptr;
Hiroki Nakagawa968139e22020-10-22 15:03:561180}
1181
Dominique Fauteux-Chapleau3e8d3452021-07-14 17:20:021182void PrerenderHostRegistry::CancelAllHostsForTesting() {
Ho Cheung13d432c22023-03-28 08:39:421183 CHECK(!reserved_prerender_host_)
Yoshiki Tanioka75decd92022-10-26 12:23:061184 << "It is not possible to cancel a reserved host, so they must not exist "
Dominique Fauteux-Chapleau3e8d3452021-07-14 17:20:021185 "when trying to cancel all hosts";
1186
1187 for (auto& iter : prerender_host_by_frame_tree_node_id_) {
1188 // Asynchronously delete the prerender host.
1189 ScheduleToDeleteAbandonedHost(
1190 std::move(iter.second),
Lingqi Chi20b3fd842022-11-24 05:29:111191 PrerenderCancellationReason(
1192 PrerenderFinalStatus::kCancelAllHostsForTesting));
Dominique Fauteux-Chapleau3e8d3452021-07-14 17:20:021193 }
Yoshiki Tanioka87aab3f2022-09-13 06:25:371194
Yoshiki Tanioka07915d82022-09-15 06:07:511195 // After we're done scheduling deletion, clear the map and the pending queue.
1196 prerender_host_by_frame_tree_node_id_.clear();
1197 pending_prerenders_.clear();
Dominique Fauteux-Chapleau3e8d3452021-07-14 17:20:021198}
1199
Kevin McNee097680d2023-04-05 22:14:511200void PrerenderHostRegistry::BackNavigationLikely(
1201 PreloadingPredictor predictor) {
1202 if (http_cache_query_loader_) {
1203 return;
1204 }
1205
1206 PreloadingData* preloading_data =
1207 PreloadingData::GetOrCreateForWebContents(web_contents());
1208 preloading_data->SetIsNavigationInDomainCallback(
1209 predictor,
1210 base::BindRepeating(IsNavigationInSessionHistoryPredictorDomain));
1211
1212 WebContentsImpl* contents = static_cast<WebContentsImpl*>(web_contents());
1213 NavigationControllerImpl& controller = contents->GetController();
1214 const absl::optional<int> target_index = controller.GetIndexForGoBack();
1215
1216 if (!target_index.has_value()) {
1217 RecordPrerenderBackNavigationEligibility(
1218 predictor, PrerenderBackNavigationEligibility::kNoBackEntry, nullptr);
1219 return;
1220 }
1221
1222 NavigationEntryImpl* back_entry = controller.GetEntryAtIndex(*target_index);
1223 CHECK(back_entry);
1224 const GURL& back_url = back_entry->GetURL();
1225
Mingyu Lei7584b6b2023-04-13 03:02:561226 if (controller.GetBackForwardCache()
1227 .GetOrEvictEntry(back_entry->GetUniqueID())
1228 .has_value()) {
Kevin McNee097680d2023-04-05 22:14:511229 RecordPrerenderBackNavigationEligibility(
1230 predictor, PrerenderBackNavigationEligibility::kBfcacheEntryExists,
1231 nullptr);
1232 return;
1233 }
1234
1235 PreloadingURLMatchCallback same_url_matcher =
1236 PreloadingData::GetSameURLMatcher(back_url);
1237 preloading_data->AddPreloadingPrediction(predictor, /*confidence=*/100,
1238 same_url_matcher);
1239 PreloadingAttempt* attempt = preloading_data->AddPreloadingAttempt(
Hiroki Nakagawaf93776b2023-09-12 22:04:271240 predictor, PreloadingType::kPrerender, same_url_matcher,
1241 web_contents()->GetPrimaryMainFrame()->GetPageUkmSourceId());
Kevin McNee097680d2023-04-05 22:14:511242
1243 if (back_entry->GetMainFrameDocumentSequenceNumber() ==
1244 controller.GetLastCommittedEntry()
1245 ->GetMainFrameDocumentSequenceNumber()) {
1246 RecordPrerenderBackNavigationEligibility(
1247 predictor, PrerenderBackNavigationEligibility::kTargetIsSameDocument,
1248 attempt);
1249 return;
1250 }
1251
1252 if (back_entry->root_node()->frame_entry->method() != "GET") {
1253 RecordPrerenderBackNavigationEligibility(
1254 predictor, PrerenderBackNavigationEligibility::kMethodNotGet, attempt);
1255 return;
1256 }
1257
1258 if (prerender_navigation_utils::IsDisallowedHttpResponseCode(
1259 back_entry->GetHttpStatusCode())) {
1260 RecordPrerenderBackNavigationEligibility(
1261 predictor,
1262 PrerenderBackNavigationEligibility::kTargetIsFailedNavigation, attempt);
1263 return;
1264 }
1265
1266 if (!back_url.SchemeIsHTTPOrHTTPS()) {
1267 RecordPrerenderBackNavigationEligibility(
1268 predictor, PrerenderBackNavigationEligibility::kTargetIsNonHttp,
1269 attempt);
1270 return;
1271 }
1272
1273 // While same site back navigations could potentially be prerendered, doing so
1274 // would involve more significant compat risk. For now, we consider them
1275 // ineligible. See https://crbug.com/1422266 .
1276 if (prerender_navigation_utils::IsSameSite(
1277 back_url,
1278 contents->GetPrimaryMainFrame()->GetLastCommittedOrigin())) {
1279 RecordPrerenderBackNavigationEligibility(
1280 predictor, PrerenderBackNavigationEligibility::kTargetIsSameSite,
1281 attempt);
1282 return;
1283 }
1284
Kevin McNee8bad63f2023-08-23 21:19:371285 // Session history prerendering will reuse the back navigation entry's
1286 // existing SiteInstance. We can't have a prerendered document share a
1287 // SiteInstance with related active content (i.e. an active document with the
1288 // same BrowsingInstance) as that would risk having scripting connections to
1289 // prerendered documents. So this case is not eligible for prerendering.
1290 SiteInstanceImpl* entry_site_instance = back_entry->site_instance();
1291 // `entry_site_instance` could be null in cases such as session restore.
1292 if (entry_site_instance) {
1293 const bool current_and_target_related =
1294 contents->GetSiteInstance()->IsRelatedSiteInstance(entry_site_instance);
1295 const size_t allowable_related_count = current_and_target_related ? 1u : 0u;
1296 if (entry_site_instance->GetRelatedActiveContentsCount() >
1297 allowable_related_count) {
1298 RecordPrerenderBackNavigationEligibility(
1299 predictor, PrerenderBackNavigationEligibility::kRelatedActiveContents,
1300 attempt);
1301 return;
1302 }
1303 }
1304
Kevin McNee097680d2023-04-05 22:14:511305 // To determine whether the resource for the target entry is in the HTTP
1306 // cache, we send a "fake" ResourceRequest which only loads from the cache.
1307 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory =
1308 contents->GetPrimaryMainFrame()
1309 ->GetStoragePartition()
1310 ->GetURLLoaderFactoryForBrowserProcess();
1311 http_cache_query_loader_ = CreateHttpCacheQueryingResourceLoad(back_url);
1312 http_cache_query_loader_->DownloadHeadersOnly(
1313 url_loader_factory.get(),
1314 base::BindOnce(&PrerenderHostRegistry::OnBackResourceCacheResult,
1315 base::Unretained(this), predictor, attempt->GetWeakPtr(),
1316 back_url));
1317}
1318
1319void PrerenderHostRegistry::OnBackResourceCacheResult(
1320 PreloadingPredictor predictor,
1321 base::WeakPtr<PreloadingAttempt> attempt,
1322 GURL back_url,
1323 scoped_refptr<net::HttpResponseHeaders> headers) {
1324 // It's safe to delete the SimpleURLLoader while running the callback that was
1325 // passed to it. We do so once we're done with it in this method.
1326 std::unique_ptr<network::SimpleURLLoader> http_cache_query_loader =
1327 std::move(http_cache_query_loader_);
1328
1329 if (!http_cache_query_loader->LoadedFromCache()) {
1330 // If not in the cache, then this cache-only request must have failed.
1331 CHECK_NE(http_cache_query_loader->NetError(), net::OK);
1332
1333 RecordPrerenderBackNavigationEligibility(
1334 predictor, PrerenderBackNavigationEligibility::kNoHttpCacheEntry,
1335 attempt.get());
1336 return;
1337 }
1338
1339 RecordPrerenderBackNavigationEligibility(
1340 predictor, PrerenderBackNavigationEligibility::kEligible, attempt.get());
1341
1342 if (attempt) {
1343 attempt->SetHoldbackStatus(PreloadingHoldbackStatus::kAllowed);
1344 // At this point, we are only collecting metrics and not actually
1345 // prerendering anything.
1346 attempt->SetTriggeringOutcome(PreloadingTriggeringOutcome::kNoOp);
1347 }
1348}
1349
Lingqi Chi35549e92021-06-22 02:36:131350base::WeakPtr<PrerenderHostRegistry> PrerenderHostRegistry::GetWeakPtr() {
1351 return weak_factory_.GetWeakPtr();
1352}
1353
Asami Doi39651732023-01-31 05:25:261354void PrerenderHostRegistry::DidStartNavigation(
1355 NavigationHandle* navigation_handle) {
1356 if (!base::FeatureList::IsEnabled(
1357 blink::features::kPrerender2MainFrameNavigation)) {
1358 return;
1359 }
1360
1361 // DidStartNavigation is used for monitoring the main frame navigation in a
1362 // prerendered page so do nothing for other navigations.
1363 auto* navigation_request = NavigationRequest::From(navigation_handle);
1364 if (!navigation_request->IsInPrerenderedMainFrame() ||
1365 navigation_request->IsSameDocument()) {
1366 return;
1367 }
1368
Hiroki Nakagawa5e85b9f2023-04-10 07:43:441369 // This navigation is running on the main frame in the prerendered page, so
1370 // its FrameTree::Delegate should be PrerenderHost.
1371 auto* prerender_host = static_cast<PrerenderHost*>(
1372 navigation_request->frame_tree_node()->frame_tree().delegate());
Ho Cheung13d432c22023-03-28 08:39:421373 CHECK(prerender_host);
Asami Doi39651732023-01-31 05:25:261374
1375 prerender_host->DidStartNavigation(navigation_handle);
1376}
1377
Yoshiki Taniokab6a6d722022-10-20 07:58:501378void PrerenderHostRegistry::DidFinishNavigation(
1379 NavigationHandle* navigation_handle) {
1380 auto* navigation_request = NavigationRequest::From(navigation_handle);
1381
1382 if (navigation_request->IsSameDocument())
1383 return;
1384
1385 int main_frame_host_id = navigation_request->frame_tree_node()
1386 ->frame_tree()
Arthur Sonzognif6785ec2022-12-05 10:11:501387 .root()
Yoshiki Taniokab6a6d722022-10-20 07:58:501388 ->frame_tree_node_id();
1389 PrerenderHost* prerender_host = FindNonReservedHostById(main_frame_host_id);
1390 if (!prerender_host)
1391 return;
1392
1393 prerender_host->DidFinishNavigation(navigation_handle);
1394
Taiyo Mizuhashi31bcb1992023-04-27 05:03:031395 if (running_prerender_host_id_ == main_frame_host_id) {
Yoshiki Taniokab6a6d722022-10-20 07:58:501396 running_prerender_host_id_ = RenderFrameHost::kNoFrameTreeNodeId;
1397 StartPrerendering(RenderFrameHost::kNoFrameTreeNodeId);
1398 }
1399}
1400
1401void PrerenderHostRegistry::OnVisibilityChanged(Visibility visibility) {
Hiroki Nakagawa937bf4f2023-02-28 03:26:391402 // Update the timer for prerendering timeout in the background.
Hiroki Nakagawa98af8c42023-03-14 05:36:381403 if (IsBackground(visibility)) {
1404 if (timeout_timer_for_embedder_.IsRunning() ||
1405 timeout_timer_for_speculation_rules_.IsRunning()) {
1406 // Keep the timers which started on a previous visibility change.
1407 return;
1408 }
1409 // Keep a prerendered page alive in the background when its visibility
1410 // state changes to HIDDEN or OCCLUDED.
1411 timeout_timer_for_embedder_.SetTaskRunner(GetTimerTaskRunner());
1412 timeout_timer_for_speculation_rules_.SetTaskRunner(GetTimerTaskRunner());
Yoshiki Tanioka80b78fb2022-10-27 23:40:541413
Hiroki Nakagawa98af8c42023-03-14 05:36:381414 // Cancel PrerenderHost in the background when it exceeds a certain
1415 // amount of time. The timeout differs depending on the trigger type.
1416 timeout_timer_for_embedder_.Start(
1417 FROM_HERE, kTimeToLiveInBackgroundForEmbedder,
Takashi Toyoshima43dcf4312023-04-21 09:33:051418 base::BindOnce(&PrerenderHostRegistry::CancelHostsForTriggers,
1419 base::Unretained(this),
Kouhei Ueno3f37992b2023-11-09 23:29:021420 std::vector({PreloadingTriggerType::kEmbedder}),
Hiroki Nakagawa98af8c42023-03-14 05:36:381421 PrerenderCancellationReason(
1422 PrerenderFinalStatus::kTimeoutBackgrounded)));
1423 timeout_timer_for_speculation_rules_.Start(
1424 FROM_HERE, kTimeToLiveInBackgroundForSpeculationRules,
Takashi Toyoshima43dcf4312023-04-21 09:33:051425 base::BindOnce(
1426 &PrerenderHostRegistry::CancelHostsForTriggers,
1427 base::Unretained(this),
1428 std::vector(
Kouhei Ueno3f37992b2023-11-09 23:29:021429 {PreloadingTriggerType::kSpeculationRule,
1430 PreloadingTriggerType::kSpeculationRuleFromIsolatedWorld}),
Takashi Toyoshima43dcf4312023-04-21 09:33:051431 PrerenderCancellationReason(
1432 PrerenderFinalStatus::kTimeoutBackgrounded)));
Hiroki Nakagawa98af8c42023-03-14 05:36:381433 } else {
1434 // Stop the timer when a prerendered page gets visible to users.
1435 timeout_timer_for_embedder_.Stop();
1436 timeout_timer_for_speculation_rules_.Stop();
Asami Doi714d4342022-12-07 08:28:051437
Taiyo Mizuhashi31bcb1992023-04-27 05:03:031438 // Start the next prerender if needed.
1439 if (running_prerender_host_id_ == RenderFrameHost::kNoFrameTreeNodeId) {
1440 StartPrerendering(RenderFrameHost::kNoFrameTreeNodeId);
Yoshiki Taniokab6a6d722022-10-20 07:58:501441 }
Yoshiki Taniokab6a6d722022-10-20 07:58:501442 }
1443}
1444
Asami Doi7aaf7502022-12-08 09:02:381445void PrerenderHostRegistry::PrimaryMainFrameRenderProcessGone(
1446 base::TerminationStatus status) {
Asami Doidac3f112023-01-18 06:31:021447 CancelAllHosts(
1448 status == base::TERMINATION_STATUS_PROCESS_CRASHED
1449 ? PrerenderFinalStatus::kPrimaryMainFrameRendererProcessCrashed
1450 : PrerenderFinalStatus::kPrimaryMainFrameRendererProcessKilled);
Asami Doi7aaf7502022-12-08 09:02:381451}
1452
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361453int PrerenderHostRegistry::FindHostToActivateInternal(
1454 NavigationRequest& navigation_request) {
1455 RenderFrameHostImpl* render_frame_host =
1456 navigation_request.frame_tree_node()->current_frame_host();
1457 TRACE_EVENT2("navigation",
1458 "PrerenderHostRegistry::FindHostToActivateInternal",
1459 "navigation_url", navigation_request.GetURL().spec(),
1460 "render_frame_host", render_frame_host);
1461
1462 // Disallow activation when the navigation is for a nested browsing context
Tsuyoshi Horo22e05132021-12-01 10:57:271463 // (e.g., iframes, fenced frames). This is because nested browsing contexts
1464 // such as iframes are supposed to be created in the parent's browsing context
1465 // group and can script with the parent, but prerendered pages are created in
1466 // new browsing context groups. And also, we disallow activation when the
1467 // navigation is for a fenced frame to prevent the communication path from the
1468 // embedding page to the fenced frame.
1469 if (!navigation_request.IsInPrimaryMainFrame())
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361470 return RenderFrameHost::kNoFrameTreeNodeId;
1471
1472 // Disallow activation when the navigation happens in the prerendering frame
1473 // tree.
1474 if (navigation_request.IsInPrerenderedMainFrame())
1475 return RenderFrameHost::kNoFrameTreeNodeId;
1476
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361477 // Find an available host for the navigation URL.
1478 PrerenderHost* host = nullptr;
Yoshiki Tanioka7e13cbcc2022-10-19 03:34:371479 for (const auto& [host_id, it_prerender_host] :
Lingqi Chi21d9feb2022-02-02 09:42:181480 prerender_host_by_frame_tree_node_id_) {
1481 if (it_prerender_host->IsUrlMatch(navigation_request.GetURL())) {
1482 host = it_prerender_host.get();
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361483 break;
1484 }
1485 }
1486 if (!host)
1487 return RenderFrameHost::kNoFrameTreeNodeId;
1488
Hiroki Nakagawad77864c2023-09-20 22:34:031489 // Disallow activation when the navigation URL has an effective URL like
1490 // hosted apps and NTP.
1491 if (SiteInstanceImpl::HasEffectiveURL(web_contents()->GetBrowserContext(),
1492 navigation_request.GetURL())) {
1493 CancelHost(host->frame_tree_node_id(),
1494 PrerenderFinalStatus::kActivationUrlHasEffectiveUrl);
1495 return RenderFrameHost::kNoFrameTreeNodeId;
1496 }
1497
Hiroki Nakagawa2f427342023-07-19 23:27:041498 // Disallow activation when other auxiliary browsing contexts (e.g., pop-up
1499 // windows) exist in the same browsing context group. This is because these
1500 // browsing contexts should be able to script each other, but prerendered
1501 // pages are created in new browsing context groups.
1502 SiteInstance* site_instance = render_frame_host->GetSiteInstance();
1503 if (site_instance->GetRelatedActiveContentsCount() != 1u) {
1504 CancelHost(host->frame_tree_node_id(),
1505 PrerenderFinalStatus::kActivatedWithAuxiliaryBrowsingContexts);
1506 return RenderFrameHost::kNoFrameTreeNodeId;
1507 }
1508
Asami Doi7e33f2f2022-12-12 16:15:091509 // TODO(crbug.com/1399709): Remove the restriction after further investigation
1510 // and discussion.
Hiroki Nakagawa98af8c42023-03-14 05:36:381511 // Disallow activation when the navigation happens in the hidden tab.
Asami Doi7e33f2f2022-12-12 16:15:091512 if (web_contents()->GetVisibility() == Visibility::HIDDEN) {
1513 CancelHost(host->frame_tree_node_id(),
1514 PrerenderFinalStatus::kActivatedInBackground);
1515 return RenderFrameHost::kNoFrameTreeNodeId;
1516 }
1517
Yoshiki Tanioka07915d82022-09-15 06:07:511518 if (!host->GetInitialNavigationId().has_value()) {
Yoshiki Tanioka07915d82022-09-15 06:07:511519 CancelHost(host->frame_tree_node_id(),
Yoshiki Tanioka49b4cfb2022-10-20 09:25:311520 PrerenderFinalStatus::kActivatedBeforeStarted);
Yoshiki Tanioka044f6b52022-10-20 12:18:531521 return RenderFrameHost::kNoFrameTreeNodeId;
Yoshiki Tanioka07915d82022-09-15 06:07:511522 }
1523
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361524 // Compare navigation params from activation with the navigation params
1525 // from the initial prerender navigation. If they don't match, the navigation
1526 // should not activate the prerendered page.
Miina Koyama89b815042023-10-31 08:20:121527 if (std::unique_ptr<PrerenderMismatchedHeaders> mismatched_headers =
1528 host->CheckInitialPrerenderNavigationParamsCompatibleWithNavigation(
1529 navigation_request)) {
Lingqi Chiaeec5df12022-10-26 03:41:421530 // TODO(https://crbug.com/1328365): Report a detailed reason to devtools.
1531 // Currently users have to check
1532 // Prerender.Experimental.ActivationNavigationParamsMatch.
1533 // TODO(lingqi): We'd better cancel all hosts.
Miina Koyama89b815042023-10-31 08:20:121534
1535 PrerenderCancellationReason reason = PrerenderCancellationReason::
1536 BuildForActivationNavigationParameterMismatch(
1537 std::move(mismatched_headers));
1538
1539 CancelHost(host->frame_tree_node_id(), reason);
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361540 return RenderFrameHost::kNoFrameTreeNodeId;
1541 }
1542
Harkiran Bolaria23360532021-08-12 10:27:251543 if (!host->IsFramePolicyCompatibleWithPrimaryFrameTree()) {
lingqi6d03e422023-01-19 02:37:061544 CancelHost(host->frame_tree_node_id(),
1545 PrerenderFinalStatus::kActivationFramePolicyNotCompatible);
Harkiran Bolaria23360532021-08-12 10:27:251546 return RenderFrameHost::kNoFrameTreeNodeId;
1547 }
1548
Yoshiki Tanioka7e13cbcc2022-10-19 03:34:371549 // Cancel all the other prerender hosts because we no longer need the other
1550 // hosts after we determine the host to be activated.
1551 std::vector<int> cancelled_prerenders;
1552 for (const auto& [host_id, _] : prerender_host_by_frame_tree_node_id_) {
1553 if (host_id != host->frame_tree_node_id()) {
1554 cancelled_prerenders.push_back(host_id);
1555 }
1556 }
Hiroki Nakagawa640dd712022-12-08 04:05:081557 if (base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)) {
1558 for (const auto& [host_id, _] :
1559 prerender_new_tab_handle_by_frame_tree_node_id_) {
1560 cancelled_prerenders.push_back(host_id);
1561 }
1562 } else {
Ho Cheung13d432c22023-03-28 08:39:421563 CHECK(prerender_new_tab_handle_by_frame_tree_node_id_.empty());
Hiroki Nakagawa3e2527042022-11-24 06:46:421564 }
Samar Chehade-Lepleuxce1baf22023-05-16 09:50:091565 CancelHosts(
1566 cancelled_prerenders,
1567 PrerenderCancellationReason(PrerenderFinalStatus::kTriggerDestroyed));
Yoshiki Tanioka7e13cbcc2022-10-19 03:34:371568 pending_prerenders_.clear();
1569
Hiroki Nakagawa6c5f39bd2021-07-13 00:22:361570 return host->frame_tree_node_id();
1571}
1572
Hiroki Nakagawaec54daf2021-06-08 06:12:311573void PrerenderHostRegistry::ScheduleToDeleteAbandonedHost(
1574 std::unique_ptr<PrerenderHost> prerender_host,
Lingqi Chi20b3fd842022-11-24 05:29:111575 const PrerenderCancellationReason& cancellation_reason) {
Lingqi Chi22f3c2d2023-04-27 15:55:501576 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
1577 CHECK(BrowserThread::CurrentlyOn(BrowserThread::UI))
1578 << "Post tasks to destroy PrerenderHosts on non-ui threads "
1579 "with reason of "
1580 << static_cast<int>(cancellation_reason.final_status());
Lingqi Chi20b3fd842022-11-24 05:29:111581 prerender_host->RecordFailedFinalStatus(PassKey(), cancellation_reason);
Hiroki Nakagawaec54daf2021-06-08 06:12:311582
1583 // Asynchronously delete the prerender host.
1584 to_be_deleted_hosts_.push_back(std::move(prerender_host));
Sean Maher52fa5a72022-11-14 15:53:251585 base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
Hiroki Nakagawaec54daf2021-06-08 06:12:311586 FROM_HERE, base::BindOnce(&PrerenderHostRegistry::DeleteAbandonedHosts,
1587 weak_factory_.GetWeakPtr()));
1588}
1589
Takashi Toyoshima9304e842021-05-19 11:24:551590void PrerenderHostRegistry::DeleteAbandonedHosts() {
Lingqi Chi22f3c2d2023-04-27 15:55:501591 DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
Hiroki Nakagawae2d0e492023-05-17 05:37:281592 // Swap the vector and let it scope out instead of directly destructing the
1593 // hosts in the vector, for example, by `to_be_deleted_hosts_.clear()`. This
1594 // avoids potential cases where a host being deleted indirectly modifies
1595 // `to_be_deleted_hosts_` while the vector is being cleared up. See
1596 // https://crbug.com/1431744 for contexts.
1597 std::vector<std::unique_ptr<PrerenderHost>> hosts;
1598 to_be_deleted_hosts_.swap(hosts);
Takashi Toyoshima9304e842021-05-19 11:24:551599}
1600
Matt Falkenhagenf884d0f2021-02-18 06:31:351601void PrerenderHostRegistry::NotifyTrigger(const GURL& url) {
Taiyo Mizuhashi295ccbdf2023-09-13 09:17:021602 for (Observer& obs : observers_) {
Matt Falkenhagenf884d0f2021-02-18 06:31:351603 obs.OnTrigger(url);
Taiyo Mizuhashi295ccbdf2023-09-13 09:17:021604 }
1605}
1606
1607void PrerenderHostRegistry::NotifyCancel(
Taiyo Mizuhashi5694b822023-09-26 16:10:301608 int host_frame_tree_node_id,
Taiyo Mizuhashi295ccbdf2023-09-13 09:17:021609 const PrerenderCancellationReason& reason) {
1610 for (Observer& obs : observers_) {
Taiyo Mizuhashi5694b822023-09-26 16:10:301611 obs.OnCancel(host_frame_tree_node_id, reason);
Taiyo Mizuhashi295ccbdf2023-09-13 09:17:021612 }
Carlos Caballerod3644512021-02-11 15:59:531613}
1614
Kouhei Ueno3f37992b2023-11-09 23:29:021615PreloadingTriggerType PrerenderHostRegistry::GetPrerenderTriggerType(
Asami Doi02406272021-11-12 05:01:441616 int frame_tree_node_id) {
Hiroki Nakagawada88a822023-11-13 02:39:241617 CHECK(reserved_prerender_host_);
1618 CHECK_EQ(reserved_prerender_host_->frame_tree_node_id(), frame_tree_node_id);
1619 return reserved_prerender_host_->trigger_type();
Asami Doi02406272021-11-12 05:01:441620}
1621
1622const std::string& PrerenderHostRegistry::GetPrerenderEmbedderHistogramSuffix(
1623 int frame_tree_node_id) {
Hiroki Nakagawada88a822023-11-13 02:39:241624 CHECK(reserved_prerender_host_);
1625 CHECK_EQ(reserved_prerender_host_->frame_tree_node_id(), frame_tree_node_id);
1626 return reserved_prerender_host_->embedder_histogram_suffix();
Asami Doi02406272021-11-12 05:01:441627}
1628
Taiyo Mizuhashi748071f2023-08-01 03:46:011629PrerenderHostRegistry::PrerenderLimitGroup
1630PrerenderHostRegistry::GetPrerenderLimitGroup(
Kouhei Ueno3f37992b2023-11-09 23:29:021631 PreloadingTriggerType trigger_type,
Taiyo Mizuhashi748071f2023-08-01 03:46:011632 absl::optional<blink::mojom::SpeculationEagerness> eagerness) {
Robert Lin0e31d562022-01-26 12:12:301633 switch (trigger_type) {
Kouhei Ueno3f37992b2023-11-09 23:29:021634 case PreloadingTriggerType::kSpeculationRule:
1635 case PreloadingTriggerType::kSpeculationRuleFromIsolatedWorld:
Taiyo Mizuhashi748071f2023-08-01 03:46:011636 CHECK(eagerness.has_value());
1637 switch (eagerness.value()) {
1638 // Separate the limits of speculation rules into two categories: eager,
1639 // which are triggered immediately after adding the rule, and
1640 // non-eager(moderate, conservative), which wait for a specific user
1641 // action to trigger, aiming to apply the appropriate corresponding
1642 // limits for these attributes.
1643 case blink::mojom::SpeculationEagerness::kEager:
1644 return PrerenderLimitGroup::kSpeculationRulesEager;
1645 case blink::mojom::SpeculationEagerness::kModerate:
1646 case blink::mojom::SpeculationEagerness::kConservative:
1647 return PrerenderLimitGroup::kSpeculationRulesNonEager;
1648 }
Kouhei Ueno3f37992b2023-11-09 23:29:021649 case PreloadingTriggerType::kEmbedder:
Taiyo Mizuhashi748071f2023-08-01 03:46:011650 return PrerenderLimitGroup::kEmbedder;
1651 }
1652}
1653
Taiyo Mizuhashicf7fd302023-08-29 04:38:091654int PrerenderHostRegistry::GetHostCountByTriggerType(
Kouhei Ueno3f37992b2023-11-09 23:29:021655 PreloadingTriggerType trigger_type) {
Taiyo Mizuhashicf7fd302023-08-29 04:38:091656 int host_count = 0;
1657 for (const auto& [_, host] : prerender_host_by_frame_tree_node_id_) {
1658 if (host->trigger_type() == trigger_type) {
1659 ++host_count;
1660 }
1661 }
Taiyo Mizuhashi3a4c3d42023-11-09 10:27:141662
1663 if (base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)) {
1664 for (const auto& [_, handle] :
1665 prerender_new_tab_handle_by_frame_tree_node_id_) {
1666 if (handle->trigger_type() == trigger_type) {
1667 ++host_count;
1668 }
1669 }
1670 }
1671
Taiyo Mizuhashicf7fd302023-08-29 04:38:091672 return host_count;
1673}
1674
1675int PrerenderHostRegistry::GetHostCountByLimitGroup(
1676 PrerenderLimitGroup limit_group) {
Taiyo Mizuhashi92119792023-09-13 07:18:031677 CHECK(
1678 base::FeatureList::IsEnabled(features::kPrerender2NewLimitAndScheduler));
Taiyo Mizuhashicf7fd302023-08-29 04:38:091679 int host_count = 0;
1680 for (const auto& [_, host] : prerender_host_by_frame_tree_node_id_) {
1681 if (GetPrerenderLimitGroup(host->trigger_type(), host->eagerness()) ==
1682 limit_group) {
1683 ++host_count;
1684 }
1685 }
Taiyo Mizuhashi3a4c3d42023-11-09 10:27:141686
1687 if (base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)) {
1688 for (const auto& [_, handle] :
1689 prerender_new_tab_handle_by_frame_tree_node_id_) {
1690 if (GetPrerenderLimitGroup(handle->trigger_type(), handle->eagerness()) ==
1691 limit_group) {
1692 ++host_count;
1693 }
1694 }
1695 }
1696
Taiyo Mizuhashicf7fd302023-08-29 04:38:091697 return host_count;
1698}
1699
Taiyo Mizuhashi748071f2023-08-01 03:46:011700bool PrerenderHostRegistry::IsAllowedToStartPrerenderingForTrigger(
Kouhei Ueno3f37992b2023-11-09 23:29:021701 PreloadingTriggerType trigger_type,
Taiyo Mizuhashi748071f2023-08-01 03:46:011702 absl::optional<blink::mojom::SpeculationEagerness> eagerness) {
Taiyo Mizuhashi748071f2023-08-01 03:46:011703 PrerenderLimitGroup limit_group;
Taiyo Mizuhashicf7fd302023-08-29 04:38:091704 int host_count;
Taiyo Mizuhashi92119792023-09-13 07:18:031705 if (base::FeatureList::IsEnabled(features::kPrerender2NewLimitAndScheduler)) {
Taiyo Mizuhashi748071f2023-08-01 03:46:011706 limit_group = GetPrerenderLimitGroup(trigger_type, eagerness);
Taiyo Mizuhashicf7fd302023-08-29 04:38:091707 host_count = GetHostCountByLimitGroup(limit_group);
1708 } else {
1709 host_count = GetHostCountByTriggerType(trigger_type);
Taiyo Mizuhashi748071f2023-08-01 03:46:011710 }
1711
Taiyo Mizuhashi92119792023-09-13 07:18:031712 if (base::FeatureList::IsEnabled(features::kPrerender2NewLimitAndScheduler)) {
Taiyo Mizuhashi748071f2023-08-01 03:46:011713 // Apply the limit of maximum number of running prerenders per
1714 // PrerenderLimitGroup.
1715 switch (limit_group) {
1716 case PrerenderLimitGroup::kSpeculationRulesEager:
Taiyo Mizuhashicf7fd302023-08-29 04:38:091717 return host_count < base::GetFieldTrialParamByFeatureAsInt(
Taiyo Mizuhashi92119792023-09-13 07:18:031718 features::kPrerender2NewLimitAndScheduler,
Taiyo Mizuhashicf7fd302023-08-29 04:38:091719 kMaxNumOfRunningSpeculationRulesEagerPrerenders,
1720 10);
1721 case PrerenderLimitGroup::kSpeculationRulesNonEager: {
1722 int limit_non_eager = base::GetFieldTrialParamByFeatureAsInt(
Taiyo Mizuhashi92119792023-09-13 07:18:031723 features::kPrerender2NewLimitAndScheduler,
Taiyo Mizuhashicf7fd302023-08-29 04:38:091724 kMaxNumOfRunningSpeculationRulesNonEagerPrerenders, 2);
1725
1726 // When the limit on non-eager speculation rules is reached, cancel the
1727 // oldest host to allow a newly incoming trigger to start.
1728 if (host_count >= limit_non_eager) {
1729 int oldest_prerender_host_id;
1730
1731 // Find the oldest non-eager prerender that has not been canceled yet.
1732 do {
1733 oldest_prerender_host_id =
1734 non_eager_prerender_host_id_by_arrival_order_.front();
1735 non_eager_prerender_host_id_by_arrival_order_.pop_front();
Taiyo Mizuhashi3a4c3d42023-11-09 10:27:141736 } while (
1737 base::FeatureList::IsEnabled(blink::features::kPrerender2InNewTab)
1738 ? !prerender_host_by_frame_tree_node_id_.contains(
1739 oldest_prerender_host_id) &&
1740 !prerender_new_tab_handle_by_frame_tree_node_id_
1741 .contains(oldest_prerender_host_id)
1742 : !prerender_host_by_frame_tree_node_id_.contains(
1743 oldest_prerender_host_id));
Taiyo Mizuhashicf7fd302023-08-29 04:38:091744
Taiyo Mizuhashi459a6b22023-09-13 07:56:481745 CHECK(CancelHost(oldest_prerender_host_id,
1746 PrerenderFinalStatus::
1747 kMaxNumOfRunningNonEagerPrerendersExceeded));
Taiyo Mizuhashicf7fd302023-08-29 04:38:091748
1749 CHECK_LT(GetHostCountByLimitGroup(limit_group), limit_non_eager);
1750 }
1751
1752 return true;
1753 }
Taiyo Mizuhashi748071f2023-08-01 03:46:011754 case PrerenderLimitGroup::kEmbedder:
Taiyo Mizuhashicf7fd302023-08-29 04:38:091755 return host_count < base::GetFieldTrialParamByFeatureAsInt(
Taiyo Mizuhashi92119792023-09-13 07:18:031756 features::kPrerender2NewLimitAndScheduler,
Taiyo Mizuhashicf7fd302023-08-29 04:38:091757 kMaxNumOfRunningEmbedderPrerenders, 2);
Taiyo Mizuhashi748071f2023-08-01 03:46:011758 }
1759 }
1760 switch (trigger_type) {
Kouhei Ueno3f37992b2023-11-09 23:29:021761 case PreloadingTriggerType::kSpeculationRule:
1762 case PreloadingTriggerType::kSpeculationRuleFromIsolatedWorld:
Taiyo Mizuhashi748071f2023-08-01 03:46:011763 // The number of prerenders triggered by speculation rules is limited to
1764 // a Finch config param.
Taiyo Mizuhashicf7fd302023-08-29 04:38:091765 return host_count <
Asami Doi3b6840212022-07-28 02:39:191766 base::GetFieldTrialParamByFeatureAsInt(
1767 blink::features::kPrerender2,
1768 blink::features::kPrerender2MaxNumOfRunningSpeculationRules,
Asami Doic032d6182022-09-16 15:42:291769 10);
Kouhei Ueno3f37992b2023-11-09 23:29:021770 case PreloadingTriggerType::kEmbedder:
Taiyo Mizuhashi748071f2023-08-01 03:46:011771 // Currently the number of prerenders triggered by an embedder is
1772 // limited to two.
Taiyo Mizuhashicf7fd302023-08-29 04:38:091773 return host_count < 2;
Robert Lin0e31d562022-01-26 12:12:301774 }
1775}
1776
Asami Doi3b6840212022-07-28 02:39:191777void PrerenderHostRegistry::DestroyWhenUsingExcessiveMemory(
1778 int frame_tree_node_id) {
Hiroki Nakagawa8b160fa2023-06-09 01:47:321779 if (!base::FeatureList::IsEnabled(
1780 blink::features::kPrerender2MemoryControls)) {
Asami Doi3b6840212022-07-28 02:39:191781 return;
Hiroki Nakagawa8b160fa2023-06-09 01:47:321782 }
1783
Taiyo Mizuhashi0ea5102b2023-09-13 07:38:401784 if (base::FeatureList::IsEnabled(
1785 features::kPrerender2BypassMemoryLimitCheck)) {
Hiroki Nakagawa8b160fa2023-06-09 01:47:321786 return;
1787 }
Asami Doi3b6840212022-07-28 02:39:191788
Hiroki Nakagawa3c252282023-04-18 10:31:351789 // Override the memory restriction when the DevTools is open.
1790 if (IsDevToolsOpen(*web_contents())) {
1791 return;
1792 }
1793
Asami Doi3b6840212022-07-28 02:39:191794 memory_instrumentation::MemoryInstrumentation::GetInstance()
1795 ->RequestPrivateMemoryFootprint(
1796 base::kNullProcessId,
1797 base::BindOnce(&PrerenderHostRegistry::DidReceiveMemoryDump,
1798 weak_factory_.GetWeakPtr(), frame_tree_node_id));
1799}
1800
1801void PrerenderHostRegistry::DidReceiveMemoryDump(
1802 int frame_tree_node_id,
1803 bool success,
1804 std::unique_ptr<memory_instrumentation::GlobalMemoryDump> dump) {
Ho Cheung13d432c22023-03-28 08:39:421805 CHECK(
Asami Doi3b6840212022-07-28 02:39:191806 base::FeatureList::IsEnabled(blink::features::kPrerender2MemoryControls));
Hiroki Nakagawa3c252282023-04-18 10:31:351807
1808 // Override the memory restriction when the DevTools is open.
1809 if (IsDevToolsOpen(*web_contents())) {
1810 return;
1811 }
1812
Asami Doi3b6840212022-07-28 02:39:191813 if (!success) {
Hiroki Nakagawa35fda742023-09-07 03:51:471814 // Give up checking the memory consumption and continue prerendering. This
1815 // case commonly happens due to lifecycle changes of renderer processes
1816 // during the query. Skipping the check should be safe for other safety
1817 // measures: the limit on the number of ongoing prerendering requests and
1818 // memory pressure events should prevent excessive memory usage.
Asami Doi3b6840212022-07-28 02:39:191819 return;
1820 }
1821
1822 int64_t private_footprint_total_kb = 0;
1823 for (const auto& pmd : dump->process_dumps()) {
1824 private_footprint_total_kb += pmd.os_dump().private_footprint_kb;
1825 }
1826
Hiroki Nakagawa04838d542023-05-25 04:15:211827 // The default acceptable percent is 60% of the system memory.
Asami Doi3b6840212022-07-28 02:39:191828 int acceptable_percent_of_system_memory =
1829 base::GetFieldTrialParamByFeatureAsInt(
1830 blink::features::kPrerender2MemoryControls,
1831 blink::features::
1832 kPrerender2MemoryAcceptablePercentOfSystemMemoryParamName,
Hiroki Nakagawa04838d542023-05-25 04:15:211833 60);
Asami Doi3b6840212022-07-28 02:39:191834
1835 // When the current memory usage is higher than
1836 // `acceptable_percent_of_system_memory` % of the system memory, cancel a
1837 // prerendering with `frame_tree_node_id`.
1838 if (private_footprint_total_kb * 1024 >=
1839 acceptable_percent_of_system_memory * 0.01 *
1840 base::SysInfo::AmountOfPhysicalMemory()) {
Yoshiki Tanioka49b4cfb2022-10-20 09:25:311841 CancelHost(frame_tree_node_id, PrerenderFinalStatus::kMemoryLimitExceeded);
Asami Doi3b6840212022-07-28 02:39:191842 }
1843}
1844
Hiroki Nakagawa34e5d672023-04-21 05:00:091845void PrerenderHostRegistry::OnMemoryPressure(
1846 base::MemoryPressureListener::MemoryPressureLevel memory_pressure_level) {
1847 // Ignore the memory pressure event if the memory control is disabled.
1848 if (!base::FeatureList::IsEnabled(
1849 blink::features::kPrerender2MemoryControls)) {
1850 return;
1851 }
1852
1853 switch (memory_pressure_level) {
1854 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_NONE:
1855 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_MODERATE:
1856 break;
1857 case base::MemoryPressureListener::MEMORY_PRESSURE_LEVEL_CRITICAL:
1858 CancelAllHosts(PrerenderFinalStatus::kMemoryPressureAfterTriggered);
1859 break;
1860 }
1861}
1862
Yoshiki Tanioka80b78fb2022-10-27 23:40:541863scoped_refptr<base::SingleThreadTaskRunner>
1864PrerenderHostRegistry::GetTimerTaskRunner() {
Sean Maher5b9af51f2022-11-21 15:32:471865 return timer_task_runner_for_testing_
1866 ? timer_task_runner_for_testing_
1867 : base::SingleThreadTaskRunner::GetCurrentDefault();
Yoshiki Tanioka80b78fb2022-10-27 23:40:541868}
1869
1870void PrerenderHostRegistry::SetTaskRunnerForTesting(
1871 scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
1872 timer_task_runner_for_testing_ = std::move(task_runner);
1873}
1874
Hiroki Nakagawa968139e22020-10-22 15:03:561875} // namespace content