blob: 473acff0ab2a87e24189fd270808054a32285e3d [file] [log] [blame]
Avi Drissman4e1b7bc32022-09-15 14:03:501// Copyright 2022 The Chromium Authors
Rakina Zata Amniaf55b5b62022-07-19 23:11:032// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/browser/renderer_host/renderer_cancellation_throttle.h"
6
7#include "content/browser/renderer_host/frame_tree_node.h"
8#include "content/browser/renderer_host/navigation_request.h"
9
10namespace content {
11
12namespace {
13
Rakina Zata Amnidd1c9cd2022-10-21 08:35:5414// Default timeout for the cancellation window. From gathering data through the
Rakina Zata Amniaf55b5b62022-07-19 23:11:0315// "Navigation.RendererInitiatedCancellation.DeferStartToCancellationWindowEnd"
Rakina Zata Amnidd1c9cd2022-10-21 08:35:5416// UMA, 99% of navigations' cancellation window ends in under 2000ms, and all
17// cancellation windows end in under 10000ms, so setting this to 11000ms.
18constexpr base::TimeDelta kDefaultCancellationTimeout = base::Seconds(11);
Rakina Zata Amniaf55b5b62022-07-19 23:11:0319base::TimeDelta g_cancellation_timeout = kDefaultCancellationTimeout;
20
21} // namespace
22
23// static
Takashi Toyoshimaa6f3af1812025-05-29 04:45:5924void RendererCancellationThrottle::MaybeCreateAndAdd(
25 NavigationThrottleRegistry& registry) {
26 NavigationRequest* request =
27 NavigationRequest::From(&registry.GetNavigationHandle());
Rakina Zata Amniaf55b5b62022-07-19 23:11:0328 if (request->ShouldWaitForRendererCancellationWindowToEnd()) {
Takashi Toyoshimaa6f3af1812025-05-29 04:45:5929 registry.AddThrottle(
30 std::make_unique<RendererCancellationThrottle>(registry));
Rakina Zata Amniaf55b5b62022-07-19 23:11:0331 }
Rakina Zata Amniaf55b5b62022-07-19 23:11:0332}
33
34// static
35void RendererCancellationThrottle::SetCancellationTimeoutForTesting(
36 base::TimeDelta timeout) {
37 if (timeout.is_zero()) {
38 g_cancellation_timeout = kDefaultCancellationTimeout;
39 } else {
40 g_cancellation_timeout = timeout;
41 }
42}
43
44RendererCancellationThrottle::RendererCancellationThrottle(
Takashi Toyoshimaa6f3af1812025-05-29 04:45:5945 NavigationThrottleRegistry& registry)
46 : NavigationThrottle(registry) {}
Rakina Zata Amniaf55b5b62022-07-19 23:11:0347
48RendererCancellationThrottle::~RendererCancellationThrottle() = default;
49
50NavigationThrottle::ThrottleCheckResult
51RendererCancellationThrottle::WillProcessResponse() {
Rakina Zata Amnidf8d1b42024-07-26 09:21:2552 return WaitForRendererCancellationIfNeeded();
53}
54
55NavigationThrottle::ThrottleCheckResult
56RendererCancellationThrottle::WillCommitWithoutUrlLoader() {
57 return WaitForRendererCancellationIfNeeded();
58}
59
60NavigationThrottle::ThrottleCheckResult
61RendererCancellationThrottle::WaitForRendererCancellationIfNeeded() {
Rakina Zata Amniaf55b5b62022-07-19 23:11:0362 NavigationRequest* request = NavigationRequest::From(navigation_handle());
63 DCHECK(request);
64 if (request->renderer_cancellation_window_ended()) {
65 // The cancellation window had already ended, so the navigation doesn't need
66 // deferring.
67 return NavigationThrottle::PROCEED;
68 }
69
70 if (!request->GetRenderFrameHost() ||
Sharon Yanga8906842024-10-10 16:37:1271 request->GetRenderFrameHost()->GetSiteInstance()->group() !=
72 request->frame_tree_node()
73 ->current_frame_host()
74 ->GetSiteInstance()
75 ->group()) {
76 // Only defer same-SiteInstanceGroup navigations, as only those navigations
Rakina Zata Amniaf55b5b62022-07-19 23:11:0377 // were previously guaranteed to be cancelable from the same JS task it
Sharon Yanga8906842024-10-10 16:37:1278 // started on (see class level comment for more details).
Rakina Zata Amniaf55b5b62022-07-19 23:11:0379 return NavigationThrottle::PROCEED;
80 }
81
82 // Start the cancellation timeout, to warn users of an unresponsive renderer
83 // if the cancellation window is longer than the set time limit.
84 RestartTimeout();
85 // Wait for the navigation cancellation window to end before continuing.
86 request->set_renderer_cancellation_window_ended_callback(base::BindOnce(
87 &RendererCancellationThrottle::NavigationCancellationWindowEnded,
88 base::Unretained(this)));
89
90 return NavigationThrottle::DEFER;
91}
92
93void RendererCancellationThrottle::NavigationCancellationWindowEnded() {
94 CHECK(NavigationRequest::From(navigation_handle())
95 ->renderer_cancellation_window_ended());
Rakina Zata Amniaf55b5b62022-07-19 23:11:0396 // Stop the timeout and notify that renderer is responsive if necessary.
97 renderer_cancellation_timeout_timer_.Stop();
98 NavigationRequest* request = NavigationRequest::From(navigation_handle());
99 request->GetRenderFrameHost()->GetRenderWidgetHost()->RendererIsResponsive();
100
101 Resume();
102}
103
104void RendererCancellationThrottle::OnTimeout() {
105 // Warn that the renderer is unresponsive.
106 NavigationRequest* request = NavigationRequest::From(navigation_handle());
107 DCHECK(request);
Khushal Sagard7bdc382023-10-24 19:10:43108
109 auto* previous_rfh =
110 RenderFrameHostImpl::FromID(request->GetPreviousRenderFrameHostId());
111 if (!previous_rfh) {
112 return;
113 }
114
115 previous_rfh->GetRenderWidgetHost()->RendererIsUnresponsive(
Patrick Monette617db892024-09-24 17:29:19116 RenderWidgetHostImpl::RendererIsUnresponsiveReason::
117 kRendererCancellationThrottleTimeout,
Rakina Zata Amniaf55b5b62022-07-19 23:11:03118 base::BindRepeating(&RendererCancellationThrottle::RestartTimeout,
119 weak_factory_.GetWeakPtr()));
120}
121
122void RendererCancellationThrottle::RestartTimeout() {
123 renderer_cancellation_timeout_timer_.Start(
124 FROM_HERE, g_cancellation_timeout,
125 base::BindOnce(&RendererCancellationThrottle::OnTimeout,
126 base::Unretained(this)));
127}
128
129const char* RendererCancellationThrottle::GetNameForLogging() {
130 return "RendererCancellationThrottle";
131}
132
Khushal Sagard7bdc382023-10-24 19:10:43133} // namespace content