6bbcc496fdb91a1349e78a9ee364d93ad9a9d1d7
[platform/framework/web/crosswalk.git] / src / xwalk / runtime / browser / runtime_resource_dispatcher_host_delegate_android.cc
1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5
6 #include "xwalk/runtime/browser/runtime_resource_dispatcher_host_delegate_android.h"
7
8 #include <string>
9 #include <utility>
10
11 #include "base/memory/scoped_ptr.h"
12 #include "base/memory/scoped_vector.h"
13 #include "components/auto_login_parser/auto_login_parser.h"
14 #include "components/navigation_interception/intercept_navigation_delegate.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "content/public/browser/resource_controller.h"
17 #include "content/public/browser/resource_dispatcher_host.h"
18 #include "content/public/browser/resource_dispatcher_host_login_delegate.h"
19 #include "content/public/browser/resource_request_info.h"
20 #include "content/public/browser/resource_throttle.h"
21 #include "content/public/common/url_constants.h"
22 #include "net/base/load_flags.h"
23 #include "net/base/net_errors.h"
24 #include "net/http/http_response_headers.h"
25 #include "net/url_request/url_request.h"
26 #include "xwalk/runtime/browser/android/net/url_constants.h"
27 #include "xwalk/runtime/browser/android/xwalk_contents_io_thread_client.h"
28 #include "xwalk/runtime/browser/android/xwalk_download_resource_throttle.h"
29 #include "xwalk/runtime/browser/android/xwalk_login_delegate.h"
30
31 using content::BrowserThread;
32 using navigation_interception::InterceptNavigationDelegate;
33 using xwalk::XWalkContentsIoThreadClient;
34
35 namespace {
36 base::LazyInstance<xwalk::RuntimeResourceDispatcherHostDelegateAndroid>
37     g_runtime_resource_dispatcher_host_delegate_android =
38         LAZY_INSTANCE_INITIALIZER;
39
40 void SetCacheControlFlag(
41     net::URLRequest* request, int flag) {
42   const int all_cache_control_flags = net::LOAD_BYPASS_CACHE |
43       net::LOAD_VALIDATE_CACHE |
44       net::LOAD_PREFERRING_CACHE |
45       net::LOAD_ONLY_FROM_CACHE;
46   DCHECK((flag & all_cache_control_flags) == flag);
47   int load_flags = request->load_flags();
48   load_flags &= ~all_cache_control_flags;
49   load_flags |= flag;
50   request->SetLoadFlags(load_flags);
51 }
52 }  // namespace
53
54 namespace xwalk {
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   bool MaybeDeferRequest(bool* defer);
74   void OnIoThreadClientReady(int new_render_process_id,
75                              int new_render_frame_id);
76   bool MaybeBlockRequest();
77   bool ShouldBlockRequest();
78   int render_process_id() const { return render_process_id_; }
79   int render_frame_id() const { return render_frame_id_; }
80
81  private:
82   int render_process_id_;
83   int render_frame_id_;
84   net::URLRequest* request_;
85 };
86
87 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
88                                                int render_frame_id,
89                                                net::URLRequest* request)
90     : render_process_id_(render_process_id),
91       render_frame_id_(render_frame_id),
92       request_(request) { }
93
94 IoThreadClientThrottle::~IoThreadClientThrottle() {
95   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
96   g_runtime_resource_dispatcher_host_delegate_android.Get().
97       RemovePendingThrottleOnIoThread(this);
98 }
99
100 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
101   if (render_frame_id_ < 1) {
102     // OPTIONS is used for preflighted requests which are generated internally.
103     DCHECK_EQ("OPTIONS", request_->method());
104     return;
105   }
106   DCHECK(render_process_id_);
107   if (!MaybeDeferRequest(defer)) {
108     MaybeBlockRequest();
109   }
110 }
111
112 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
113                                                  bool* defer) {
114   WillStartRequest(defer);
115 }
116
117 const char* IoThreadClientThrottle::GetNameForLogging() const {
118   return "IoThreadClientThrottle";
119 }
120
121 bool IoThreadClientThrottle::MaybeDeferRequest(bool* defer) {
122   *defer = false;
123
124   // Defer all requests of a pop up that is still not associated with Java
125   // client so that the client will get a chance to override requests.
126   scoped_ptr<XWalkContentsIoThreadClient> io_client =
127       XWalkContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
128   if (io_client && io_client->PendingAssociation()) {
129     *defer = true;
130     RuntimeResourceDispatcherHostDelegateAndroid::AddPendingThrottle(
131         render_process_id_, render_frame_id_, this);
132   }
133   return *defer;
134 }
135
136 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id,
137                                                    int new_render_frame_id) {
138   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
139
140   if (!MaybeBlockRequest()) {
141     controller()->Resume();
142   }
143 }
144
145 bool IoThreadClientThrottle::MaybeBlockRequest() {
146   if (ShouldBlockRequest()) {
147     controller()->CancelWithError(net::ERR_ACCESS_DENIED);
148     return true;
149   }
150   return false;
151 }
152
153 bool IoThreadClientThrottle::ShouldBlockRequest() {
154   scoped_ptr<XWalkContentsIoThreadClient> io_client =
155       XWalkContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
156   DCHECK(io_client.get());
157
158   // Part of implementation of WebSettings.allowContentAccess.
159   if (request_->url().SchemeIs(xwalk::kContentScheme) &&
160       io_client->ShouldBlockContentUrls()) {
161     return true;
162   }
163
164   // Part of implementation of WebSettings.allowFileAccess.
165   if (request_->url().SchemeIsFile() &&
166       io_client->ShouldBlockFileUrls()) {
167     const GURL& url = request_->url();
168     if (!url.has_path() ||
169         // Application's assets and resources are always available.
170         (url.path().find(xwalk::kAndroidResourcePath) != 0 &&
171          url.path().find(xwalk::kAndroidAssetPath) != 0)) {
172       return true;
173     }
174   }
175
176   if (io_client->ShouldBlockNetworkLoads()) {
177     if (request_->url().SchemeIs(url::kFtpScheme)) {
178       return true;
179     }
180     SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
181   } else {
182     XWalkContentsIoThreadClient::CacheMode cache_mode =
183         io_client->GetCacheMode();
184     switch (cache_mode) {
185       case XWalkContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
186         SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
187         break;
188       case XWalkContentsIoThreadClient::LOAD_NO_CACHE:
189         SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
190         break;
191       case XWalkContentsIoThreadClient::LOAD_CACHE_ONLY:
192         SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
193         break;
194       default:
195         break;
196     }
197   }
198   return false;
199 }
200
201 RuntimeResourceDispatcherHostDelegateAndroid::
202     RuntimeResourceDispatcherHostDelegateAndroid() {
203 }
204
205 RuntimeResourceDispatcherHostDelegateAndroid::
206     ~RuntimeResourceDispatcherHostDelegateAndroid() {
207 }
208
209 // static
210 void RuntimeResourceDispatcherHostDelegateAndroid::
211 ResourceDispatcherHostCreated() {
212   content::ResourceDispatcherHost::Get()->SetDelegate(
213       &g_runtime_resource_dispatcher_host_delegate_android.Get());
214 }
215
216 void RuntimeResourceDispatcherHostDelegateAndroid::RequestBeginning(
217     net::URLRequest* request,
218     content::ResourceContext* resource_context,
219     content::AppCacheService* appcache_service,
220     content::ResourceType resource_type,
221     ScopedVector<content::ResourceThrottle>* throttles) {
222   const content::ResourceRequestInfo* request_info =
223       content::ResourceRequestInfo::ForRequest(request);
224
225   // We allow intercepting only navigations within main frames. This
226   // is used to post onPageStarted. We handle shouldOverrideUrlLoading
227   // via a sync IPC for url loading in iframe.
228   if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
229     throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
230         request));
231   }
232
233   // If io_client is NULL, then the browser side objects have already been
234   // destroyed, so do not do anything to the request. Conversely if the
235   // request relates to a not-yet-created popup window, then the client will
236   // be non-NULL but PopupPendingAssociation() will be set.
237   scoped_ptr<XWalkContentsIoThreadClient> io_client =
238       XWalkContentsIoThreadClient::FromID(
239           request_info->GetChildID(), request_info->GetRenderFrameID());
240   if (!io_client)
241     return;
242
243   throttles->push_back(new IoThreadClientThrottle(
244       request_info->GetChildID(), request_info->GetRenderFrameID(), request));
245 }
246
247 void RuntimeResourceDispatcherHostDelegateAndroid::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();
261
262   request->extra_request_headers().GetHeader(
263       net::HttpRequestHeaders::kUserAgent, &user_agent);
264
265
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);
271   }
272
273   request->Cancel();
274
275   const content::ResourceRequestInfo* request_info =
276       content::ResourceRequestInfo::ForRequest(request);
277
278   scoped_ptr<XWalkContentsIoThreadClient> io_client =
279       XWalkContentsIoThreadClient::FromID(
280           child_id, request_info->GetRenderFrameID());
281
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);
290   }
291 }
292
293 content::ResourceDispatcherHostLoginDelegate*
294     RuntimeResourceDispatcherHostDelegateAndroid::CreateLoginDelegate(
295         net::AuthChallengeInfo* auth_info,
296         net::URLRequest* request) {
297   return new XWalkLoginDelegate(auth_info, request);
298 }
299
300 bool RuntimeResourceDispatcherHostDelegateAndroid::HandleExternalProtocol(
301     const GURL& url,
302     int child_id,
303     int route_id) {
304   // On Android, there are many Uris need to be handled differently.
305   // e.g: sms:, tel:, mailto: and etc.
306   // So here return false to let embedders to decide which protocol
307   // to be handled.
308   return false;
309 }
310
311 void RuntimeResourceDispatcherHostDelegateAndroid::OnResponseStarted(
312     net::URLRequest* request,
313     content::ResourceContext* resource_context,
314     content::ResourceResponse* response,
315     IPC::Sender* sender) {
316   const content::ResourceRequestInfo* request_info =
317       content::ResourceRequestInfo::ForRequest(request);
318   if (!request_info) {
319     DLOG(FATAL) << "Started request without associated info: " <<
320         request->url();
321     return;
322   }
323
324   if (request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
325     // Check for x-auto-login header.
326     auto_login_parser::HeaderData header_data;
327     if (auto_login_parser::ParserHeaderInResponse(
328             request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
329       scoped_ptr<XWalkContentsIoThreadClient> io_client =
330           XWalkContentsIoThreadClient::FromID(request_info->GetChildID(),
331                                               request_info->GetRenderFrameID());
332       if (io_client) {
333         io_client->NewLoginRequest(
334             header_data.realm, header_data.account, header_data.args);
335       }
336     }
337   }
338 }
339
340 void RuntimeResourceDispatcherHostDelegateAndroid::
341 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 RuntimeResourceDispatcherHostDelegateAndroid::OnIoThreadClientReady(
354     int new_render_process_id,
355     int new_render_frame_id) {
356   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
357       base::Bind(
358           &RuntimeResourceDispatcherHostDelegateAndroid::
359           OnIoThreadClientReadyInternal,
360           base::Unretained(
361               g_runtime_resource_dispatcher_host_delegate_android.Pointer()),
362           new_render_process_id, new_render_frame_id));
363 }
364
365 // static
366 void RuntimeResourceDispatcherHostDelegateAndroid::AddPendingThrottle(
367     int render_process_id,
368     int render_frame_id,
369     IoThreadClientThrottle* pending_throttle) {
370   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
371       base::Bind(
372           &RuntimeResourceDispatcherHostDelegateAndroid::
373               AddPendingThrottleOnIoThread,
374           base::Unretained(
375               g_runtime_resource_dispatcher_host_delegate_android.Pointer()),
376           render_process_id, render_frame_id, pending_throttle));
377 }
378
379 void RuntimeResourceDispatcherHostDelegateAndroid::
380     AddPendingThrottleOnIoThread(
381         int render_process_id,
382         int render_frame_id,
383         IoThreadClientThrottle* pending_throttle) {
384   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
385   pending_throttles_.insert(
386       std::pair<FrameRouteIDPair, IoThreadClientThrottle*>(
387           FrameRouteIDPair(render_process_id, render_frame_id),
388           pending_throttle));
389 }
390
391 void RuntimeResourceDispatcherHostDelegateAndroid::
392 OnIoThreadClientReadyInternal(
393     int new_render_process_id,
394     int new_render_frame_id) {
395   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
396   PendingThrottleMap::iterator it = pending_throttles_.find(
397       FrameRouteIDPair(new_render_process_id, new_render_frame_id));
398
399   if (it != pending_throttles_.end()) {
400     IoThreadClientThrottle* throttle = it->second;
401     throttle->OnIoThreadClientReady(new_render_process_id,
402                                     new_render_frame_id);
403     pending_throttles_.erase(it);
404   }
405 }
406
407 }  // namespace xwalk