blob: f6c42544228f2cc5f84f401edfcb07879d9f5072 [file] [log] [blame]
Avi Drissman8ba1bad2022-09-13 19:22:361// Copyright 2012 The Chromium Authors
[email protected]4360ae72012-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
[email protected]62885ab2013-01-23 03:55:165#include "components/navigation_interception/intercept_navigation_delegate.h"
[email protected]4360ae72012-10-09 22:10:466
Gyuyoung Kimcb7965e2018-01-25 00:39:017#include <memory>
8
[email protected]4360ae72012-10-09 22:10:469#include "base/android/jni_android.h"
10#include "base/android/jni_string.h"
Sebastien Marchand53801a32019-01-25 16:26:1111#include "base/bind.h"
[email protected]4360ae72012-10-09 22:10:4612#include "base/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"
[email protected]4360ae72012-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"
[email protected]4360ae72012-10-09 22:10:4619#include "content/public/browser/render_view_host.h"
[email protected]4360ae72012-10-09 22:10:4620#include "content/public/browser/web_contents.h"
Michael Thiessen1a49e4d52022-12-02 21:54:4021#include "mojo/public/cpp/bindings/self_owned_receiver.h"
22#include "net/http/http_status_code.h"
23#include "net/http/http_util.h"
24#include "net/url_request/redirect_info.h"
25#include "net/url_request/redirect_util.h"
26#include "services/network/public/cpp/parsed_headers.h"
27#include "services/network/public/cpp/resource_request.h"
28#include "services/network/public/cpp/single_request_url_loader_factory.h"
29#include "services/network/public/mojom/url_response_head.mojom.h"
Michael Thiessenca245a382022-02-21 16:11:1730#include "url/android/gurl_android.h"
[email protected]e3b599e2013-07-05 07:15:1731#include "url/gurl.h"
[email protected]4360ae72012-10-09 22:10:4632
33using base::android::ConvertUTF8ToJavaString;
34using base::android::ScopedJavaLocalRef;
35using content::BrowserThread;
[email protected]4360ae72012-10-09 22:10:4636using content::RenderViewHost;
37using content::WebContents;
Michael Thiessenca245a382022-02-21 16:11:1738using ui::PageTransition;
[email protected]4360ae72012-10-09 22:10:4639
[email protected]8812e3d02013-05-22 12:38:5340namespace navigation_interception {
[email protected]4360ae72012-10-09 22:10:4641
42namespace {
43
thestig3b6a2f12015-09-25 08:17:2044const void* const kInterceptNavigationDelegateUserDataKey =
[email protected]4360ae72012-10-09 22:10:4645 &kInterceptNavigationDelegateUserDataKey;
46
Michael Thiessenca245a382022-02-21 16:11:1747bool CheckIfShouldIgnoreNavigationOnUIThread(
48 content::NavigationHandle* navigation_handle) {
mostynbad1e8c962015-03-25 21:51:1249 DCHECK_CURRENTLY_ON(BrowserThread::UI);
Michael Thiessenca245a382022-02-21 16:11:1750 DCHECK(navigation_handle);
[email protected]4360ae72012-10-09 22:10:4651
[email protected]4360ae72012-10-09 22:10:4652 InterceptNavigationDelegate* intercept_navigation_delegate =
Michael Thiessenca245a382022-02-21 16:11:1753 InterceptNavigationDelegate::Get(navigation_handle->GetWebContents());
[email protected]4360ae72012-10-09 22:10:4654 if (!intercept_navigation_delegate)
55 return false;
56
Michael Thiessenca245a382022-02-21 16:11:1757 return intercept_navigation_delegate->ShouldIgnoreNavigation(
58 navigation_handle);
[email protected]4360ae72012-10-09 22:10:4659}
60
Michael Thiessen1a49e4d52022-12-02 21:54:4061class RedirectURLLoader : public network::mojom::URLLoader {
62 public:
63 RedirectURLLoader(const GURL& url,
64 const network::ResourceRequest& resource_request,
65 mojo::PendingRemote<network::mojom::URLLoaderClient> client)
66 : client_(std::move(client)) {
67 net::HttpStatusCode response_code = net::HTTP_TEMPORARY_REDIRECT;
68 auto response_head = network::mojom::URLResponseHead::New();
69 response_head->encoded_data_length = 0;
70 response_head->headers = base::MakeRefCounted<net::HttpResponseHeaders>(
71 net::HttpUtil::AssembleRawHeaders("HTTP/1.1 307 Temporary Redirect"));
72
73 // Avoid a round-trip to the network service by pre-parsing headers.
74 // This doesn't violate: `docs/security/rule-of-2.md`, because the input is
75 // trusted, before appending the Location: <url> header.
76 response_head->parsed_headers =
77 network::PopulateParsedHeaders(response_head->headers.get(), url);
78
79 response_head->headers->AddHeader("Location", url.spec());
80
81 auto first_party_url_policy =
82 resource_request.update_first_party_url_on_redirect
83 ? net::RedirectInfo::FirstPartyURLPolicy::UPDATE_URL_ON_REDIRECT
84 : net::RedirectInfo::FirstPartyURLPolicy::NEVER_CHANGE_URL;
85
86 client_->OnReceiveRedirect(
87 net::RedirectInfo::ComputeRedirectInfo(
88 resource_request.method, resource_request.url,
89 resource_request.site_for_cookies, first_party_url_policy,
90 resource_request.referrer_policy, resource_request.referrer.spec(),
91 response_code, url, absl::nullopt,
92 /*insecure_scheme_was_upgraded=*/false,
93 /*copy_fragment=*/false),
94 std::move(response_head));
95 }
96
97 RedirectURLLoader(const RedirectURLLoader&) = delete;
98 RedirectURLLoader& operator=(const RedirectURLLoader&) = delete;
99
100 ~RedirectURLLoader() override = default;
101
102 private:
103 // network::mojom::URLLoader overrides:
104 void FollowRedirect(
105 const std::vector<std::string>& removed_headers,
106 const net::HttpRequestHeaders& modified_headers,
107 const net::HttpRequestHeaders& modified_cors_exempt_headers,
108 const absl::optional<GURL>& new_url) override {
109 NOTREACHED();
110 }
111 void SetPriority(net::RequestPriority priority,
112 int intra_priority_value) override {}
113 void PauseReadingBodyFromNet() override {}
114 void ResumeReadingBodyFromNet() override {}
115
116 mojo::Remote<network::mojom::URLLoaderClient> client_;
117};
118
119void RedirectToCallback(
120 GURL url,
121 const network::ResourceRequest& resource_request,
122 mojo::PendingReceiver<network::mojom::URLLoader> pending_receiver,
123 mojo::PendingRemote<network::mojom::URLLoaderClient> pending_client) {
124 mojo::MakeSelfOwnedReceiver(
125 std::make_unique<RedirectURLLoader>(url, resource_request,
126 std::move(pending_client)),
127 std::move(pending_receiver));
128}
129
[email protected]a8e69a742013-10-15 10:58:55130} // namespace
[email protected]4360ae72012-10-09 22:10:46131
132// static
133void InterceptNavigationDelegate::Associate(
134 WebContents* web_contents,
dcheng84c358e2016-04-26 07:05:53135 std::unique_ptr<InterceptNavigationDelegate> delegate) {
[email protected]4360ae72012-10-09 22:10:46136 web_contents->SetUserData(kInterceptNavigationDelegateUserDataKey,
avi8945fc92017-05-02 16:03:23137 std::move(delegate));
[email protected]4360ae72012-10-09 22:10:46138}
139
140// static
141InterceptNavigationDelegate* InterceptNavigationDelegate::Get(
142 WebContents* web_contents) {
dtrainor037df0d2014-10-08 18:05:24143 return static_cast<InterceptNavigationDelegate*>(
[email protected]4360ae72012-10-09 22:10:46144 web_contents->GetUserData(kInterceptNavigationDelegateUserDataKey));
145}
146
147// static
dcheng84c358e2016-04-26 07:05:53148std::unique_ptr<content::NavigationThrottle>
David Bokan2a48f7bb2021-07-09 13:21:36149InterceptNavigationDelegate::MaybeCreateThrottleFor(
Charlie Harrison3286ab72019-02-13 20:13:30150 content::NavigationHandle* handle,
151 navigation_interception::SynchronyMode mode) {
David Bokan2a48f7bb2021-07-09 13:21:36152 // Navigations in a subframe or non-primary frame tree should not be
153 // intercepted. As examples of a non-primary frame tree, a navigation
154 // occurring in a Portal element or an unactivated prerendering page should
155 // not launch an app.
156 // TODO(bokan): This is a bit of a stopgap approach since we won't run
157 // throttles again when the prerender is activated which means links that are
158 // prerendered will avoid launching an app intent that a regular navigation
159 // would have. Longer term we'll want prerender activation to check for app
160 // intents, or have this throttle cancel the prerender if an intent would
161 // have been launched (without launching the intent). It's also not clear
162 // what the right behavior for <portal> elements is.
163 // https://crbug.com/1227659.
164 if (!handle->IsInPrimaryMainFrame())
165 return nullptr;
166
Gyuyoung Kimcb7965e2018-01-25 00:39:01167 return std::make_unique<InterceptNavigationThrottle>(
Ken Rockotae24ce92019-12-19 20:00:25168 handle, base::BindRepeating(&CheckIfShouldIgnoreNavigationOnUIThread),
169 mode);
[email protected]4360ae72012-10-09 22:10:46170}
171
[email protected]4360ae72012-10-09 22:10:46172InterceptNavigationDelegate::InterceptNavigationDelegate(
Colin Blundell4695e8142020-03-16 11:13:12173 JNIEnv* env,
174 jobject jdelegate,
175 bool escape_external_handler_value)
176 : weak_jdelegate_(env, jdelegate),
177 escape_external_handler_value_(escape_external_handler_value) {}
[email protected]4360ae72012-10-09 22:10:46178
Michael Thiessen1a49e4d52022-12-02 21:54:40179InterceptNavigationDelegate::~InterceptNavigationDelegate() = default;
[email protected]4360ae72012-10-09 22:10:46180
181bool InterceptNavigationDelegate::ShouldIgnoreNavigation(
Michael Thiessenca245a382022-02-21 16:11:17182 content::NavigationHandle* navigation_handle) {
183 GURL escaped_url = escape_external_handler_value_
Ryan Hamilton7f3bd3d2022-04-23 00:07:39184 ? GURL(base::EscapeExternalHandlerValue(
Michael Thiessenca245a382022-02-21 16:11:17185 navigation_handle->GetURL().spec()))
186 : navigation_handle->GetURL();
Colin Blundell4695e8142020-03-16 11:13:12187
Michael Thiessenca245a382022-02-21 16:11:17188 if (!escaped_url.is_valid())
[email protected]4360ae72012-10-09 22:10:46189 return false;
190
191 JNIEnv* env = base::android::AttachCurrentThread();
192 ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
193
194 if (jdelegate.is_null())
195 return false;
196
[email protected]4360ae72012-10-09 22:10:46197 return Java_InterceptNavigationDelegate_shouldIgnoreNavigation(
Michael Thiessenca245a382022-02-21 16:11:17198 env, jdelegate, navigation_handle->GetJavaNavigationHandle(),
Michael Thiessen332dadb62022-07-13 14:44:07199 url::GURLAndroid::FromNativeGURL(env, escaped_url));
Michael Thiessenca245a382022-02-21 16:11:17200}
201
Michael Thiessen7cb129e2022-11-08 17:24:51202void InterceptNavigationDelegate::HandleSubframeExternalProtocol(
Michael Thiessenca245a382022-02-21 16:11:17203 const GURL& url,
204 ui::PageTransition page_transition,
205 bool has_user_gesture,
Michael Thiessen1a49e4d52022-12-02 21:54:40206 const absl::optional<url::Origin>& initiating_origin,
207 mojo::PendingRemote<network::mojom::URLLoaderFactory>* out_factory) {
Michael Thiessenca245a382022-02-21 16:11:17208 GURL escaped_url = escape_external_handler_value_
Ryan Hamilton7f3bd3d2022-04-23 00:07:39209 ? GURL(base::EscapeExternalHandlerValue(url.spec()))
Michael Thiessenca245a382022-02-21 16:11:17210 : url;
211 if (!escaped_url.is_valid())
212 return;
213
214 JNIEnv* env = base::android::AttachCurrentThread();
215 ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
216
217 if (jdelegate.is_null())
218 return;
Michael Thiessen1a49e4d52022-12-02 21:54:40219 ScopedJavaLocalRef<jobject> j_gurl =
220 Java_InterceptNavigationDelegate_handleSubframeExternalProtocol(
221 env, jdelegate, url::GURLAndroid::FromNativeGURL(env, escaped_url),
222 page_transition, has_user_gesture,
223 initiating_origin ? initiating_origin->CreateJavaObject() : nullptr);
224 if (j_gurl.is_null())
225 return;
226 std::unique_ptr<GURL> gurl = url::GURLAndroid::ToNativeGURL(env, j_gurl);
227
228 mojo::PendingReceiver<network::mojom::URLLoaderFactory> receiver =
229 out_factory->InitWithNewPipeAndPassReceiver();
230 scoped_refptr<network::SharedURLLoaderFactory> loader_factory =
231 base::MakeRefCounted<network::SingleRequestURLLoaderFactory>(
232 base::BindOnce(&RedirectToCallback, *gurl));
233 loader_factory->Clone(std::move(receiver));
[email protected]4360ae72012-10-09 22:10:46234}
235
Michael Thiessen332dadb62022-07-13 14:44:07236void InterceptNavigationDelegate::OnResourceRequestWithGesture() {
237 JNIEnv* env = base::android::AttachCurrentThread();
238 ScopedJavaLocalRef<jobject> jdelegate = weak_jdelegate_.get(env);
239 if (jdelegate.is_null())
240 return;
241 Java_InterceptNavigationDelegate_onResourceRequestWithGesture(env, jdelegate);
Michael Thiessene5663522022-05-25 21:23:28242}
243
[email protected]8812e3d02013-05-22 12:38:53244} // namespace navigation_interception