Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / android_webview / browser / renderer_host / aw_resource_dispatcher_host_delegate.cc
blob5f672c73e0703f305929f1a46311dfd00158c56e
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"
7 #include <string>
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;
34 namespace {
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;
48 load_flags |= flag;
49 request->SetLoadFlags(load_flags);
52 } // namespace
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 {
62 public:
63 IoThreadClientThrottle(int render_process_id,
64 int render_frame_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_; }
80 private:
81 int render_process_id_;
82 int render_frame_id_;
83 net::URLRequest* request_;
86 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
87 int render_frame_id,
88 net::URLRequest* request)
89 : render_process_id_(render_process_id),
90 render_frame_id_(render_frame_id),
91 request_(request) { }
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());
109 return;
111 DCHECK(render_process_id_);
112 *defer = false;
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()) {
119 *defer = true;
120 AwResourceDispatcherHostDelegate::AddPendingThrottle(
121 render_process_id_, render_frame_id_, this);
122 } else {
123 MaybeBlockRequest();
127 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
128 bool* defer) {
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);
144 return true;
146 return false;
149 bool IoThreadClientThrottle::ShouldBlockRequest() {
150 scoped_ptr<AwContentsIoThreadClient> io_client =
151 AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
152 if (!io_client)
153 return false;
155 // Part of implementation of WebSettings.allowContentAccess.
156 if (request_->url().SchemeIs(android_webview::kContentScheme) &&
157 io_client->ShouldBlockContentUrls()) {
158 return true;
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)) {
169 return true;
173 if (io_client->ShouldBlockNetworkLoads()) {
174 if (request_->url().SchemeIs(url::kFtpScheme)) {
175 return true;
177 SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
178 } else {
179 AwContentsIoThreadClient::CacheMode cache_mode = io_client->GetCacheMode();
180 switch(cache_mode) {
181 case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
182 SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
183 break;
184 case AwContentsIoThreadClient::LOAD_NO_CACHE:
185 SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
186 break;
187 case AwContentsIoThreadClient::LOAD_CACHE_ONLY:
188 SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
189 break;
190 default:
191 break;
194 return false;
197 // static
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 resource_type,
215 ScopedVector<content::ResourceThrottle>* throttles) {
217 AddExtraHeadersIfNeeded(request, resource_context);
219 const content::ResourceRequestInfo* request_info =
220 content::ResourceRequestInfo::ForRequest(request);
222 // We always push the throttles here. Checking the existence of io_client
223 // is racy when a popup window is created. That is because RequestBeginning
224 // is called whether or not requests are blocked via BlockRequestForRoute()
225 // however io_client may or may not be ready at the time depending on whether
226 // webcontents is created.
227 throttles->push_back(new IoThreadClientThrottle(
228 request_info->GetChildID(), request_info->GetRenderFrameID(), request));
230 // We allow intercepting only navigations within main frames. This
231 // is used to post onPageStarted. We handle shouldOverrideUrlLoading
232 // via a sync IPC.
233 if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
234 throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
235 request));
238 void AwResourceDispatcherHostDelegate::OnRequestRedirected(
239 const GURL& redirect_url,
240 net::URLRequest* request,
241 content::ResourceContext* resource_context,
242 content::ResourceResponse* response) {
243 AddExtraHeadersIfNeeded(request, resource_context);
247 void AwResourceDispatcherHostDelegate::DownloadStarting(
248 net::URLRequest* request,
249 content::ResourceContext* resource_context,
250 int child_id,
251 int route_id,
252 int request_id,
253 bool is_content_initiated,
254 bool must_download,
255 ScopedVector<content::ResourceThrottle>* throttles) {
256 GURL url(request->url());
257 std::string user_agent;
258 std::string content_disposition;
259 std::string mime_type;
260 int64 content_length = request->GetExpectedContentSize();
262 request->extra_request_headers().GetHeader(
263 net::HttpRequestHeaders::kUserAgent, &user_agent);
266 net::HttpResponseHeaders* response_headers = request->response_headers();
267 if (response_headers) {
268 response_headers->GetNormalizedHeader("content-disposition",
269 &content_disposition);
270 response_headers->GetMimeType(&mime_type);
273 request->Cancel();
275 const content::ResourceRequestInfo* request_info =
276 content::ResourceRequestInfo::ForRequest(request);
278 scoped_ptr<AwContentsIoThreadClient> io_client =
279 AwContentsIoThreadClient::FromID(
280 child_id, request_info->GetRenderFrameID());
282 // POST request cannot be repeated in general, so prevent client from
283 // retrying the same request, even if it is with a GET.
284 if ("GET" == request->method() && io_client) {
285 io_client->NewDownload(url,
286 user_agent,
287 content_disposition,
288 mime_type,
289 content_length);
293 content::ResourceDispatcherHostLoginDelegate*
294 AwResourceDispatcherHostDelegate::CreateLoginDelegate(
295 net::AuthChallengeInfo* auth_info,
296 net::URLRequest* request) {
297 return new AwLoginDelegate(auth_info, request);
300 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL& url,
301 int child_id,
302 int route_id) {
303 // The AwURLRequestJobFactory implementation should ensure this method never
304 // gets called.
305 NOTREACHED();
306 return false;
309 void AwResourceDispatcherHostDelegate::OnResponseStarted(
310 net::URLRequest* request,
311 content::ResourceContext* resource_context,
312 content::ResourceResponse* response,
313 IPC::Sender* sender) {
314 const content::ResourceRequestInfo* request_info =
315 content::ResourceRequestInfo::ForRequest(request);
316 if (!request_info) {
317 DLOG(FATAL) << "Started request without associated info: " <<
318 request->url();
319 return;
322 if (request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
323 // Check for x-auto-login header.
324 auto_login_parser::HeaderData header_data;
325 if (auto_login_parser::ParserHeaderInResponse(
326 request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
327 scoped_ptr<AwContentsIoThreadClient> io_client =
328 AwContentsIoThreadClient::FromID(request_info->GetChildID(),
329 request_info->GetRenderFrameID());
330 if (io_client) {
331 io_client->NewLoginRequest(
332 header_data.realm, header_data.account, header_data.args);
338 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
339 IoThreadClientThrottle* throttle) {
340 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
341 PendingThrottleMap::iterator it = pending_throttles_.find(
342 FrameRouteIDPair(throttle->render_process_id(),
343 throttle->render_frame_id()));
344 if (it != pending_throttles_.end()) {
345 pending_throttles_.erase(it);
349 // static
350 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
351 int new_render_process_id,
352 int new_render_frame_id) {
353 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
354 base::Bind(
355 &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal,
356 base::Unretained(
357 g_webview_resource_dispatcher_host_delegate.Pointer()),
358 new_render_process_id, new_render_frame_id));
361 // static
362 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
363 int render_process_id,
364 int render_frame_id,
365 IoThreadClientThrottle* pending_throttle) {
366 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
367 base::Bind(
368 &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread,
369 base::Unretained(
370 g_webview_resource_dispatcher_host_delegate.Pointer()),
371 render_process_id, render_frame_id, pending_throttle));
374 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
375 int render_process_id,
376 int render_frame_id_id,
377 IoThreadClientThrottle* pending_throttle) {
378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
379 pending_throttles_.insert(
380 std::pair<FrameRouteIDPair, IoThreadClientThrottle*>(
381 FrameRouteIDPair(render_process_id, render_frame_id_id),
382 pending_throttle));
385 void AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal(
386 int new_render_process_id,
387 int new_render_frame_id) {
388 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
389 PendingThrottleMap::iterator it = pending_throttles_.find(
390 FrameRouteIDPair(new_render_process_id, new_render_frame_id));
392 if (it != pending_throttles_.end()) {
393 IoThreadClientThrottle* throttle = it->second;
394 throttle->OnIoThreadClientReady(new_render_process_id, new_render_frame_id);
395 pending_throttles_.erase(it);
399 void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
400 net::URLRequest* request,
401 content::ResourceContext* resource_context) {
402 const content::ResourceRequestInfo* request_info =
403 content::ResourceRequestInfo::ForRequest(request);
404 if (!request_info)
405 return;
406 if (request_info->GetResourceType() != content::RESOURCE_TYPE_MAIN_FRAME)
407 return;
409 const content::PageTransition transition = request_info->GetPageTransition();
410 const bool is_load_url =
411 transition & content::PAGE_TRANSITION_FROM_API;
412 const bool is_go_back_forward =
413 transition & content::PAGE_TRANSITION_FORWARD_BACK;
414 const bool is_reload = content::PageTransitionCoreTypeIs(
415 transition, content::PAGE_TRANSITION_RELOAD);
416 if (is_load_url || is_go_back_forward || is_reload) {
417 AwResourceContext* awrc = static_cast<AwResourceContext*>(resource_context);
418 std::string extra_headers = awrc->GetExtraHeaders(request->url());
419 if (!extra_headers.empty()) {
420 net::HttpRequestHeaders headers;
421 headers.AddHeadersFromString(extra_headers);
422 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); ) {
423 request->SetExtraRequestHeaderByName(it.name(), it.value(), false);
429 } // namespace android_webview