[go: nahoru, domu]

blob: 2d863c31b46537c0deb9214dfbf5580e608d1d6e [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2012 The Chromium Authors
mkosiba@chromium.org4360ae72012-10-09 22:10:462// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
kaiwang@chromium.org62885ab2013-01-23 03:55:165#include "components/navigation_interception/intercept_navigation_delegate.h"
mkosiba@chromium.org4360ae72012-10-09 22:10:466
Gyuyoung Kimcb7965e2018-01-25 00:39:017#include <memory>
8
mkosiba@chromium.org4360ae72012-10-09 22:10:469#include "base/android/jni_android.h"
10#include "base/android/jni_string.h"
Avi Drissman12be0312023-01-11 09:16:0911#include "base/functional/bind.h"
12#include "base/functional/callback.h"
Ryan Hamilton7f3bd3d2022-04-23 00:07:3913#include "base/strings/escape.h"
Mohamed Heikalbd641312019-06-22 00:14:3714#include "components/navigation_interception/jni_headers/InterceptNavigationDelegate_jni.h"
mkosiba@chromium.org4360ae72012-10-09 22:10:4615#include "content/public/browser/browser_thread.h"
David Bokan2a48f7bb2021-07-09 13:21:3616#include "content/public/browser/navigation_handle.h"
clamy40c9e142015-09-29 11:18:4717#include "content/public/browser/navigation_throttle.h"
jaekyun038903192015-03-31 14:15:5918#include "content/public/browser/render_frame_host.h"
mkosiba@chromium.org4360ae72012-10-09 22:10:4619#include "content/public/browser/render_view_host.h"
20#include "content/public/browser/web_contents.h"
Michael Thiessen41821c982023-08-14 21:45:5421#include "content/public/common/page_visibility_state.h"
Michael Thiessen1a49e4d2022-12-02 21:54:4022#include "mojo/public/cpp/bindings/self_owned_receiver.h"
23#include "net/http/http_status_code.h"
24#include "net/http/http_util.h"
25#include "net/url_request/redirect_info.h"
26#include "net/url_request/redirect_util.h"
27#include "services/network/public/cpp/parsed_headers.h"
28#include "services/network/public/cpp/resource_request.h"
29#include "services/network/public/cpp/single_request_url_loader_factory.h"
30#include "services/network/public/mojom/url_response_head.mojom.h"
Michael Thiessenca245a382022-02-21 16:11:1731#include "url/android/gurl_android.h"
tfarina@chromium.orge3b599e2013-07-05 07:15:1732#include "url/gurl.h"
mkosiba@chromium.org4360ae72012-10-09 22:10:4633
34using base::android::ConvertUTF8ToJavaString;
35using base::android::ScopedJavaLocalRef;
36using content::BrowserThread;
37using content::RenderViewHost;
38using content::WebContents;
Michael Thiessenca245a382022-02-21 16:11:1739using ui::PageTransition;
mkosiba@chromium.org4360ae72012-10-09 22:10:4640
tfarina@chromium.org8812e3d02013-05-22 12:38:5341namespace navigation_interception {
mkosiba@chromium.org4360ae72012-10-09 22:10:4642
43namespace {
44
thestig3b6a2f12015-09-25 08:17:2045const void* const kInterceptNavigationDelegateUserDataKey =
mkosiba@chromium.org4360ae72012-10-09 22:10:4646 &kInterceptNavigationDelegateUserDataKey;
47
Michael Thiessenca245a382022-02-21 16:11:1748bool CheckIfShouldIgnoreNavigationOnUIThread(
49 content::NavigationHandle* navigation_handle) {
mostynbad1e8c962015-03-25 21:51:1250 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Michael Thiessenca245a382022-02-21 16:11:1751 DCHECK(navigation_handle);
mkosiba@chromium.org4360ae72012-10-09 22:10:4652
mkosiba@chromium.org4360ae72012-10-09 22:10:4653 InterceptNavigationDelegate* intercept_navigation_delegate =
Michael Thiessenca245a382022-02-21 16:11:1754 InterceptNavigationDelegate::Get(navigation_handle->GetWebContents());
mkosiba@chromium.org4360ae72012-10-09 22:10:4655 if (!intercept_navigation_delegate)
56 return false;
57
Michael Thiessenca245a382022-02-21 16:11:1758 return intercept_navigation_delegate->ShouldIgnoreNavigation(
59 navigation_handle);
mkosiba@chromium.org4360ae72012-10-09 22:10:4660}
61
Michael Thiessen1a49e4d2022-12-02 21:54:4062class RedirectURLLoader : public network::mojom::URLLoader {
63 public:
Michael Thiessen7b531172023-01-28 05:25:5964 RedirectURLLoader(const network::ResourceRequest& resource_request,
Michael Thiessen1a49e4d2022-12-02 21:54:4065 mojo::PendingRemote<network::mojom::URLLoaderClient> client)
Michael Thiessen7b531172023-01-28 05:25:5966 : client_(std::move(client)), request_(resource_request) {}
67
68 void DoRedirect(std::unique_ptr<GURL> url) {
Michael Thiessen1a49e4d2022-12-02 21:54:4069 net::HttpStatusCode response_code = net::HTTP_TEMPORARY_REDIRECT;
70 auto response_head = network::mojom::URLResponseHead::New();
71 response_head->encoded_data_length = 0;
72 response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
73 net::HttpUtil::AssembleRawHeaders("HTTP/1.1 307 Temporary Redirect"));
74
75 // Avoid a round-trip to the network service by pre-parsing headers.
76 // This doesn't violate: `docs/security/rule-of-2.md`, because the input is
77 // trusted, before appending the Location: <url> header.
78 response_head->parsed_headers =
Michael Thiessen7b531172023-01-28 05:25:5979 network::PopulateParsedHeaders(response_head->headers.get(), *url);
Michael Thiessen1a49e4d2022-12-02 21:54:4080
Michael Thiessen7b531172023-01-28 05:25:5981 response_head->headers->AddHeader("Location", url->spec());
Michael Thiessen1a49e4d2022-12-02 21:54:4082
83 auto first_party_url_policy =
Michael Thiessen7b531172023-01-28 05:25:5984 request_.update_first_party_url_on_redirect
Michael Thiessen1a49e4d2022-12-02 21:54:4085 ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT
86 : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL;
87
88 client_->OnReceiveRedirect(
89 net::RedirectInfo::ComputeRedirectInfo(
Michael Thiessen7b531172023-01-28 05:25:5990 request_.method, request_.url, request_.site_for_cookies,
91 first_party_url_policy, request_.referrer_policy,
Arthur Sonzognic571efb2024-01-26 20:26:1892 request_.referrer.spec(), response_code, *url, std::nullopt,
Michael Thiessen1a49e4d2022-12-02 21:54:4093 /*insecure_scheme_was_upgraded=*/false,
94 /*copy_fragment=*/false),
95 std::move(response_head));
96 }
97
Michael Thiessen7b531172023-01-28 05:25:5998 void OnNonRedirectAsyncAction() {
99 client_->OnComplete(network::URLLoaderCompletionStatus(net::ERR_ABORTED));
100 }
101
Michael Thiessen1a49e4d2022-12-02 21:54:40102 RedirectURLLoader(const RedirectURLLoader&) = delete;
103 RedirectURLLoader& operator=(const RedirectURLLoader&) = delete;
104
105 ~RedirectURLLoader() override = default;
106
107 private:
108 // network::mojom::URLLoader overrides:
109 void FollowRedirect(
110 const std::vector<std::string>& removed_headers,
111 const net::HttpRequestHeaders& modified_headers,
112 const net::HttpRequestHeaders& modified_cors_exempt_headers,
Arthur Sonzognic571efb2024-01-26 20:26:18113 const std::optional<GURL>& new_url) override {
Michael Thiessen1a49e4d2022-12-02 21:54:40114 NOTREACHED();
115 }
116 void SetPriority(net::RequestPriority priority,
117 int intra_priority_value) override {}
118 void PauseReadingBodyFromNet() override {}
119 void ResumeReadingBodyFromNet() override {}
120
121 mojo::Remote<network::mojom::URLLoaderClient> client_;
Michael Thiessen7b531172023-01-28 05:25:59122 network::ResourceRequest request_;
Michael Thiessen1a49e4d2022-12-02 21:54:40123};
124
thestig@chromium.orga8e69a742013-10-15 10:58:55125} // namespace
mkosiba@chromium.org4360ae72012-10-09 22:10:46126
127// static
128void InterceptNavigationDelegate::Associate(
129 WebContents* web_contents,
dcheng84c358e2016-04-26 07:05:53130 std::unique_ptr<InterceptNavigationDelegate> delegate) {
mkosiba@chromium.org4360ae72012-10-09 22:10:46131 web_contents->SetUserData(kInterceptNavigationDelegateUserDataKey,
avi8945fc92017-05-02 16:03:23132 std::move(delegate));
mkosiba@chromium.org4360ae72012-10-09 22:10:46133}
134
135// static
136InterceptNavigationDelegate* InterceptNavigationDelegate::Get(
137 WebContents* web_contents) {
dtrainor037df0d2014-10-08 18:05:24138 return static_cast<InterceptNavigationDelegate*>(
mkosiba@chromium.org4360ae72012-10-09 22:10:46139 web_contents->GetUserData(kInterceptNavigationDelegateUserDataKey));
140}
141
142// static
dcheng84c358e2016-04-26 07:05:53143std::unique_ptr<content::NavigationThrottle>
David Bokan2a48f7bb2021-07-09 13:21:36144InterceptNavigationDelegate::MaybeCreateThrottleFor(
Charlie Harrison3286ab72019-02-13 20:13:30145 content::NavigationHandle* handle,
146 navigation_interception::SynchronyMode mode) {
David Bokan2a48f7bb2021-07-09 13:21:36147 // Navigations in a subframe or non-primary frame tree should not be
148 // intercepted. As examples of a non-primary frame tree, a navigation
149 // occurring in a Portal element or an unactivated prerendering page should
150 // not launch an app.
151 // TODO(bokan): This is a bit of a stopgap approach since we won't run
152 // throttles again when the prerender is activated which means links that are
153 // prerendered will avoid launching an app intent that a regular navigation
154 // would have. Longer term we'll want prerender activation to check for app
155 // intents, or have this throttle cancel the prerender if an intent would
156 // have been launched (without launching the intent). It's also not clear
157 // what the right behavior for <portal> elements is.
158 // https://crbug.com/1227659.
159 if (!handle->IsInPrimaryMainFrame())
160 return nullptr;
161
Gyuyoung Kimcb7965e2018-01-25 00:39:01162 return std::make_unique<InterceptNavigationThrottle>(
Ken Rockotae24ce92019-12-19 20:00:25163 handle, base::BindRepeating(&CheckIfShouldIgnoreNavigationOnUIThread),
164 mode);
mkosiba@chromium.org4360ae72012-10-09 22:10:46165}
166
167InterceptNavigationDelegate::InterceptNavigationDelegate(
Colin Blundell4695e8142020-03-16 11:13:12168 JNIEnv* env,
169 jobject jdelegate,
170 bool escape_external_handler_value)
171 : weak_jdelegate_(env, jdelegate),
172 escape_external_handler_value_(escape_external_handler_value) {}
mkosiba@chromium.org4360ae72012-10-09 22:10:46173
Michael Thiessen1a49e4d2022-12-02 21:54:40174InterceptNavigationDelegate::~InterceptNavigationDelegate() = default;
mkosiba@chromium.org4360ae72012-10-09 22:10:46175
176bool InterceptNavigationDelegate::ShouldIgnoreNavigation(
Michael Thiessenca245a382022-02-21 16:11:17177 content::NavigationHandle* navigation_handle) {
178 GURL escaped_url = escape_external_handler_value_
Ryan Hamilton7f3bd3d2022-04-23 00:07:39179 ? GURL(base::EscapeExternalHandlerValue(
Michael Thiessenca245a382022-02-21 16:11:17180 navigation_handle->GetURL().spec()))
181 : navigation_handle->GetURL();
Colin Blundell4695e8142020-03-16 11:13:12182
Michael Thiessenca245a382022-02-21 16:11:17183 if (!escaped_url.is_valid())
mkosiba@chromium.org4360ae72012-10-09 22:10:46184 return false;
185
186 JNIEnv* env = base::android::AttachCurrentThread();
187 ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
188
189 if (jdelegate.is_null())
190 return false;
191
Michael Thiessen41821c982023-08-14 21:45:54192 bool hidden_cross_frame = false;
Michael Thiessen884e6172023-02-13 17:50:31193 // Only main frame navigations use this path, so we only need to check if the
194 // navigation is cross-frame to the main frame.
Michael Thiessen41821c982023-08-14 21:45:54195 if (navigation_handle->GetInitiatorFrameToken() &&
196 navigation_handle->GetInitiatorFrameToken() !=
197 navigation_handle->GetWebContents()
198 ->GetPrimaryMainFrame()
199 ->GetFrameToken()) {
200 content::RenderFrameHost* initiator_frame_host =
201 content::RenderFrameHost::FromFrameToken(
Dave Tapuskae9b7c0f72023-11-06 16:38:01202 content::GlobalRenderFrameHostToken(
203 navigation_handle->GetInitiatorProcessId(),
204 navigation_handle->GetInitiatorFrameToken().value()));
Michael Thiessen41821c982023-08-14 21:45:54205 // If the initiator is gone treat it as not visible.
206 hidden_cross_frame =
207 !initiator_frame_host || initiator_frame_host->GetVisibilityState() !=
208 content::PageVisibilityState::kVisible;
209 }
Michael Thiessen884e6172023-02-13 17:50:31210
Michael Thiessen8fd3520b2023-03-10 20:55:47211 // We don't care which sandbox flags are present, only that any sandbox flags
212 // are present, as we don't support persisting sandbox flags through fallback
213 // URL navigation.
214 bool is_sandboxed = navigation_handle->SandboxFlagsInherited() !=
Michael Thiessend80a1af12023-08-21 17:17:35215 network::mojom::WebSandboxFlags::kNone ||
216 navigation_handle->SandboxFlagsInitiator() !=
217 network::mojom::WebSandboxFlags::kNone;
Arthur Sonzognidc8508ab2023-08-17 08:42:42218
mkosiba@chromium.org4360ae72012-10-09 22:10:46219 return Java_InterceptNavigationDelegate_shouldIgnoreNavigation(
Michael Thiessenca245a382022-02-21 16:11:17220 env, jdelegate, navigation_handle->GetJavaNavigationHandle(),
Michael Thiessen41821c982023-08-14 21:45:54221 url::GURLAndroid::FromNativeGURL(env, escaped_url), hidden_cross_frame,
Michael Thiessen8fd3520b2023-03-10 20:55:47222 is_sandboxed);
Michael Thiessenca245a382022-02-21 16:11:17223}
224
Michael Thiessen7cb129e2022-11-08 17:24:51225void InterceptNavigationDelegate::HandleSubframeExternalProtocol(
Michael Thiessenca245a382022-02-21 16:11:17226 const GURL& url,
227 ui::PageTransition page_transition,
228 bool has_user_gesture,
Arthur Sonzognic571efb2024-01-26 20:26:18229 const std::optional<url::Origin>& initiating_origin,
Michael Thiessen1a49e4d2022-12-02 21:54:40230 mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
Michael Thiessen7b531172023-01-28 05:25:59231 // If there's a pending async subframe action, don't consider external
232 // navigation for the current navigation.
233 if (subframe_redirect_url_ || url_loader_) {
234 return;
235 }
236
Michael Thiessenca245a382022-02-21 16:11:17237 GURL escaped_url = escape_external_handler_value_
Ryan Hamilton7f3bd3d2022-04-23 00:07:39238 ? GURL(base::EscapeExternalHandlerValue(url.spec()))
Michael Thiessenca245a382022-02-21 16:11:17239 : url;
240 if (!escaped_url.is_valid())
241 return;
242
243 JNIEnv* env = base::android::AttachCurrentThread();
244 ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
245
246 if (jdelegate.is_null())
247 return;
Michael Thiessen1a49e4d2022-12-02 21:54:40248 ScopedJavaLocalRef<jobject> j_gurl =
249 Java_InterceptNavigationDelegate_handleSubframeExternalProtocol(
250 env, jdelegate, url::GURLAndroid::FromNativeGURL(env, escaped_url),
251 page_transition, has_user_gesture,
Andrew Grieve5cec6392023-09-06 14:46:01252 initiating_origin ? initiating_origin->ToJavaObject() : nullptr);
Michael Thiessen1a49e4d2022-12-02 21:54:40253 if (j_gurl.is_null())
254 return;
Michael Thiessen7b531172023-01-28 05:25:59255 subframe_redirect_url_ = url::GURLAndroid::ToNativeGURL(env, j_gurl);
Michael Thiessen1a49e4d2022-12-02 21:54:40256
257 mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver =
258 out_factory->InitWithNewPipeAndPassReceiver();
259 scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
260 base::MakeRefCounted<network::SingleRequestURLLoaderFactory>(
Michael Thiessen7b531172023-01-28 05:25:59261 base::BindOnce(&InterceptNavigationDelegate::LoaderCallback,
262 weak_ptr_factory_.GetWeakPtr()));
Michael Thiessen1a49e4d2022-12-02 21:54:40263 loader_factory->Clone(std::move(receiver));
mkosiba@chromium.org4360ae72012-10-09 22:10:46264}
265
Michael Thiessen7b531172023-01-28 05:25:59266void InterceptNavigationDelegate::LoaderCallback(
267 const network::ResourceRequest& resource_request,
268 mojo::PendingReceiver<network::mojom::URLLoader> pending_receiver,
269 mojo::PendingRemote<network::mojom::URLLoaderClient> pending_client) {
270 url_loader_ = mojo::MakeSelfOwnedReceiver(
271 std::make_unique<RedirectURLLoader>(resource_request,
272 std::move(pending_client)),
273 std::move(pending_receiver));
274 MaybeHandleSubframeAction();
275}
276
277void InterceptNavigationDelegate::MaybeHandleSubframeAction() {
278 // An empty subframe_redirect_url_ implies a pending async action.
279 if (!url_loader_ ||
280 (subframe_redirect_url_ && subframe_redirect_url_->is_empty())) {
281 return;
282 }
283 RedirectURLLoader* loader =
284 static_cast<RedirectURLLoader*>(url_loader_->impl());
285 if (!subframe_redirect_url_) {
286 loader->OnNonRedirectAsyncAction();
287 } else {
288 loader->DoRedirect(std::move(subframe_redirect_url_));
289 }
290 url_loader_.reset();
291}
292
Michael Thiessen332dadb62022-07-13 14:44:07293void InterceptNavigationDelegate::OnResourceRequestWithGesture() {
294 JNIEnv* env = base::android::AttachCurrentThread();
295 ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
296 if (jdelegate.is_null())
297 return;
298 Java_InterceptNavigationDelegate_onResourceRequestWithGesture(env, jdelegate);
Michael Thiessene5663522022-05-25 21:23:28299}
300
Michael Thiessen7b531172023-01-28 05:25:59301void InterceptNavigationDelegate::OnSubframeAsyncActionTaken(
302 JNIEnv* env,
303 const base::android::JavaParamRef<jobject>& j_gurl) {
304 // subframe_redirect_url_ no longer empty indicates the async action has been
305 // taken.
306 subframe_redirect_url_ =
307 j_gurl.is_null() ? nullptr : url::GURLAndroid::ToNativeGURL(env, j_gurl);
308 MaybeHandleSubframeAction();
309}
310
tfarina@chromium.org8812e3d02013-05-22 12:38:53311} // namespace navigation_interception