Upstream version 11.39.244.0
[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_request_info.h"
18 #include "content/public/browser/resource_throttle.h"
19 #include "content/public/common/url_constants.h"
20 #include "net/base/load_flags.h"
21 #include "net/base/net_errors.h"
22 #include "net/http/http_response_headers.h"
23 #include "net/url_request/url_request.h"
24 #include "xwalk/runtime/browser/android/net/url_constants.h"
25 #include "xwalk/runtime/browser/android/xwalk_contents_io_thread_client.h"
26 #include "xwalk/runtime/browser/android/xwalk_download_resource_throttle.h"
27 #include "xwalk/runtime/browser/android/xwalk_login_delegate.h"
28 #include "xwalk/runtime/browser/xwalk_content_browser_client.h"
29
30 using content::BrowserThread;
31 using navigation_interception::InterceptNavigationDelegate;
32 using xwalk::XWalkContentsIoThreadClient;
33
34 namespace {
35 void SetCacheControlFlag(
36     net::URLRequest* request, int flag) {
37   const int all_cache_control_flags = net::LOAD_BYPASS_CACHE |
38       net::LOAD_VALIDATE_CACHE |
39       net::LOAD_PREFERRING_CACHE |
40       net::LOAD_ONLY_FROM_CACHE;
41   DCHECK((flag & all_cache_control_flags) == flag);
42   int load_flags = request->load_flags();
43   load_flags &= ~all_cache_control_flags;
44   load_flags |= flag;
45   request->SetLoadFlags(load_flags);
46 }
47 }  // namespace
48
49 namespace xwalk {
50
51 // Calls through the IoThreadClient to check the embedders settings to determine
52 // if the request should be cancelled. There may not always be an IoThreadClient
53 // available for the |render_process_id|, |render_frame_id| pair (in the case of
54 // newly created pop up windows, for example) and in that case the request and
55 // the client callbacks will be deferred the request until a client is ready.
56 class IoThreadClientThrottle : public content::ResourceThrottle {
57  public:
58   IoThreadClientThrottle(int render_process_id,
59                          int render_frame_id,
60                          net::URLRequest* request);
61   virtual ~IoThreadClientThrottle();
62
63   // From content::ResourceThrottle
64   virtual void WillStartRequest(bool* defer) OVERRIDE;
65   virtual void WillRedirectRequest(const GURL& new_url, bool* defer) OVERRIDE;
66   virtual const char* GetNameForLogging() const OVERRIDE;
67
68   bool MaybeDeferRequest(bool* defer);
69   void OnIoThreadClientReady(int new_render_process_id,
70                              int new_render_frame_id);
71   bool MaybeBlockRequest();
72   bool ShouldBlockRequest();
73   int render_process_id() const { return render_process_id_; }
74   int render_frame_id() const { return render_frame_id_; }
75
76  private:
77   int render_process_id_;
78   int render_frame_id_;
79   net::URLRequest* request_;
80 };
81
82 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
83                                                int render_frame_id,
84                                                net::URLRequest* request)
85     : render_process_id_(render_process_id),
86       render_frame_id_(render_frame_id),
87       request_(request) { }
88
89 IoThreadClientThrottle::~IoThreadClientThrottle() {
90   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
91   static_cast<RuntimeResourceDispatcherHostDelegateAndroid*>(
92       XWalkContentBrowserClient::Get()->resource_dispatcher_host_delegate())->
93       RemovePendingThrottleOnIoThread(this);
94 }
95
96 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
97   if (render_frame_id_ < 1) {
98     // OPTIONS is used for preflighted requests which are generated internally.
99     DCHECK_EQ("OPTIONS", request_->method());
100     return;
101   }
102   DCHECK(render_process_id_);
103   if (!MaybeDeferRequest(defer)) {
104     MaybeBlockRequest();
105   }
106 }
107
108 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
109                                                  bool* defer) {
110   WillStartRequest(defer);
111 }
112
113 const char* IoThreadClientThrottle::GetNameForLogging() const {
114   return "IoThreadClientThrottle";
115 }
116
117 bool IoThreadClientThrottle::MaybeDeferRequest(bool* defer) {
118   *defer = false;
119
120   // Defer all requests of a pop up that is still not associated with Java
121   // client so that the client will get a chance to override requests.
122   scoped_ptr<XWalkContentsIoThreadClient> io_client =
123       XWalkContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
124   if (io_client && io_client->PendingAssociation()) {
125     *defer = true;
126     RuntimeResourceDispatcherHostDelegateAndroid::AddPendingThrottle(
127         render_process_id_, render_frame_id_, this);
128   }
129   return *defer;
130 }
131
132 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id,
133                                                    int new_render_frame_id) {
134   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
135
136   if (!MaybeBlockRequest()) {
137     controller()->Resume();
138   }
139 }
140
141 bool IoThreadClientThrottle::MaybeBlockRequest() {
142   if (ShouldBlockRequest()) {
143     controller()->CancelWithError(net::ERR_ACCESS_DENIED);
144     return true;
145   }
146   return false;
147 }
148
149 bool IoThreadClientThrottle::ShouldBlockRequest() {
150   scoped_ptr<XWalkContentsIoThreadClient> io_client =
151       XWalkContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
152   DCHECK(io_client.get());
153
154   // Part of implementation of WebSettings.allowContentAccess.
155   if (request_->url().SchemeIs(xwalk::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(xwalk::kAndroidResourcePath) != 0 &&
167          url.path().find(xwalk::kAndroidAssetPath) != 0)) {
168       return true;
169     }
170   }
171
172   if (io_client->ShouldBlockNetworkLoads()) {
173     if (request_->url().SchemeIs(url::kFtpScheme)) {
174       return true;
175     }
176     SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
177   } else {
178     XWalkContentsIoThreadClient::CacheMode cache_mode =
179         io_client->GetCacheMode();
180     switch (cache_mode) {
181       case XWalkContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
182         SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
183         break;
184       case XWalkContentsIoThreadClient::LOAD_NO_CACHE:
185         SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
186         break;
187       case XWalkContentsIoThreadClient::LOAD_CACHE_ONLY:
188         SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
189         break;
190       default:
191         break;
192     }
193   }
194   return false;
195 }
196
197 RuntimeResourceDispatcherHostDelegateAndroid::
198     RuntimeResourceDispatcherHostDelegateAndroid() {
199 }
200
201 RuntimeResourceDispatcherHostDelegateAndroid::
202     ~RuntimeResourceDispatcherHostDelegateAndroid() {
203 }
204
205 void RuntimeResourceDispatcherHostDelegateAndroid::RequestBeginning(
206     net::URLRequest* request,
207     content::ResourceContext* resource_context,
208     content::AppCacheService* appcache_service,
209     content::ResourceType resource_type,
210     ScopedVector<content::ResourceThrottle>* throttles) {
211   const content::ResourceRequestInfo* request_info =
212       content::ResourceRequestInfo::ForRequest(request);
213
214   // We allow intercepting only navigations within main frames. This
215   // is used to post onPageStarted. We handle shouldOverrideUrlLoading
216   // via a sync IPC for url loading in iframe.
217   if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
218     throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
219         request));
220   }
221
222   // If io_client is NULL, then the browser side objects have already been
223   // destroyed, so do not do anything to the request. Conversely if the
224   // request relates to a not-yet-created popup window, then the client will
225   // be non-NULL but PopupPendingAssociation() will be set.
226   scoped_ptr<XWalkContentsIoThreadClient> io_client =
227       XWalkContentsIoThreadClient::FromID(
228           request_info->GetChildID(), request_info->GetRenderFrameID());
229   if (!io_client)
230     return;
231
232   throttles->push_back(new IoThreadClientThrottle(
233       request_info->GetChildID(), request_info->GetRenderFrameID(), request));
234 }
235
236 void RuntimeResourceDispatcherHostDelegateAndroid::DownloadStarting(
237     net::URLRequest* request,
238     content::ResourceContext* resource_context,
239     int child_id,
240     int route_id,
241     int request_id,
242     bool is_content_initiated,
243     bool must_download,
244     ScopedVector<content::ResourceThrottle>* throttles) {
245   GURL url(request->url());
246   std::string user_agent;
247   std::string content_disposition;
248   std::string mime_type;
249   int64 content_length = request->GetExpectedContentSize();
250
251   request->extra_request_headers().GetHeader(
252       net::HttpRequestHeaders::kUserAgent, &user_agent);
253
254
255   net::HttpResponseHeaders* response_headers = request->response_headers();
256   if (response_headers) {
257     response_headers->GetNormalizedHeader("content-disposition",
258         &content_disposition);
259     response_headers->GetMimeType(&mime_type);
260   }
261
262   request->Cancel();
263
264   const content::ResourceRequestInfo* request_info =
265       content::ResourceRequestInfo::ForRequest(request);
266
267   scoped_ptr<XWalkContentsIoThreadClient> io_client =
268       XWalkContentsIoThreadClient::FromID(
269           child_id, request_info->GetRenderFrameID());
270
271   // POST request cannot be repeated in general, so prevent client from
272   // retrying the same request, even if it is with a GET.
273   if ("GET" == request->method() && io_client) {
274     io_client->NewDownload(url,
275                            user_agent,
276                            content_disposition,
277                            mime_type,
278                            content_length);
279   }
280 }
281
282 content::ResourceDispatcherHostLoginDelegate*
283     RuntimeResourceDispatcherHostDelegateAndroid::CreateLoginDelegate(
284         net::AuthChallengeInfo* auth_info,
285         net::URLRequest* request) {
286   return new XWalkLoginDelegate(auth_info, request);
287 }
288
289 bool RuntimeResourceDispatcherHostDelegateAndroid::HandleExternalProtocol(
290     const GURL& url,
291     int child_id,
292     int route_id) {
293   // On Android, there are many Uris need to be handled differently.
294   // e.g: sms:, tel:, mailto: and etc.
295   // So here return false to let embedders to decide which protocol
296   // to be handled.
297   return false;
298 }
299
300 void RuntimeResourceDispatcherHostDelegateAndroid::OnResponseStarted(
301     net::URLRequest* request,
302     content::ResourceContext* resource_context,
303     content::ResourceResponse* response,
304     IPC::Sender* sender) {
305   const content::ResourceRequestInfo* request_info =
306       content::ResourceRequestInfo::ForRequest(request);
307   if (!request_info) {
308     DLOG(FATAL) << "Started request without associated info: " <<
309         request->url();
310     return;
311   }
312
313   if (request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
314     // Check for x-auto-login header.
315     auto_login_parser::HeaderData header_data;
316     if (auto_login_parser::ParserHeaderInResponse(
317             request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
318       scoped_ptr<XWalkContentsIoThreadClient> io_client =
319           XWalkContentsIoThreadClient::FromID(request_info->GetChildID(),
320                                               request_info->GetRenderFrameID());
321       if (io_client) {
322         io_client->NewLoginRequest(
323             header_data.realm, header_data.account, header_data.args);
324       }
325     }
326   }
327 }
328
329 void RuntimeResourceDispatcherHostDelegateAndroid::
330 RemovePendingThrottleOnIoThread(
331     IoThreadClientThrottle* throttle) {
332   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
333   PendingThrottleMap::iterator it = pending_throttles_.find(
334       FrameRouteIDPair(throttle->render_process_id(),
335                        throttle->render_frame_id()));
336   if (it != pending_throttles_.end()) {
337     pending_throttles_.erase(it);
338   }
339 }
340
341 // static
342 void RuntimeResourceDispatcherHostDelegateAndroid::OnIoThreadClientReady(
343     int new_render_process_id,
344     int new_render_frame_id) {
345   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
346       base::Bind(
347           &RuntimeResourceDispatcherHostDelegateAndroid::
348           OnIoThreadClientReadyInternal,
349           base::Unretained(
350               static_cast<RuntimeResourceDispatcherHostDelegateAndroid*>(
351                   XWalkContentBrowserClient::Get()->
352                       resource_dispatcher_host_delegate())),
353           new_render_process_id, new_render_frame_id));
354 }
355
356 // static
357 void RuntimeResourceDispatcherHostDelegateAndroid::AddPendingThrottle(
358     int render_process_id,
359     int render_frame_id,
360     IoThreadClientThrottle* pending_throttle) {
361   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
362       base::Bind(
363           &RuntimeResourceDispatcherHostDelegateAndroid::
364               AddPendingThrottleOnIoThread,
365           base::Unretained(
366               static_cast<RuntimeResourceDispatcherHostDelegateAndroid*>(
367                   XWalkContentBrowserClient::Get()->
368                       resource_dispatcher_host_delegate())),
369           render_process_id, render_frame_id, pending_throttle));
370 }
371
372 void RuntimeResourceDispatcherHostDelegateAndroid::
373     AddPendingThrottleOnIoThread(
374         int render_process_id,
375         int render_frame_id,
376         IoThreadClientThrottle* pending_throttle) {
377   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
378   pending_throttles_.insert(
379       std::pair<FrameRouteIDPair, IoThreadClientThrottle*>(
380           FrameRouteIDPair(render_process_id, render_frame_id),
381           pending_throttle));
382 }
383
384 void RuntimeResourceDispatcherHostDelegateAndroid::
385 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));
391
392   if (it != pending_throttles_.end()) {
393     IoThreadClientThrottle* throttle = it->second;
394     throttle->OnIoThreadClientReady(new_render_process_id,
395                                     new_render_frame_id);
396     pending_throttles_.erase(it);
397   }
398 }
399
400 }  // namespace xwalk