1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h"
9 #include "android_webview/browser/aw_contents_io_thread_client.h"
10 #include "android_webview/browser/aw_login_delegate.h"
11 #include "android_webview/browser/aw_resource_context.h"
12 #include "android_webview/common/url_constants.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/scoped_vector.h"
15 #include "components/auto_login_parser/auto_login_parser.h"
16 #include "components/navigation_interception/intercept_navigation_delegate.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/resource_controller.h"
19 #include "content/public/browser/resource_dispatcher_host.h"
20 #include "content/public/browser/resource_dispatcher_host_login_delegate.h"
21 #include "content/public/browser/resource_request_info.h"
22 #include "content/public/browser/resource_throttle.h"
23 #include "net/base/load_flags.h"
24 #include "net/base/net_errors.h"
25 #include "net/http/http_response_headers.h"
26 #include "net/url_request/url_request.h"
27 #include "url/url_constants.h"
29 using android_webview::AwContentsIoThreadClient
;
30 using content::BrowserThread
;
31 using content::ResourceType
;
32 using navigation_interception::InterceptNavigationDelegate
;
36 base::LazyInstance
<android_webview::AwResourceDispatcherHostDelegate
>
37 g_webview_resource_dispatcher_host_delegate
= LAZY_INSTANCE_INITIALIZER
;
39 void SetCacheControlFlag(
40 net::URLRequest
* request
, int flag
) {
41 const int all_cache_control_flags
= net::LOAD_BYPASS_CACHE
|
42 net::LOAD_VALIDATE_CACHE
|
43 net::LOAD_PREFERRING_CACHE
|
44 net::LOAD_ONLY_FROM_CACHE
;
45 DCHECK_EQ((flag
& all_cache_control_flags
), flag
);
46 int load_flags
= request
->load_flags();
47 load_flags
&= ~all_cache_control_flags
;
49 request
->SetLoadFlags(load_flags
);
54 namespace android_webview
{
56 // Calls through the IoThreadClient to check the embedders settings to determine
57 // if the request should be cancelled. There may not always be an IoThreadClient
58 // available for the |render_process_id|, |render_frame_id| pair (in the case of
59 // newly created pop up windows, for example) and in that case the request and
60 // the client callbacks will be deferred the request until a client is ready.
61 class IoThreadClientThrottle
: public content::ResourceThrottle
{
63 IoThreadClientThrottle(int render_process_id
,
65 net::URLRequest
* request
);
66 virtual ~IoThreadClientThrottle();
68 // From content::ResourceThrottle
69 virtual void WillStartRequest(bool* defer
) override
;
70 virtual void WillRedirectRequest(const GURL
& new_url
, bool* defer
) override
;
71 virtual const char* GetNameForLogging() const override
;
73 void OnIoThreadClientReady(int new_render_process_id
,
74 int new_render_frame_id
);
75 bool MaybeBlockRequest();
76 bool ShouldBlockRequest();
77 int render_process_id() const { return render_process_id_
; }
78 int render_frame_id() const { return render_frame_id_
; }
81 int render_process_id_
;
83 net::URLRequest
* request_
;
86 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id
,
88 net::URLRequest
* request
)
89 : render_process_id_(render_process_id
),
90 render_frame_id_(render_frame_id
),
93 IoThreadClientThrottle::~IoThreadClientThrottle() {
94 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
95 g_webview_resource_dispatcher_host_delegate
.Get().
96 RemovePendingThrottleOnIoThread(this);
99 const char* IoThreadClientThrottle::GetNameForLogging() const {
100 return "IoThreadClientThrottle";
103 void IoThreadClientThrottle::WillStartRequest(bool* defer
) {
104 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
105 if (render_frame_id_
< 1)
107 DCHECK(render_process_id_
);
110 // Defer all requests of a pop up that is still not associated with Java
111 // client so that the client will get a chance to override requests.
112 scoped_ptr
<AwContentsIoThreadClient
> io_client
=
113 AwContentsIoThreadClient::FromID(render_process_id_
, render_frame_id_
);
114 if (io_client
&& io_client
->PendingAssociation()) {
116 AwResourceDispatcherHostDelegate::AddPendingThrottle(
117 render_process_id_
, render_frame_id_
, this);
123 void IoThreadClientThrottle::WillRedirectRequest(const GURL
& new_url
,
125 WillStartRequest(defer
);
128 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id
,
129 int new_render_frame_id
) {
130 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
132 if (!MaybeBlockRequest()) {
133 controller()->Resume();
137 bool IoThreadClientThrottle::MaybeBlockRequest() {
138 if (ShouldBlockRequest()) {
139 controller()->CancelWithError(net::ERR_ACCESS_DENIED
);
145 bool IoThreadClientThrottle::ShouldBlockRequest() {
146 scoped_ptr
<AwContentsIoThreadClient
> io_client
=
147 AwContentsIoThreadClient::FromID(render_process_id_
, render_frame_id_
);
151 // Part of implementation of WebSettings.allowContentAccess.
152 if (request_
->url().SchemeIs(android_webview::kContentScheme
) &&
153 io_client
->ShouldBlockContentUrls()) {
157 // Part of implementation of WebSettings.allowFileAccess.
158 if (request_
->url().SchemeIsFile() &&
159 io_client
->ShouldBlockFileUrls()) {
160 const GURL
& url
= request_
->url();
161 if (!url
.has_path() ||
162 // Application's assets and resources are always available.
163 (url
.path().find(android_webview::kAndroidResourcePath
) != 0 &&
164 url
.path().find(android_webview::kAndroidAssetPath
) != 0)) {
169 if (io_client
->ShouldBlockNetworkLoads()) {
170 if (request_
->url().SchemeIs(url::kFtpScheme
)) {
173 SetCacheControlFlag(request_
, net::LOAD_ONLY_FROM_CACHE
);
175 AwContentsIoThreadClient::CacheMode cache_mode
= io_client
->GetCacheMode();
177 case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK
:
178 SetCacheControlFlag(request_
, net::LOAD_PREFERRING_CACHE
);
180 case AwContentsIoThreadClient::LOAD_NO_CACHE
:
181 SetCacheControlFlag(request_
, net::LOAD_BYPASS_CACHE
);
183 case AwContentsIoThreadClient::LOAD_CACHE_ONLY
:
184 SetCacheControlFlag(request_
, net::LOAD_ONLY_FROM_CACHE
);
194 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
195 content::ResourceDispatcherHost::Get()->SetDelegate(
196 &g_webview_resource_dispatcher_host_delegate
.Get());
199 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
200 : content::ResourceDispatcherHostDelegate() {
203 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
206 void AwResourceDispatcherHostDelegate::RequestBeginning(
207 net::URLRequest
* request
,
208 content::ResourceContext
* resource_context
,
209 content::AppCacheService
* appcache_service
,
210 ResourceType resource_type
,
211 ScopedVector
<content::ResourceThrottle
>* throttles
) {
213 AddExtraHeadersIfNeeded(request
, resource_context
);
215 const content::ResourceRequestInfo
* request_info
=
216 content::ResourceRequestInfo::ForRequest(request
);
218 // We always push the throttles here. Checking the existence of io_client
219 // is racy when a popup window is created. That is because RequestBeginning
220 // is called whether or not requests are blocked via BlockRequestForRoute()
221 // however io_client may or may not be ready at the time depending on whether
222 // webcontents is created.
223 throttles
->push_back(new IoThreadClientThrottle(
224 request_info
->GetChildID(), request_info
->GetRenderFrameID(), request
));
226 // We allow intercepting only navigations within main frames. This
227 // is used to post onPageStarted. We handle shouldOverrideUrlLoading
229 if (resource_type
== content::RESOURCE_TYPE_MAIN_FRAME
)
230 throttles
->push_back(InterceptNavigationDelegate::CreateThrottleFor(
234 void AwResourceDispatcherHostDelegate::OnRequestRedirected(
235 const GURL
& redirect_url
,
236 net::URLRequest
* request
,
237 content::ResourceContext
* resource_context
,
238 content::ResourceResponse
* response
) {
239 AddExtraHeadersIfNeeded(request
, resource_context
);
243 void AwResourceDispatcherHostDelegate::DownloadStarting(
244 net::URLRequest
* request
,
245 content::ResourceContext
* resource_context
,
249 bool is_content_initiated
,
251 ScopedVector
<content::ResourceThrottle
>* throttles
) {
252 GURL
url(request
->url());
253 std::string user_agent
;
254 std::string content_disposition
;
255 std::string mime_type
;
256 int64 content_length
= request
->GetExpectedContentSize();
258 request
->extra_request_headers().GetHeader(
259 net::HttpRequestHeaders::kUserAgent
, &user_agent
);
262 net::HttpResponseHeaders
* response_headers
= request
->response_headers();
263 if (response_headers
) {
264 response_headers
->GetNormalizedHeader("content-disposition",
265 &content_disposition
);
266 response_headers
->GetMimeType(&mime_type
);
271 const content::ResourceRequestInfo
* request_info
=
272 content::ResourceRequestInfo::ForRequest(request
);
274 scoped_ptr
<AwContentsIoThreadClient
> io_client
=
275 AwContentsIoThreadClient::FromID(
276 child_id
, request_info
->GetRenderFrameID());
278 // POST request cannot be repeated in general, so prevent client from
279 // retrying the same request, even if it is with a GET.
280 if ("GET" == request
->method() && io_client
) {
281 io_client
->NewDownload(url
,
289 content::ResourceDispatcherHostLoginDelegate
*
290 AwResourceDispatcherHostDelegate::CreateLoginDelegate(
291 net::AuthChallengeInfo
* auth_info
,
292 net::URLRequest
* request
) {
293 return new AwLoginDelegate(auth_info
, request
);
296 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL
& url
,
299 // The AwURLRequestJobFactory implementation should ensure this method never
305 void AwResourceDispatcherHostDelegate::OnResponseStarted(
306 net::URLRequest
* request
,
307 content::ResourceContext
* resource_context
,
308 content::ResourceResponse
* response
,
309 IPC::Sender
* sender
) {
310 const content::ResourceRequestInfo
* request_info
=
311 content::ResourceRequestInfo::ForRequest(request
);
313 DLOG(FATAL
) << "Started request without associated info: " <<
318 if (request_info
->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME
) {
319 // Check for x-auto-login header.
320 auto_login_parser::HeaderData header_data
;
321 if (auto_login_parser::ParserHeaderInResponse(
322 request
, auto_login_parser::ALLOW_ANY_REALM
, &header_data
)) {
323 scoped_ptr
<AwContentsIoThreadClient
> io_client
=
324 AwContentsIoThreadClient::FromID(request_info
->GetChildID(),
325 request_info
->GetRenderFrameID());
327 io_client
->NewLoginRequest(
328 header_data
.realm
, header_data
.account
, header_data
.args
);
334 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
335 IoThreadClientThrottle
* throttle
) {
336 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
337 PendingThrottleMap::iterator it
= pending_throttles_
.find(
338 FrameRouteIDPair(throttle
->render_process_id(),
339 throttle
->render_frame_id()));
340 if (it
!= pending_throttles_
.end()) {
341 pending_throttles_
.erase(it
);
346 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
347 int new_render_process_id
,
348 int new_render_frame_id
) {
349 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
351 &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal
,
353 g_webview_resource_dispatcher_host_delegate
.Pointer()),
354 new_render_process_id
, new_render_frame_id
));
358 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
359 int render_process_id
,
361 IoThreadClientThrottle
* pending_throttle
) {
362 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
364 &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread
,
366 g_webview_resource_dispatcher_host_delegate
.Pointer()),
367 render_process_id
, render_frame_id
, pending_throttle
));
370 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
371 int render_process_id
,
372 int render_frame_id_id
,
373 IoThreadClientThrottle
* pending_throttle
) {
374 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
375 pending_throttles_
.insert(
376 std::pair
<FrameRouteIDPair
, IoThreadClientThrottle
*>(
377 FrameRouteIDPair(render_process_id
, render_frame_id_id
),
381 void AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal(
382 int new_render_process_id
,
383 int new_render_frame_id
) {
384 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
385 PendingThrottleMap::iterator it
= pending_throttles_
.find(
386 FrameRouteIDPair(new_render_process_id
, new_render_frame_id
));
388 if (it
!= pending_throttles_
.end()) {
389 IoThreadClientThrottle
* throttle
= it
->second
;
390 throttle
->OnIoThreadClientReady(new_render_process_id
, new_render_frame_id
);
391 pending_throttles_
.erase(it
);
395 void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
396 net::URLRequest
* request
,
397 content::ResourceContext
* resource_context
) {
398 const content::ResourceRequestInfo
* request_info
=
399 content::ResourceRequestInfo::ForRequest(request
);
402 if (request_info
->GetResourceType() != content::RESOURCE_TYPE_MAIN_FRAME
)
405 const ui::PageTransition transition
= request_info
->GetPageTransition();
406 const bool is_load_url
=
407 transition
& ui::PAGE_TRANSITION_FROM_API
;
408 const bool is_go_back_forward
=
409 transition
& ui::PAGE_TRANSITION_FORWARD_BACK
;
410 const bool is_reload
= ui::PageTransitionCoreTypeIs(
411 transition
, ui::PAGE_TRANSITION_RELOAD
);
412 if (is_load_url
|| is_go_back_forward
|| is_reload
) {
413 AwResourceContext
* awrc
= static_cast<AwResourceContext
*>(resource_context
);
414 std::string extra_headers
= awrc
->GetExtraHeaders(request
->url());
415 if (!extra_headers
.empty()) {
416 net::HttpRequestHeaders headers
;
417 headers
.AddHeadersFromString(extra_headers
);
418 for (net::HttpRequestHeaders::Iterator
it(headers
); it
.GetNext(); ) {
419 request
->SetExtraRequestHeaderByName(it
.name(), it
.value(), false);
425 } // namespace android_webview