Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / android_webview / browser / renderer_host / aw_resource_dispatcher_host_delegate.cc
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.
4
5 #include "android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h"
6
7 #include <string>
8
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 "content/public/common/url_constants.h"
24 #include "net/base/load_flags.h"
25 #include "net/base/net_errors.h"
26 #include "net/http/http_response_headers.h"
27 #include "net/url_request/url_request.h"
28
29 using android_webview::AwContentsIoThreadClient;
30 using content::BrowserThread;
31 using navigation_interception::InterceptNavigationDelegate;
32
33 namespace {
34
35 base::LazyInstance<android_webview::AwResourceDispatcherHostDelegate>
36     g_webview_resource_dispatcher_host_delegate = LAZY_INSTANCE_INITIALIZER;
37
38 void SetCacheControlFlag(
39     net::URLRequest* request, int flag) {
40   const int all_cache_control_flags = net::LOAD_BYPASS_CACHE |
41       net::LOAD_VALIDATE_CACHE |
42       net::LOAD_PREFERRING_CACHE |
43       net::LOAD_ONLY_FROM_CACHE;
44   DCHECK((flag & all_cache_control_flags) == flag);
45   int load_flags = request->load_flags();
46   load_flags &= ~all_cache_control_flags;
47   load_flags |= flag;
48   request->SetLoadFlags(load_flags);
49 }
50
51 }  // namespace
52
53 namespace android_webview {
54
55 // Calls through the IoThreadClient to check the embedders settings to determine
56 // if the request should be cancelled. There may not always be an IoThreadClient
57 // available for the |render_process_id|, |render_frame_id| pair (in the case of
58 // newly created pop up windows, for example) and in that case the request and
59 // the client callbacks will be deferred the request until a client is ready.
60 class IoThreadClientThrottle : public content::ResourceThrottle {
61  public:
62   IoThreadClientThrottle(int render_process_id,
63                          int render_frame_id,
64                          net::URLRequest* request);
65   virtual ~IoThreadClientThrottle();
66
67   // From content::ResourceThrottle
68   virtual void WillStartRequest(bool* defer) OVERRIDE;
69   virtual void WillRedirectRequest(const GURL& new_url, bool* defer) OVERRIDE;
70   virtual const char* GetNameForLogging() const OVERRIDE;
71
72   void OnIoThreadClientReady(int new_render_process_id,
73                              int new_render_frame_id);
74   bool MaybeBlockRequest();
75   bool ShouldBlockRequest();
76   int render_process_id() const { return render_process_id_; }
77   int render_frame_id() const { return render_frame_id_; }
78
79  private:
80   int render_process_id_;
81   int render_frame_id_;
82   net::URLRequest* request_;
83 };
84
85 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
86                                                int render_frame_id,
87                                                net::URLRequest* request)
88     : render_process_id_(render_process_id),
89       render_frame_id_(render_frame_id),
90       request_(request) { }
91
92 IoThreadClientThrottle::~IoThreadClientThrottle() {
93   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
94   g_webview_resource_dispatcher_host_delegate.Get().
95       RemovePendingThrottleOnIoThread(this);
96 }
97
98 const char* IoThreadClientThrottle::GetNameForLogging() const {
99   return "IoThreadClientThrottle";
100 }
101
102 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
103   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
104   // TODO(sgurun): This block can be removed when crbug.com/277937 is fixed.
105   if (render_frame_id_ < 1) {
106     // OPTIONS is used for preflighted requests which are generated internally.
107     DCHECK_EQ("OPTIONS", request_->method());
108     return;
109   }
110   DCHECK(render_process_id_);
111   *defer = false;
112
113   // Defer all requests of a pop up that is still not associated with Java
114   // client so that the client will get a chance to override requests.
115   scoped_ptr<AwContentsIoThreadClient> io_client =
116       AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
117   if (io_client && io_client->PendingAssociation()) {
118     *defer = true;
119     AwResourceDispatcherHostDelegate::AddPendingThrottle(
120         render_process_id_, render_frame_id_, this);
121   } else {
122     MaybeBlockRequest();
123   }
124 }
125
126 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
127                                                  bool* defer) {
128   WillStartRequest(defer);
129 }
130
131 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id,
132                                                    int new_render_frame_id) {
133   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
134
135   if (!MaybeBlockRequest()) {
136     controller()->Resume();
137   }
138 }
139
140 bool IoThreadClientThrottle::MaybeBlockRequest() {
141   if (ShouldBlockRequest()) {
142     controller()->CancelWithError(net::ERR_ACCESS_DENIED);
143     return true;
144   }
145   return false;
146 }
147
148 bool IoThreadClientThrottle::ShouldBlockRequest() {
149   scoped_ptr<AwContentsIoThreadClient> io_client =
150       AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
151   if (!io_client)
152     return false;
153
154   // Part of implementation of WebSettings.allowContentAccess.
155   if (request_->url().SchemeIs(android_webview::kContentScheme) &&
156       io_client->ShouldBlockContentUrls()) {
157     return true;
158   }
159
160   // Part of implementation of WebSettings.allowFileAccess.
161   if (request_->url().SchemeIsFile() &&
162       io_client->ShouldBlockFileUrls()) {
163     const GURL& url = request_->url();
164     if (!url.has_path() ||
165         // Application's assets and resources are always available.
166         (url.path().find(android_webview::kAndroidResourcePath) != 0 &&
167          url.path().find(android_webview::kAndroidAssetPath) != 0)) {
168       return true;
169     }
170   }
171
172   if (io_client->ShouldBlockNetworkLoads()) {
173     if (request_->url().SchemeIs(content::kFtpScheme)) {
174       return true;
175     }
176     SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
177   } else {
178     AwContentsIoThreadClient::CacheMode cache_mode = io_client->GetCacheMode();
179     switch(cache_mode) {
180       case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
181         SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
182         break;
183       case AwContentsIoThreadClient::LOAD_NO_CACHE:
184         SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
185         break;
186       case AwContentsIoThreadClient::LOAD_CACHE_ONLY:
187         SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
188         break;
189       default:
190         break;
191     }
192   }
193   return false;
194 }
195
196 // static
197 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
198   content::ResourceDispatcherHost::Get()->SetDelegate(
199       &g_webview_resource_dispatcher_host_delegate.Get());
200 }
201
202 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
203     : content::ResourceDispatcherHostDelegate() {
204 }
205
206 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
207 }
208
209 void AwResourceDispatcherHostDelegate::RequestBeginning(
210     net::URLRequest* request,
211     content::ResourceContext* resource_context,
212     appcache::AppCacheService* appcache_service,
213     ResourceType::Type resource_type,
214     int child_id,
215     int route_id,
216     ScopedVector<content::ResourceThrottle>* throttles) {
217
218   AddExtraHeadersIfNeeded(request, resource_context);
219
220   const content::ResourceRequestInfo* request_info =
221       content::ResourceRequestInfo::ForRequest(request);
222
223   // We always push the throttles here. Checking the existence of io_client
224   // is racy when a popup window is created. That is because RequestBeginning
225   // is called whether or not requests are blocked via BlockRequestForRoute()
226   // however io_client may or may not be ready at the time depending on whether
227   // webcontents is created.
228   throttles->push_back(new IoThreadClientThrottle(
229       child_id, request_info->GetRenderFrameID(), request));
230
231   // We allow intercepting only navigations within main frames. This
232   // is used to post onPageStarted. We handle shouldOverrideUrlLoading
233   // via a sync IPC.
234   if (resource_type == ResourceType::MAIN_FRAME)
235     throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
236         request));
237 }
238
239 void AwResourceDispatcherHostDelegate::OnRequestRedirected(
240     const GURL& redirect_url,
241     net::URLRequest* request,
242     content::ResourceContext* resource_context,
243     content::ResourceResponse* response) {
244   AddExtraHeadersIfNeeded(request, resource_context);
245 }
246
247
248 void AwResourceDispatcherHostDelegate::DownloadStarting(
249     net::URLRequest* request,
250     content::ResourceContext* resource_context,
251     int child_id,
252     int route_id,
253     int request_id,
254     bool is_content_initiated,
255     bool must_download,
256     ScopedVector<content::ResourceThrottle>* throttles) {
257   GURL url(request->url());
258   std::string user_agent;
259   std::string content_disposition;
260   std::string mime_type;
261   int64 content_length = request->GetExpectedContentSize();
262
263   request->extra_request_headers().GetHeader(
264       net::HttpRequestHeaders::kUserAgent, &user_agent);
265
266
267   net::HttpResponseHeaders* response_headers = request->response_headers();
268   if (response_headers) {
269     response_headers->GetNormalizedHeader("content-disposition",
270         &content_disposition);
271     response_headers->GetMimeType(&mime_type);
272   }
273
274   request->Cancel();
275
276   const content::ResourceRequestInfo* request_info =
277       content::ResourceRequestInfo::ForRequest(request);
278
279   scoped_ptr<AwContentsIoThreadClient> io_client =
280       AwContentsIoThreadClient::FromID(
281           child_id, request_info->GetRenderFrameID());
282
283   // POST request cannot be repeated in general, so prevent client from
284   // retrying the same request, even if it is with a GET.
285   if ("GET" == request->method() && io_client) {
286     io_client->NewDownload(url,
287                            user_agent,
288                            content_disposition,
289                            mime_type,
290                            content_length);
291   }
292 }
293
294 content::ResourceDispatcherHostLoginDelegate*
295     AwResourceDispatcherHostDelegate::CreateLoginDelegate(
296         net::AuthChallengeInfo* auth_info,
297         net::URLRequest* request) {
298   return new AwLoginDelegate(auth_info, request);
299 }
300
301 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(
302     const GURL& url,
303     int child_id,
304     int route_id,
305     bool initiated_by_user_gesture) {
306   // The AwURLRequestJobFactory implementation should ensure this method never
307   // gets called.
308   NOTREACHED();
309   return false;
310 }
311
312 void AwResourceDispatcherHostDelegate::OnResponseStarted(
313     net::URLRequest* request,
314     content::ResourceContext* resource_context,
315     content::ResourceResponse* response,
316     IPC::Sender* sender) {
317   const content::ResourceRequestInfo* request_info =
318       content::ResourceRequestInfo::ForRequest(request);
319   if (!request_info) {
320     DLOG(FATAL) << "Started request without associated info: " <<
321         request->url();
322     return;
323   }
324
325   if (request_info->GetResourceType() == ResourceType::MAIN_FRAME) {
326     // Check for x-auto-login header.
327     auto_login_parser::HeaderData header_data;
328     if (auto_login_parser::ParserHeaderInResponse(
329             request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
330       scoped_ptr<AwContentsIoThreadClient> io_client =
331           AwContentsIoThreadClient::FromID(request_info->GetChildID(),
332                                            request_info->GetRenderFrameID());
333       if (io_client) {
334         io_client->NewLoginRequest(
335             header_data.realm, header_data.account, header_data.args);
336       }
337     }
338   }
339 }
340
341 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
342     IoThreadClientThrottle* throttle) {
343   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
344   PendingThrottleMap::iterator it = pending_throttles_.find(
345       FrameRouteIDPair(throttle->render_process_id(),
346                        throttle->render_frame_id()));
347   if (it != pending_throttles_.end()) {
348     pending_throttles_.erase(it);
349   }
350 }
351
352 // static
353 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
354     int new_render_process_id,
355     int new_render_frame_id) {
356   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
357       base::Bind(
358           &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal,
359           base::Unretained(
360               g_webview_resource_dispatcher_host_delegate.Pointer()),
361           new_render_process_id, new_render_frame_id));
362 }
363
364 // static
365 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
366     int render_process_id,
367     int render_frame_id,
368     IoThreadClientThrottle* pending_throttle) {
369   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
370       base::Bind(
371           &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread,
372           base::Unretained(
373               g_webview_resource_dispatcher_host_delegate.Pointer()),
374           render_process_id, render_frame_id, pending_throttle));
375 }
376
377 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
378     int render_process_id,
379     int render_frame_id_id,
380     IoThreadClientThrottle* pending_throttle) {
381   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
382   pending_throttles_.insert(
383       std::pair<FrameRouteIDPair, IoThreadClientThrottle*>(
384           FrameRouteIDPair(render_process_id, render_frame_id_id),
385           pending_throttle));
386 }
387
388 void AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal(
389     int new_render_process_id,
390     int new_render_frame_id) {
391   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
392   PendingThrottleMap::iterator it = pending_throttles_.find(
393       FrameRouteIDPair(new_render_process_id, new_render_frame_id));
394
395   if (it != pending_throttles_.end()) {
396     IoThreadClientThrottle* throttle = it->second;
397     throttle->OnIoThreadClientReady(new_render_process_id, new_render_frame_id);
398     pending_throttles_.erase(it);
399   }
400 }
401
402 void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
403     net::URLRequest* request,
404     content::ResourceContext* resource_context) {
405   const content::ResourceRequestInfo* request_info =
406       content::ResourceRequestInfo::ForRequest(request);
407   if (!request_info) return;
408   if (request_info->GetResourceType() != ResourceType::MAIN_FRAME) return;
409
410   const content::PageTransition transition = request_info->GetPageTransition();
411   const bool is_load_url =
412       transition & content::PAGE_TRANSITION_FROM_API;
413   const bool is_go_back_forward =
414       transition & content::PAGE_TRANSITION_FORWARD_BACK;
415   const bool is_reload = content::PageTransitionCoreTypeIs(
416       transition, content::PAGE_TRANSITION_RELOAD);
417   if (is_load_url || is_go_back_forward || is_reload) {
418     AwResourceContext* awrc = static_cast<AwResourceContext*>(resource_context);
419     std::string extra_headers = awrc->GetExtraHeaders(request->url());
420     if (!extra_headers.empty()) {
421       net::HttpRequestHeaders headers;
422       headers.AddHeadersFromString(extra_headers);
423       for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); ) {
424         request->SetExtraRequestHeaderByName(it.name(), it.value(), false);
425       }
426     }
427   }
428 }
429
430 }  // namespace android_webview