blob: bfa0fb02bf1eaacc6fcf519315f2b6c8a211e5e2 [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2021 The Chromium Authors
Robert Linc37fb582021-11-11 03:18:472// 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_handle_impl.h"
Robert Linc37fb582021-11-11 03:18:476
Hiroki Nakagawa56dbf1472025-01-29 08:40:387#include <limits>
8
Yoshiki Tanioka49b4cfb2022-10-20 09:25:319#include "content/browser/preloading/prerender/prerender_final_status.h"
Lei Zhang5686e522023-03-02 17:33:1010#include "content/browser/preloading/prerender/prerender_host.h"
11#include "content/browser/preloading/prerender/prerender_host_registry.h"
Hiroshige Hayashizaki583b4962025-08-19 18:58:2612#include "content/public/browser/preloading_data.h"
Kouhei Ueno3f37992b2023-11-09 23:29:0213#include "content/public/browser/preloading_trigger_type.h"
Lingqi Chi60cff3252023-12-05 02:32:5514#include "url/gurl.h"
Asami Doi158ffb422022-01-26 08:43:0515
Robert Linc37fb582021-11-11 03:18:4716namespace content {
17
Hiroki Nakagawa86e06d3a2025-01-22 04:51:4218namespace {
19
Hiroki Nakagawa56dbf1472025-01-29 08:40:3820int32_t GetNextHandleId() {
21 static int32_t next_handle_id = 1;
22 CHECK_LT(next_handle_id, std::numeric_limits<int32_t>::max());
23 return next_handle_id++;
24}
25
Hiroki Nakagawa86e06d3a2025-01-22 04:51:4226// Returns true when the error callback should be fired. The callback does not
27// need to be fired when prerendering succeed but is never activated, or it is
28// intentinally cancelled by an embedder (e.g., calling the cancellation API).
29// Otherwise, the callback should be fired.
30bool ShouldFireErrorCallback(PrerenderFinalStatus status) {
31 switch (status) {
32 case PrerenderFinalStatus::kActivated:
33 NOTREACHED();
34
35 // Prerendering is not activated.
36 case PrerenderFinalStatus::kDestroyed:
37 return false;
38
39 case PrerenderFinalStatus::kLowEndDevice:
40 case PrerenderFinalStatus::kInvalidSchemeRedirect:
41 case PrerenderFinalStatus::kInvalidSchemeNavigation:
42 case PrerenderFinalStatus::kNavigationRequestBlockedByCsp:
Hiroki Nakagawa86e06d3a2025-01-22 04:51:4243 case PrerenderFinalStatus::kMojoBinderPolicy:
44 case PrerenderFinalStatus::kRendererProcessCrashed:
45 case PrerenderFinalStatus::kRendererProcessKilled:
46 case PrerenderFinalStatus::kDownload:
47 return true;
48
49 // Prerendering is intentionally cancelled by the app or not activated.
50 case PrerenderFinalStatus::kTriggerDestroyed:
51 return false;
52
53 case PrerenderFinalStatus::kNavigationNotCommitted:
54 case PrerenderFinalStatus::kNavigationBadHttpStatus:
55 case PrerenderFinalStatus::kClientCertRequested:
56 case PrerenderFinalStatus::kNavigationRequestNetworkError:
57 case PrerenderFinalStatus::kCancelAllHostsForTesting:
58 case PrerenderFinalStatus::kDidFailLoad:
59 case PrerenderFinalStatus::kStop:
60 case PrerenderFinalStatus::kSslCertificateError:
61 case PrerenderFinalStatus::kLoginAuthRequested:
62 case PrerenderFinalStatus::kUaChangeRequiresReload:
63 case PrerenderFinalStatus::kBlockedByClient:
64 case PrerenderFinalStatus::kMixedContent:
65 case PrerenderFinalStatus::kTriggerBackgrounded:
66 case PrerenderFinalStatus::kMemoryLimitExceeded:
67 case PrerenderFinalStatus::kDataSaverEnabled:
68 case PrerenderFinalStatus::kTriggerUrlHasEffectiveUrl:
69 case PrerenderFinalStatus::kActivatedBeforeStarted:
70 case PrerenderFinalStatus::kInactivePageRestriction:
71 case PrerenderFinalStatus::kStartFailed:
72 case PrerenderFinalStatus::kTimeoutBackgrounded:
73 case PrerenderFinalStatus::kCrossSiteRedirectInInitialNavigation:
74 case PrerenderFinalStatus::kCrossSiteNavigationInInitialNavigation:
75 case PrerenderFinalStatus::
76 kSameSiteCrossOriginRedirectNotOptInInInitialNavigation:
77 case PrerenderFinalStatus::
78 kSameSiteCrossOriginNavigationNotOptInInInitialNavigation:
79 case PrerenderFinalStatus::kActivationNavigationParameterMismatch:
80 case PrerenderFinalStatus::kActivatedInBackground:
81 case PrerenderFinalStatus::kActivationNavigationDestroyedBeforeSuccess:
82 return true;
83
84 // The associated tab is closed.
85 case PrerenderFinalStatus::kTabClosedByUserGesture:
86 case PrerenderFinalStatus::kTabClosedWithoutUserGesture:
87 return false;
88
89 case PrerenderFinalStatus::kPrimaryMainFrameRendererProcessCrashed:
90 case PrerenderFinalStatus::kPrimaryMainFrameRendererProcessKilled:
91 case PrerenderFinalStatus::kActivationFramePolicyNotCompatible:
92 case PrerenderFinalStatus::kPreloadingDisabled:
93 case PrerenderFinalStatus::kBatterySaverEnabled:
94 case PrerenderFinalStatus::kActivatedDuringMainFrameNavigation:
95 case PrerenderFinalStatus::kPreloadingUnsupportedByWebContents:
96 case PrerenderFinalStatus::kCrossSiteRedirectInMainFrameNavigation:
97 case PrerenderFinalStatus::kCrossSiteNavigationInMainFrameNavigation:
98 case PrerenderFinalStatus::
99 kSameSiteCrossOriginRedirectNotOptInInMainFrameNavigation:
100 case PrerenderFinalStatus::
101 kSameSiteCrossOriginNavigationNotOptInInMainFrameNavigation:
102 case PrerenderFinalStatus::kMemoryPressureOnTrigger:
103 case PrerenderFinalStatus::kMemoryPressureAfterTriggered:
104 case PrerenderFinalStatus::kPrerenderingDisabledByDevTools:
105 return true;
106
107 // This is used for speculation rules, not for embedder triggers.
108 case PrerenderFinalStatus::kSpeculationRuleRemoved:
109 NOTREACHED();
110
111 case PrerenderFinalStatus::kActivatedWithAuxiliaryBrowsingContexts:
112 return true;
113
114 // These are used for speculation rules, not for embedder triggers.
Takashi Nakayama978f0a152025-06-17 08:26:25115 case PrerenderFinalStatus::kMaxNumOfRunningImmediatePrerendersExceeded:
116 case PrerenderFinalStatus::kMaxNumOfRunningNonImmediatePrerendersExceeded:
Hiroki Nakagawa86e06d3a2025-01-22 04:51:42117 NOTREACHED();
118
119 case PrerenderFinalStatus::kMaxNumOfRunningEmbedderPrerendersExceeded:
120 case PrerenderFinalStatus::kPrerenderingUrlHasEffectiveUrl:
121 case PrerenderFinalStatus::kRedirectedPrerenderingUrlHasEffectiveUrl:
122 case PrerenderFinalStatus::kActivationUrlHasEffectiveUrl:
123 case PrerenderFinalStatus::kJavaScriptInterfaceAdded:
124 case PrerenderFinalStatus::kJavaScriptInterfaceRemoved:
125 case PrerenderFinalStatus::kAllPrerenderingCanceled:
126 return true;
127
128 // window.close() is called in a prerendered page.
129 case PrerenderFinalStatus::kWindowClosed:
130 return false;
131
132 case PrerenderFinalStatus::kSlowNetwork:
133 return true;
134
135 case PrerenderFinalStatus::kOtherPrerenderedPageActivated:
136 return false;
137
Hiroki Nakagawa86e06d3a2025-01-22 04:51:42138 case PrerenderFinalStatus::kPrerenderFailedDuringPrefetch:
139 return true;
Steven Wei893cb02c2025-02-12 20:51:30140
141 // Prerendering is intentionally canceled by the Delete Browsing Data
142 // option or with Clear-Site-Data response headers.
143 case PrerenderFinalStatus::kBrowsingDataRemoved:
144 return false;
Jiacheng Guocfca196d2025-07-30 02:13:20145 // The PrerenderHost is reused by another prerender request.
146 case PrerenderFinalStatus::kPrerenderHostReused:
147 return false;
Hiroki Nakagawa86e06d3a2025-01-22 04:51:42148 }
149}
150
151} // namespace
152
Robert Linc37fb582021-11-11 03:18:47153PrerenderHandleImpl::PrerenderHandleImpl(
154 base::WeakPtr<PrerenderHostRegistry> prerender_host_registry,
Avi Drissmandcc8e682024-09-04 14:14:48155 FrameTreeNodeId frame_tree_node_id,
Hiroki Nakagawae628b452025-03-29 13:08:06156 const GURL& prerendering_url,
157 std::optional<net::HttpNoVarySearchData> no_vary_search_hint)
Hiroki Nakagawa56dbf1472025-01-29 08:40:38158 : handle_id_(GetNextHandleId()),
159 prerender_host_registry_(std::move(prerender_host_registry)),
Robert Lin624a0fe2021-12-08 22:58:30160 frame_tree_node_id_(frame_tree_node_id),
Hiroki Nakagawae628b452025-03-29 13:08:06161 prerendering_url_(prerendering_url),
162 no_vary_search_hint_(std::move(no_vary_search_hint)) {
Ho Cheung13d432c22023-03-28 08:39:42163 CHECK(!prerendering_url_.is_empty());
Lingqi Chi90679972022-08-15 04:09:32164 // PrerenderHandleImpl is now designed only for embedder triggers. If you use
Asami Doi158ffb422022-01-26 08:43:05165 // this handle for other triggers, please make sure to update the logging etc.
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21166 auto* prerender_host = GetPrerenderHost();
Prudhvikumar Bommanae34ac672024-01-10 18:23:44167 CHECK(prerender_host);
168 CHECK_EQ(prerender_host->trigger_type(), PreloadingTriggerType::kEmbedder);
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21169 prerender_host->AddObserver(this);
Robert Lin624a0fe2021-12-08 22:58:30170}
Robert Linc37fb582021-11-11 03:18:47171
172PrerenderHandleImpl::~PrerenderHandleImpl() {
Jiacheng Guo4391a962025-07-30 02:14:20173 // GetPrerenderHost() fetches the PrerenderHost by the frame_tree_node_id_.
174 // If the underlying PrerenderHost is reused, frame_tree_node_id_ will
175 // be reset and prerender_host will be nullptr. The reused host will
176 // not be cancelled.
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21177 PrerenderHost* prerender_host = GetPrerenderHost();
178 if (!prerender_host) {
179 return;
Robert Linc37fb582021-11-11 03:18:47180 }
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21181 prerender_host->RemoveObserver(this);
182
183 prerender_host_registry_->CancelHost(frame_tree_node_id_,
184 PrerenderFinalStatus::kTriggerDestroyed);
Robert Linc37fb582021-11-11 03:18:47185}
186
Hiroki Nakagawa56dbf1472025-01-29 08:40:38187int32_t PrerenderHandleImpl::GetHandleId() const {
188 return handle_id_;
189}
190
Lingqi Chi60cff3252023-12-05 02:32:55191const GURL& PrerenderHandleImpl::GetInitialPrerenderingUrl() const {
Robert Lin624a0fe2021-12-08 22:58:30192 return prerendering_url_;
193}
194
Hiroki Nakagawae628b452025-03-29 13:08:06195const std::optional<net::HttpNoVarySearchData>&
196PrerenderHandleImpl::GetNoVarySearchHint() const {
197 return no_vary_search_hint_;
198}
199
Robert Lin6d825ba2022-02-10 03:35:40200base::WeakPtr<PrerenderHandle> PrerenderHandleImpl::GetWeakPtr() {
201 return weak_factory_.GetWeakPtr();
202}
203
Sreeja Kamishetty26e40d22022-10-17 17:54:35204void PrerenderHandleImpl::SetPreloadingAttemptFailureReason(
205 PreloadingFailureReason reason) {
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21206 auto* prerender_host = GetPrerenderHost();
207 if (!prerender_host || !prerender_host->preloading_attempt()) {
Simon Pelchatb8f226d2023-02-17 05:06:59208 return;
209 }
Sreeja Kamishetty26e40d22022-10-17 17:54:35210 prerender_host->preloading_attempt()->SetFailureReason(reason);
211}
212
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13213void PrerenderHandleImpl::AddActivationCallback(
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21214 base::OnceClosure activation_callback) {
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13215 CHECK_EQ(State::kValid, state_);
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21216 CHECK(activation_callback);
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13217 activation_callbacks_.push_back(std::move(activation_callback));
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21218}
219
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13220void PrerenderHandleImpl::AddErrorCallback(base::OnceClosure error_callback) {
221 CHECK_EQ(State::kValid, state_);
Hiroki Nakagawa1075a94f2025-01-16 12:26:53222 CHECK(error_callback);
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13223 error_callbacks_.push_back(std::move(error_callback));
224}
225
226bool PrerenderHandleImpl::IsValid() const {
227 switch (state_) {
228 case State::kValid:
229 return true;
230 case State::kActivated:
231 case State::kCanceled:
232 return false;
233 }
Hiroki Nakagawa1075a94f2025-01-16 12:26:53234}
235
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21236void PrerenderHandleImpl::OnActivated() {
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13237 CHECK_EQ(State::kValid, state_);
238 state_ = State::kActivated;
Hiroki Nakagawa1075a94f2025-01-16 12:26:53239
240 // An error should not be reported after activation.
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13241 error_callbacks_.clear();
Hiroki Nakagawa1075a94f2025-01-16 12:26:53242
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13243 std::vector<base::OnceClosure> callbacks;
244 callbacks.swap(activation_callbacks_);
245 // Don't touch `this` after this line, as a callback could destroy `this`.
246 for (auto& callback : callbacks) {
247 std::move(callback).Run();
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21248 }
249}
250
Hiroki Nakagawa1075a94f2025-01-16 12:26:53251void PrerenderHandleImpl::OnFailed(PrerenderFinalStatus status) {
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13252 CHECK_EQ(State::kValid, state_);
253 state_ = State::kCanceled;
254
255 // An activation never happen after cancellation.
256 activation_callbacks_.clear();
Hiroki Nakagawa1075a94f2025-01-16 12:26:53257
Hiroki Nakagawa86e06d3a2025-01-22 04:51:42258 if (!ShouldFireErrorCallback(status)) {
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13259 error_callbacks_.clear();
Hiroki Nakagawa86e06d3a2025-01-22 04:51:42260 return;
261 }
Hiroki Nakagawa1075a94f2025-01-16 12:26:53262
263 // TODO(crbug.com/41490450): Pass a cancellation reason to the callback.
264 // Note that we should not expose detailed reasons to prevent embedders from
265 // depending on them. Such an implicit contract with embedders would impair
266 // flexibility of internal implementation.
Hiroki Nakagawa3154fcfd2025-01-29 03:14:13267 std::vector<base::OnceClosure> callbacks;
268 callbacks.swap(error_callbacks_);
269 // Don't touch `this` after this line, as a callback could destroy `this`.
270 for (auto& callback : callbacks) {
271 std::move(callback).Run();
272 }
Hiroki Nakagawa1075a94f2025-01-16 12:26:53273}
274
Jiacheng Guo4391a962025-07-30 02:14:20275void PrerenderHandleImpl::OnHostReused() {
276 // Since the frame_tree_node_id_ is reused by the new PrerenderHost, we will
277 // stop tracking the FrameTree and reset frame_tree_node_id_.
278 // TODO(crbug.com/434826191): Add a new unique identifier for the
279 // PrerenderHost.
280 frame_tree_node_id_ = FrameTreeNodeId();
281}
282
Hiroki Nakagawab6b8b2e2024-12-04 01:24:21283PrerenderHost* PrerenderHandleImpl::GetPrerenderHost() {
284 auto* prerender_frame_tree_node =
285 FrameTreeNode::GloballyFindByID(frame_tree_node_id_);
286 if (!prerender_frame_tree_node) {
287 return nullptr;
288 }
289 PrerenderHost& prerender_host =
290 PrerenderHost::GetFromFrameTreeNode(*prerender_frame_tree_node);
291 return &prerender_host;
292}
293
Robert Linc37fb582021-11-11 03:18:47294} // namespace content