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((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 // TODO(sgurun): This block can be removed when crbug.com/277937 is fixed.
106 if (render_frame_id_
< 1) {
107 // OPTIONS is used for preflighted requests which are generated internally.
108 DCHECK_EQ("OPTIONS", request_
->method());
111 DCHECK(render_process_id_
);
114 // Defer all requests of a pop up that is still not associated with Java
115 // client so that the client will get a chance to override requests.
116 scoped_ptr
<AwContentsIoThreadClient
> io_client
=
117 AwContentsIoThreadClient::FromID(render_process_id_
, render_frame_id_
);
118 if (io_client
&& io_client
->PendingAssociation()) {
120 AwResourceDispatcherHostDelegate::AddPendingThrottle(
121 render_process_id_
, render_frame_id_
, this);
127 void IoThreadClientThrottle::WillRedirectRequest(const GURL
& new_url
,
129 WillStartRequest(defer
);
132 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id
,
133 int new_render_frame_id
) {
134 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
136 if (!MaybeBlockRequest()) {
137 controller()->Resume();
141 bool IoThreadClientThrottle::MaybeBlockRequest() {
142 if (ShouldBlockRequest()) {
143 controller()->CancelWithError(net::ERR_ACCESS_DENIED
);
149 bool IoThreadClientThrottle::ShouldBlockRequest() {
150 scoped_ptr
<AwContentsIoThreadClient
> io_client
=
151 AwContentsIoThreadClient::FromID(render_process_id_
, render_frame_id_
);
155 // Part of implementation of WebSettings.allowContentAccess.
156 if (request_
->url().SchemeIs(android_webview::kContentScheme
) &&
157 io_client
->ShouldBlockContentUrls()) {
161 // Part of implementation of WebSettings.allowFileAccess.
162 if (request_
->url().SchemeIsFile() &&
163 io_client
->ShouldBlockFileUrls()) {
164 const GURL
& url
= request_
->url();
165 if (!url
.has_path() ||
166 // Application's assets and resources are always available.
167 (url
.path().find(android_webview::kAndroidResourcePath
) != 0 &&
168 url
.path().find(android_webview::kAndroidAssetPath
) != 0)) {
173 if (io_client
->ShouldBlockNetworkLoads()) {
174 if (request_
->url().SchemeIs(url::kFtpScheme
)) {
177 SetCacheControlFlag(request_
, net::LOAD_ONLY_FROM_CACHE
);
179 AwContentsIoThreadClient::CacheMode cache_mode
= io_client
->GetCacheMode();
181 case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK
:
182 SetCacheControlFlag(request_
, net::LOAD_PREFERRING_CACHE
);
184 case AwContentsIoThreadClient::LOAD_NO_CACHE
:
185 SetCacheControlFlag(request_
, net::LOAD_BYPASS_CACHE
);
187 case AwContentsIoThreadClient::LOAD_CACHE_ONLY
:
188 SetCacheControlFlag(request_
, net::LOAD_ONLY_FROM_CACHE
);
198 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
199 content::ResourceDispatcherHost::Get()->SetDelegate(
200 &g_webview_resource_dispatcher_host_delegate
.Get());
203 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
204 : content::ResourceDispatcherHostDelegate() {
207 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
210 void AwResourceDispatcherHostDelegate::RequestBeginning(
211 net::URLRequest
* request
,
212 content::ResourceContext
* resource_context
,
213 content::AppCacheService
* appcache_service
,
214 ResourceType::Type resource_type
,
217 ScopedVector
<content::ResourceThrottle
>* throttles
) {
219 AddExtraHeadersIfNeeded(request
, resource_context
);
221 const content::ResourceRequestInfo
* request_info
=
222 content::ResourceRequestInfo::ForRequest(request
);
224 // We always push the throttles here. Checking the existence of io_client
225 // is racy when a popup window is created. That is because RequestBeginning
226 // is called whether or not requests are blocked via BlockRequestForRoute()
227 // however io_client may or may not be ready at the time depending on whether
228 // webcontents is created.
229 throttles
->push_back(new IoThreadClientThrottle(
230 child_id
, request_info
->GetRenderFrameID(), request
));
232 // We allow intercepting only navigations within main frames. This
233 // is used to post onPageStarted. We handle shouldOverrideUrlLoading
235 if (resource_type
== ResourceType::MAIN_FRAME
)
236 throttles
->push_back(InterceptNavigationDelegate::CreateThrottleFor(
240 void AwResourceDispatcherHostDelegate::OnRequestRedirected(
241 const GURL
& redirect_url
,
242 net::URLRequest
* request
,
243 content::ResourceContext
* resource_context
,
244 content::ResourceResponse
* response
) {
245 AddExtraHeadersIfNeeded(request
, resource_context
);
249 void AwResourceDispatcherHostDelegate::DownloadStarting(
250 net::URLRequest
* request
,
251 content::ResourceContext
* resource_context
,
255 bool is_content_initiated
,
257 ScopedVector
<content::ResourceThrottle
>* throttles
) {
258 GURL
url(request
->url());
259 std::string user_agent
;
260 std::string content_disposition
;
261 std::string mime_type
;
262 int64 content_length
= request
->GetExpectedContentSize();
264 request
->extra_request_headers().GetHeader(
265 net::HttpRequestHeaders::kUserAgent
, &user_agent
);
268 net::HttpResponseHeaders
* response_headers
= request
->response_headers();
269 if (response_headers
) {
270 response_headers
->GetNormalizedHeader("content-disposition",
271 &content_disposition
);
272 response_headers
->GetMimeType(&mime_type
);
277 const content::ResourceRequestInfo
* request_info
=
278 content::ResourceRequestInfo::ForRequest(request
);
280 scoped_ptr
<AwContentsIoThreadClient
> io_client
=
281 AwContentsIoThreadClient::FromID(
282 child_id
, request_info
->GetRenderFrameID());
284 // POST request cannot be repeated in general, so prevent client from
285 // retrying the same request, even if it is with a GET.
286 if ("GET" == request
->method() && io_client
) {
287 io_client
->NewDownload(url
,
295 content::ResourceDispatcherHostLoginDelegate
*
296 AwResourceDispatcherHostDelegate::CreateLoginDelegate(
297 net::AuthChallengeInfo
* auth_info
,
298 net::URLRequest
* request
) {
299 return new AwLoginDelegate(auth_info
, request
);
302 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(
306 bool initiated_by_user_gesture
) {
307 // The AwURLRequestJobFactory implementation should ensure this method never
313 void AwResourceDispatcherHostDelegate::OnResponseStarted(
314 net::URLRequest
* request
,
315 content::ResourceContext
* resource_context
,
316 content::ResourceResponse
* response
,
317 IPC::Sender
* sender
) {
318 const content::ResourceRequestInfo
* request_info
=
319 content::ResourceRequestInfo::ForRequest(request
);
321 DLOG(FATAL
) << "Started request without associated info: " <<
326 if (request_info
->GetResourceType() == ResourceType::MAIN_FRAME
) {
327 // Check for x-auto-login header.
328 auto_login_parser::HeaderData header_data
;
329 if (auto_login_parser::ParserHeaderInResponse(
330 request
, auto_login_parser::ALLOW_ANY_REALM
, &header_data
)) {
331 scoped_ptr
<AwContentsIoThreadClient
> io_client
=
332 AwContentsIoThreadClient::FromID(request_info
->GetChildID(),
333 request_info
->GetRenderFrameID());
335 io_client
->NewLoginRequest(
336 header_data
.realm
, header_data
.account
, header_data
.args
);
342 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
343 IoThreadClientThrottle
* throttle
) {
344 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
345 PendingThrottleMap::iterator it
= pending_throttles_
.find(
346 FrameRouteIDPair(throttle
->render_process_id(),
347 throttle
->render_frame_id()));
348 if (it
!= pending_throttles_
.end()) {
349 pending_throttles_
.erase(it
);
354 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
355 int new_render_process_id
,
356 int new_render_frame_id
) {
357 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
359 &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal
,
361 g_webview_resource_dispatcher_host_delegate
.Pointer()),
362 new_render_process_id
, new_render_frame_id
));
366 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
367 int render_process_id
,
369 IoThreadClientThrottle
* pending_throttle
) {
370 BrowserThread::PostTask(BrowserThread::IO
, FROM_HERE
,
372 &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread
,
374 g_webview_resource_dispatcher_host_delegate
.Pointer()),
375 render_process_id
, render_frame_id
, pending_throttle
));
378 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
379 int render_process_id
,
380 int render_frame_id_id
,
381 IoThreadClientThrottle
* pending_throttle
) {
382 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
383 pending_throttles_
.insert(
384 std::pair
<FrameRouteIDPair
, IoThreadClientThrottle
*>(
385 FrameRouteIDPair(render_process_id
, render_frame_id_id
),
389 void AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal(
390 int new_render_process_id
,
391 int new_render_frame_id
) {
392 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO
));
393 PendingThrottleMap::iterator it
= pending_throttles_
.find(
394 FrameRouteIDPair(new_render_process_id
, new_render_frame_id
));
396 if (it
!= pending_throttles_
.end()) {
397 IoThreadClientThrottle
* throttle
= it
->second
;
398 throttle
->OnIoThreadClientReady(new_render_process_id
, new_render_frame_id
);
399 pending_throttles_
.erase(it
);
403 void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
404 net::URLRequest
* request
,
405 content::ResourceContext
* resource_context
) {
406 const content::ResourceRequestInfo
* request_info
=
407 content::ResourceRequestInfo::ForRequest(request
);
408 if (!request_info
) return;
409 if (request_info
->GetResourceType() != ResourceType::MAIN_FRAME
) return;
411 const content::PageTransition transition
= request_info
->GetPageTransition();
412 const bool is_load_url
=
413 transition
& content::PAGE_TRANSITION_FROM_API
;
414 const bool is_go_back_forward
=
415 transition
& content::PAGE_TRANSITION_FORWARD_BACK
;
416 const bool is_reload
= content::PageTransitionCoreTypeIs(
417 transition
, content::PAGE_TRANSITION_RELOAD
);
418 if (is_load_url
|| is_go_back_forward
|| is_reload
) {
419 AwResourceContext
* awrc
= static_cast<AwResourceContext
*>(resource_context
);
420 std::string extra_headers
= awrc
->GetExtraHeaders(request
->url());
421 if (!extra_headers
.empty()) {
422 net::HttpRequestHeaders headers
;
423 headers
.AddHeadersFromString(extra_headers
);
424 for (net::HttpRequestHeaders::Iterator
it(headers
); it
.GetNext(); ) {
425 request
->SetExtraRequestHeaderByName(it
.name(), it
.value(), false);
431 } // namespace android_webview