blob: 0c3cb511dfaed7ae18857dcbdedcd69ee5be9cbc [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
24std::unique_ptr<RendererCancellationThrottle>
25RendererCancellationThrottle::MaybeCreateThrottleFor(NavigationHandle* handle) {
26 NavigationRequest* request = NavigationRequest::From(handle);
27 if (request->ShouldWaitForRendererCancellationWindowToEnd()) {
28 return std::make_unique<RendererCancellationThrottle>(handle);
29 }
30 return nullptr;
31}
32
33// static
34void RendererCancellationThrottle::SetCancellationTimeoutForTesting(
35 base::TimeDelta timeout) {
36 if (timeout.is_zero()) {
37 g_cancellation_timeout = kDefaultCancellationTimeout;
38 } else {
39 g_cancellation_timeout = timeout;
40 }
41}
42
43RendererCancellationThrottle::RendererCancellationThrottle(
44 NavigationHandle* navigation_handle)
45 : NavigationThrottle(navigation_handle) {}
46
47RendererCancellationThrottle::~RendererCancellationThrottle() = default;
48
49NavigationThrottle::ThrottleCheckResult
50RendererCancellationThrottle::WillProcessResponse() {
Rakina Zata Amnidf8d1b42024-07-26 09:21:2551 return WaitForRendererCancellationIfNeeded();
52}
53
54NavigationThrottle::ThrottleCheckResult
55RendererCancellationThrottle::WillCommitWithoutUrlLoader() {
56 return WaitForRendererCancellationIfNeeded();
57}
58
59NavigationThrottle::ThrottleCheckResult
60RendererCancellationThrottle::WaitForRendererCancellationIfNeeded() {
Rakina Zata Amniaf55b5b62022-07-19 23:11:0361 NavigationRequest* request = NavigationRequest::From(navigation_handle());
62 DCHECK(request);
63 if (request->renderer_cancellation_window_ended()) {
64 // The cancellation window had already ended, so the navigation doesn't need
65 // deferring.
66 return NavigationThrottle::PROCEED;
67 }
68
69 if (!request->GetRenderFrameHost() ||
Sharon Yanga8906842024-10-10 16:37:1270 request->GetRenderFrameHost()->GetSiteInstance()->group() !=
71 request->frame_tree_node()
72 ->current_frame_host()
73 ->GetSiteInstance()
74 ->group()) {
75 // Only defer same-SiteInstanceGroup navigations, as only those navigations
Rakina Zata Amniaf55b5b62022-07-19 23:11:0376 // were previously guaranteed to be cancelable from the same JS task it
Sharon Yanga8906842024-10-10 16:37:1277 // started on (see class level comment for more details).
Rakina Zata Amniaf55b5b62022-07-19 23:11:0378 return NavigationThrottle::PROCEED;
79 }
80
81 // Start the cancellation timeout, to warn users of an unresponsive renderer
82 // if the cancellation window is longer than the set time limit.
83 RestartTimeout();
84 // Wait for the navigation cancellation window to end before continuing.
85 request->set_renderer_cancellation_window_ended_callback(base::BindOnce(
86 &RendererCancellationThrottle::NavigationCancellationWindowEnded,
87 base::Unretained(this)));
88
89 return NavigationThrottle::DEFER;
90}
91
92void RendererCancellationThrottle::NavigationCancellationWindowEnded() {
93 CHECK(NavigationRequest::From(navigation_handle())
94 ->renderer_cancellation_window_ended());
Rakina Zata Amniaf55b5b62022-07-19 23:11:0395 // Stop the timeout and notify that renderer is responsive if necessary.
96 renderer_cancellation_timeout_timer_.Stop();
97 NavigationRequest* request = NavigationRequest::From(navigation_handle());
98 request->GetRenderFrameHost()->GetRenderWidgetHost()->RendererIsResponsive();
99
100 Resume();
101}
102
103void RendererCancellationThrottle::OnTimeout() {
104 // Warn that the renderer is unresponsive.
105 NavigationRequest* request = NavigationRequest::From(navigation_handle());
106 DCHECK(request);
Khushal Sagard7bdc382023-10-24 19:10:43107
108 auto* previous_rfh =
109 RenderFrameHostImpl::FromID(request->GetPreviousRenderFrameHostId());
110 if (!previous_rfh) {
111 return;
112 }
113
114 previous_rfh->GetRenderWidgetHost()->RendererIsUnresponsive(
Patrick Monette617db892024-09-24 17:29:19115 RenderWidgetHostImpl::RendererIsUnresponsiveReason::
116 kRendererCancellationThrottleTimeout,
Rakina Zata Amniaf55b5b62022-07-19 23:11:03117 base::BindRepeating(&RendererCancellationThrottle::RestartTimeout,
118 weak_factory_.GetWeakPtr()));
119}
120
121void RendererCancellationThrottle::RestartTimeout() {
122 renderer_cancellation_timeout_timer_.Start(
123 FROM_HERE, g_cancellation_timeout,
124 base::BindOnce(&RendererCancellationThrottle::OnTimeout,
125 base::Unretained(this)));
126}
127
128const char* RendererCancellationThrottle::GetNameForLogging() {
129 return "RendererCancellationThrottle";
130}
131
Khushal Sagard7bdc382023-10-24 19:10:43132} // namespace content