Update To 11.40.268.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 "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"
28
29 using android_webview::AwContentsIoThreadClient;
30 using content::BrowserThread;
31 using content::ResourceType;
32 using navigation_interception::InterceptNavigationDelegate;
33
34 namespace {
35
36 base::LazyInstance<android_webview::AwResourceDispatcherHostDelegate>
37     g_webview_resource_dispatcher_host_delegate = LAZY_INSTANCE_INITIALIZER;
38
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;
48   load_flags |= flag;
49   request->SetLoadFlags(load_flags);
50 }
51
52 }  // namespace
53
54 namespace android_webview {
55
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();
67
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;
72
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_; }
79
80  private:
81   int render_process_id_;
82   int render_frame_id_;
83   net::URLRequest* request_;
84 };
85
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) { }
92
93 IoThreadClientThrottle::~IoThreadClientThrottle() {
94   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
95   g_webview_resource_dispatcher_host_delegate.Get().
96       RemovePendingThrottleOnIoThread(this);
97 }
98
99 const char* IoThreadClientThrottle::GetNameForLogging() const {
100   return "IoThreadClientThrottle";
101 }
102
103 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
104   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
105   if (render_frame_id_ < 1)
106     return;
107   DCHECK(render_process_id_);
108   *defer = false;
109
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()) {
115     *defer = true;
116     AwResourceDispatcherHostDelegate::AddPendingThrottle(
117         render_process_id_, render_frame_id_, this);
118   } else {
119     MaybeBlockRequest();
120   }
121 }
122
123 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
124                                                  bool* defer) {
125   WillStartRequest(defer);
126 }
127
128 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id,
129                                                    int new_render_frame_id) {
130   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
131
132   if (!MaybeBlockRequest()) {
133     controller()->Resume();
134   }
135 }
136
137 bool IoThreadClientThrottle::MaybeBlockRequest() {
138   if (ShouldBlockRequest()) {
139     controller()->CancelWithError(net::ERR_ACCESS_DENIED);
140     return true;
141   }
142   return false;
143 }
144
145 bool IoThreadClientThrottle::ShouldBlockRequest() {
146   scoped_ptr<AwContentsIoThreadClient> io_client =
147       AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
148   if (!io_client)
149     return false;
150
151   // Part of implementation of WebSettings.allowContentAccess.
152   if (request_->url().SchemeIs(android_webview::kContentScheme) &&
153       io_client->ShouldBlockContentUrls()) {
154     return true;
155   }
156
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)) {
165       return true;
166     }
167   }
168
169   if (io_client->ShouldBlockNetworkLoads()) {
170     if (request_->url().SchemeIs(url::kFtpScheme)) {
171       return true;
172     }
173     SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
174   } else {
175     AwContentsIoThreadClient::CacheMode cache_mode = io_client->GetCacheMode();
176     switch(cache_mode) {
177       case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
178         SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
179         break;
180       case AwContentsIoThreadClient::LOAD_NO_CACHE:
181         SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
182         break;
183       case AwContentsIoThreadClient::LOAD_CACHE_ONLY:
184         SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
185         break;
186       default:
187         break;
188     }
189   }
190   return false;
191 }
192
193 // static
194 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
195   content::ResourceDispatcherHost::Get()->SetDelegate(
196       &g_webview_resource_dispatcher_host_delegate.Get());
197 }
198
199 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
200     : content::ResourceDispatcherHostDelegate() {
201 }
202
203 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
204 }
205
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) {
212
213   AddExtraHeadersIfNeeded(request, resource_context);
214
215   const content::ResourceRequestInfo* request_info =
216       content::ResourceRequestInfo::ForRequest(request);
217
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));
225
226   // We allow intercepting only navigations within main frames. This
227   // is used to post onPageStarted. We handle shouldOverrideUrlLoading
228   // via a sync IPC.
229   if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
230     throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
231         request));
232 }
233
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);
240 }
241
242
243 void AwResourceDispatcherHostDelegate::DownloadStarting(
244     net::URLRequest* request,
245     content::ResourceContext* resource_context,
246     int child_id,
247     int route_id,
248     int request_id,
249     bool is_content_initiated,
250     bool must_download,
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();
257
258   request->extra_request_headers().GetHeader(
259       net::HttpRequestHeaders::kUserAgent, &user_agent);
260
261
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);
267   }
268
269   request->Cancel();
270
271   const content::ResourceRequestInfo* request_info =
272       content::ResourceRequestInfo::ForRequest(request);
273
274   scoped_ptr<AwContentsIoThreadClient> io_client =
275       AwContentsIoThreadClient::FromID(
276           child_id, request_info->GetRenderFrameID());
277
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,
282                            user_agent,
283                            content_disposition,
284                            mime_type,
285                            content_length);
286   }
287 }
288
289 content::ResourceDispatcherHostLoginDelegate*
290     AwResourceDispatcherHostDelegate::CreateLoginDelegate(
291         net::AuthChallengeInfo* auth_info,
292         net::URLRequest* request) {
293   return new AwLoginDelegate(auth_info, request);
294 }
295
296 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL& url,
297                                                               int child_id,
298                                                               int route_id) {
299   // The AwURLRequestJobFactory implementation should ensure this method never
300   // gets called.
301   NOTREACHED();
302   return false;
303 }
304
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);
312   if (!request_info) {
313     DLOG(FATAL) << "Started request without associated info: " <<
314         request->url();
315     return;
316   }
317
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());
326       if (io_client) {
327         io_client->NewLoginRequest(
328             header_data.realm, header_data.account, header_data.args);
329       }
330     }
331   }
332 }
333
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);
342   }
343 }
344
345 // static
346 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
347     int new_render_process_id,
348     int new_render_frame_id) {
349   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
350       base::Bind(
351           &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal,
352           base::Unretained(
353               g_webview_resource_dispatcher_host_delegate.Pointer()),
354           new_render_process_id, new_render_frame_id));
355 }
356
357 // static
358 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
359     int render_process_id,
360     int render_frame_id,
361     IoThreadClientThrottle* pending_throttle) {
362   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
363       base::Bind(
364           &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread,
365           base::Unretained(
366               g_webview_resource_dispatcher_host_delegate.Pointer()),
367           render_process_id, render_frame_id, pending_throttle));
368 }
369
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),
378           pending_throttle));
379 }
380
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));
387
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);
392   }
393 }
394
395 void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
396     net::URLRequest* request,
397     content::ResourceContext* resource_context) {
398   const content::ResourceRequestInfo* request_info =
399       content::ResourceRequestInfo::ForRequest(request);
400   if (!request_info)
401     return;
402   if (request_info->GetResourceType() != content::RESOURCE_TYPE_MAIN_FRAME)
403     return;
404
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);
420       }
421     }
422   }
423 }
424
425 }  // namespace android_webview