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.
6 #include "xwalk/runtime/browser/runtime_resource_dispatcher_host_delegate_android.h"
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"
31 using content::BrowserThread;
32 using navigation_interception::InterceptNavigationDelegate;
33 using xwalk::XWalkContentsIoThreadClient;
36 base::LazyInstance<xwalk::RuntimeResourceDispatcherHostDelegateAndroid>
37 g_runtime_resource_dispatcher_host_delegate_android =
38 LAZY_INSTANCE_INITIALIZER;
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;
50 request->SetLoadFlags(load_flags);
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 {
63 IoThreadClientThrottle(int render_process_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 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_; }
82 int render_process_id_;
84 net::URLRequest* request_;
87 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
89 net::URLRequest* request)
90 : render_process_id_(render_process_id),
91 render_frame_id_(render_frame_id),
94 IoThreadClientThrottle::~IoThreadClientThrottle() {
95 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
96 g_runtime_resource_dispatcher_host_delegate_android.Get().
97 RemovePendingThrottleOnIoThread(this);
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());
106 DCHECK(render_process_id_);
107 if (!MaybeDeferRequest(defer)) {
112 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
114 WillStartRequest(defer);
117 const char* IoThreadClientThrottle::GetNameForLogging() const {
118 return "IoThreadClientThrottle";
121 bool IoThreadClientThrottle::MaybeDeferRequest(bool* defer) {
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()) {
130 RuntimeResourceDispatcherHostDelegateAndroid::AddPendingThrottle(
131 render_process_id_, render_frame_id_, this);
136 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id,
137 int new_render_frame_id) {
138 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
140 if (!MaybeBlockRequest()) {
141 controller()->Resume();
145 bool IoThreadClientThrottle::MaybeBlockRequest() {
146 if (ShouldBlockRequest()) {
147 controller()->CancelWithError(net::ERR_ACCESS_DENIED);
153 bool IoThreadClientThrottle::ShouldBlockRequest() {
154 scoped_ptr<XWalkContentsIoThreadClient> io_client =
155 XWalkContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
156 DCHECK(io_client.get());
158 // Part of implementation of WebSettings.allowContentAccess.
159 if (request_->url().SchemeIs(xwalk::kContentScheme) &&
160 io_client->ShouldBlockContentUrls()) {
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)) {
176 if (io_client->ShouldBlockNetworkLoads()) {
177 if (request_->url().SchemeIs(url::kFtpScheme)) {
180 SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
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);
188 case XWalkContentsIoThreadClient::LOAD_NO_CACHE:
189 SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
191 case XWalkContentsIoThreadClient::LOAD_CACHE_ONLY:
192 SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
201 RuntimeResourceDispatcherHostDelegateAndroid::
202 RuntimeResourceDispatcherHostDelegateAndroid() {
205 RuntimeResourceDispatcherHostDelegateAndroid::
206 ~RuntimeResourceDispatcherHostDelegateAndroid() {
210 void RuntimeResourceDispatcherHostDelegateAndroid::
211 ResourceDispatcherHostCreated() {
212 content::ResourceDispatcherHost::Get()->SetDelegate(
213 &g_runtime_resource_dispatcher_host_delegate_android.Get());
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);
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(
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());
243 throttles->push_back(new IoThreadClientThrottle(
244 request_info->GetChildID(), request_info->GetRenderFrameID(), request));
247 void RuntimeResourceDispatcherHostDelegateAndroid::DownloadStarting(
248 net::URLRequest* request,
249 content::ResourceContext* resource_context,
253 bool is_content_initiated,
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);
275 const content::ResourceRequestInfo* request_info =
276 content::ResourceRequestInfo::ForRequest(request);
278 scoped_ptr<XWalkContentsIoThreadClient> io_client =
279 XWalkContentsIoThreadClient::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,
293 content::ResourceDispatcherHostLoginDelegate*
294 RuntimeResourceDispatcherHostDelegateAndroid::CreateLoginDelegate(
295 net::AuthChallengeInfo* auth_info,
296 net::URLRequest* request) {
297 return new XWalkLoginDelegate(auth_info, request);
300 bool RuntimeResourceDispatcherHostDelegateAndroid::HandleExternalProtocol(
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
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);
319 DLOG(FATAL) << "Started request without associated info: " <<
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());
333 io_client->NewLoginRequest(
334 header_data.realm, header_data.account, header_data.args);
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);
353 void RuntimeResourceDispatcherHostDelegateAndroid::OnIoThreadClientReady(
354 int new_render_process_id,
355 int new_render_frame_id) {
356 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
358 &RuntimeResourceDispatcherHostDelegateAndroid::
359 OnIoThreadClientReadyInternal,
361 g_runtime_resource_dispatcher_host_delegate_android.Pointer()),
362 new_render_process_id, new_render_frame_id));
366 void RuntimeResourceDispatcherHostDelegateAndroid::AddPendingThrottle(
367 int render_process_id,
369 IoThreadClientThrottle* pending_throttle) {
370 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
372 &RuntimeResourceDispatcherHostDelegateAndroid::
373 AddPendingThrottleOnIoThread,
375 g_runtime_resource_dispatcher_host_delegate_android.Pointer()),
376 render_process_id, render_frame_id, pending_throttle));
379 void RuntimeResourceDispatcherHostDelegateAndroid::
380 AddPendingThrottleOnIoThread(
381 int render_process_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),
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));
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);