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_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 #include "xwalk/runtime/common/xwalk_content_client.h"
31 using content::BrowserThread;
32 using navigation_interception::InterceptNavigationDelegate;
33 using xwalk::XWalkContentsIoThreadClient;
36 void SetCacheControlFlag(
37 net::URLRequest* request, int flag) {
38 const int all_cache_control_flags = net::LOAD_BYPASS_CACHE |
39 net::LOAD_VALIDATE_CACHE |
40 net::LOAD_PREFERRING_CACHE |
41 net::LOAD_ONLY_FROM_CACHE;
42 DCHECK((flag & all_cache_control_flags) == flag);
43 int load_flags = request->load_flags();
44 load_flags &= ~all_cache_control_flags;
46 request->SetLoadFlags(load_flags);
52 // Calls through the IoThreadClient to check the embedders settings to determine
53 // if the request should be cancelled. There may not always be an IoThreadClient
54 // available for the |render_process_id|, |render_frame_id| pair (in the case of
55 // newly created pop up windows, for example) and in that case the request and
56 // the client callbacks will be deferred the request until a client is ready.
57 class IoThreadClientThrottle : public content::ResourceThrottle {
59 IoThreadClientThrottle(int render_process_id,
61 net::URLRequest* request);
62 virtual ~IoThreadClientThrottle();
64 // From content::ResourceThrottle
65 void WillStartRequest(bool* defer) override;
66 void WillRedirectRequest(const GURL& new_url, bool* defer) override;
67 const char* GetNameForLogging() const override;
69 bool MaybeDeferRequest(bool* defer);
70 void OnIoThreadClientReady(int new_render_process_id,
71 int new_render_frame_id);
72 bool MaybeBlockRequest();
73 bool ShouldBlockRequest();
74 int render_process_id() const { return render_process_id_; }
75 int render_frame_id() const { return render_frame_id_; }
78 int render_process_id_;
80 net::URLRequest* request_;
83 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
85 net::URLRequest* request)
86 : render_process_id_(render_process_id),
87 render_frame_id_(render_frame_id),
90 IoThreadClientThrottle::~IoThreadClientThrottle() {
91 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
92 static_cast<RuntimeResourceDispatcherHostDelegateAndroid*>(
93 XWalkContentBrowserClient::Get()->resource_dispatcher_host_delegate())->
94 RemovePendingThrottleOnIoThread(this);
97 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
98 if (render_frame_id_ < 1) {
99 // OPTIONS is used for preflighted requests which are generated internally.
100 DCHECK_EQ("OPTIONS", request_->method());
103 DCHECK(render_process_id_);
104 if (!MaybeDeferRequest(defer)) {
109 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
111 WillStartRequest(defer);
114 const char* IoThreadClientThrottle::GetNameForLogging() const {
115 return "IoThreadClientThrottle";
118 bool IoThreadClientThrottle::MaybeDeferRequest(bool* defer) {
121 // Defer all requests of a pop up that is still not associated with Java
122 // client so that the client will get a chance to override requests.
123 scoped_ptr<XWalkContentsIoThreadClient> io_client =
124 XWalkContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
125 if (io_client && io_client->PendingAssociation()) {
127 RuntimeResourceDispatcherHostDelegateAndroid::AddPendingThrottle(
128 render_process_id_, render_frame_id_, this);
133 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id,
134 int new_render_frame_id) {
135 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
137 if (!MaybeBlockRequest()) {
138 controller()->Resume();
142 bool IoThreadClientThrottle::MaybeBlockRequest() {
143 if (ShouldBlockRequest()) {
144 controller()->CancelWithError(net::ERR_ACCESS_DENIED);
150 bool IoThreadClientThrottle::ShouldBlockRequest() {
151 scoped_ptr<XWalkContentsIoThreadClient> io_client =
152 XWalkContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
153 DCHECK(io_client.get());
155 // Part of implementation of WebSettings.allowContentAccess.
156 if (request_->url().SchemeIs(xwalk::kContentScheme) &&
157 io_client->ShouldBlockContentUrls()) {
161 // Part of implementation of WebSettings.allowFileAccess.
162 if (request_->url().SchemeIsFile() &&
163 io_client->ShouldBlockFileUrls()) {
164 const GURL& url = request_->url();
165 if (!url.has_path() ||
166 // Application's assets and resources are always available.
167 (url.path().find(xwalk::kAndroidResourcePath) != 0 &&
168 url.path().find(xwalk::kAndroidAssetPath) != 0)) {
173 if (io_client->ShouldBlockNetworkLoads()) {
174 if (request_->url().SchemeIs(url::kFtpScheme)) {
177 SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
179 XWalkContentsIoThreadClient::CacheMode cache_mode =
180 io_client->GetCacheMode();
181 switch (cache_mode) {
182 case XWalkContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
183 SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
185 case XWalkContentsIoThreadClient::LOAD_NO_CACHE:
186 SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
188 case XWalkContentsIoThreadClient::LOAD_CACHE_ONLY:
189 SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
198 RuntimeResourceDispatcherHostDelegateAndroid::
199 RuntimeResourceDispatcherHostDelegateAndroid() {
202 RuntimeResourceDispatcherHostDelegateAndroid::
203 ~RuntimeResourceDispatcherHostDelegateAndroid() {
206 void RuntimeResourceDispatcherHostDelegateAndroid::RequestBeginning(
207 net::URLRequest* request,
208 content::ResourceContext* resource_context,
209 content::AppCacheService* appcache_service,
210 content::ResourceType resource_type,
211 ScopedVector<content::ResourceThrottle>* throttles) {
212 const content::ResourceRequestInfo* request_info =
213 content::ResourceRequestInfo::ForRequest(request);
215 // We allow intercepting only navigations within main frames. This
216 // is used to post onPageStarted. We handle shouldOverrideUrlLoading
217 // via a sync IPC for url loading in iframe.
218 if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME) {
219 throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
223 // If io_client is NULL, then the browser side objects have already been
224 // destroyed, so do not do anything to the request. Conversely if the
225 // request relates to a not-yet-created popup window, then the client will
226 // be non-NULL but PopupPendingAssociation() will be set.
227 scoped_ptr<XWalkContentsIoThreadClient> io_client =
228 XWalkContentsIoThreadClient::FromID(
229 request_info->GetChildID(), request_info->GetRenderFrameID());
233 throttles->push_back(new IoThreadClientThrottle(
234 request_info->GetChildID(), request_info->GetRenderFrameID(), request));
237 void RuntimeResourceDispatcherHostDelegateAndroid::DownloadStarting(
238 net::URLRequest* request,
239 content::ResourceContext* resource_context,
243 bool is_content_initiated,
245 ScopedVector<content::ResourceThrottle>* throttles) {
246 GURL url(request->url());
247 std::string user_agent;
248 std::string content_disposition;
249 std::string mime_type;
250 int64 content_length = request->GetExpectedContentSize();
252 if (!request->extra_request_headers().GetHeader(
253 net::HttpRequestHeaders::kUserAgent, &user_agent))
254 user_agent = xwalk::GetUserAgent();
256 net::HttpResponseHeaders* response_headers = request->response_headers();
257 if (response_headers) {
258 response_headers->GetNormalizedHeader("content-disposition",
259 &content_disposition);
260 response_headers->GetMimeType(&mime_type);
265 const content::ResourceRequestInfo* request_info =
266 content::ResourceRequestInfo::ForRequest(request);
268 scoped_ptr<XWalkContentsIoThreadClient> io_client =
269 XWalkContentsIoThreadClient::FromID(
270 child_id, request_info->GetRenderFrameID());
272 // POST request cannot be repeated in general, so prevent client from
273 // retrying the same request, even if it is with a GET.
274 if ("GET" == request->method() && io_client) {
275 io_client->NewDownload(url,
283 content::ResourceDispatcherHostLoginDelegate*
284 RuntimeResourceDispatcherHostDelegateAndroid::CreateLoginDelegate(
285 net::AuthChallengeInfo* auth_info,
286 net::URLRequest* request) {
287 return new XWalkLoginDelegate(auth_info, request);
290 bool RuntimeResourceDispatcherHostDelegateAndroid::HandleExternalProtocol(
294 // On Android, there are many Uris need to be handled differently.
295 // e.g: sms:, tel:, mailto: and etc.
296 // So here return false to let embedders to decide which protocol
301 void RuntimeResourceDispatcherHostDelegateAndroid::OnResponseStarted(
302 net::URLRequest* request,
303 content::ResourceContext* resource_context,
304 content::ResourceResponse* response,
305 IPC::Sender* sender) {
306 const content::ResourceRequestInfo* request_info =
307 content::ResourceRequestInfo::ForRequest(request);
309 DLOG(FATAL) << "Started request without associated info: " <<
314 if (request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
315 // Check for x-auto-login header.
316 auto_login_parser::HeaderData header_data;
317 if (auto_login_parser::ParserHeaderInResponse(
318 request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
319 scoped_ptr<XWalkContentsIoThreadClient> io_client =
320 XWalkContentsIoThreadClient::FromID(request_info->GetChildID(),
321 request_info->GetRenderFrameID());
323 io_client->NewLoginRequest(
324 header_data.realm, header_data.account, header_data.args);
330 void RuntimeResourceDispatcherHostDelegateAndroid::
331 RemovePendingThrottleOnIoThread(
332 IoThreadClientThrottle* throttle) {
333 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
334 PendingThrottleMap::iterator it = pending_throttles_.find(
335 FrameRouteIDPair(throttle->render_process_id(),
336 throttle->render_frame_id()));
337 if (it != pending_throttles_.end()) {
338 pending_throttles_.erase(it);
343 void RuntimeResourceDispatcherHostDelegateAndroid::OnIoThreadClientReady(
344 int new_render_process_id,
345 int new_render_frame_id) {
346 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
348 &RuntimeResourceDispatcherHostDelegateAndroid::
349 OnIoThreadClientReadyInternal,
351 static_cast<RuntimeResourceDispatcherHostDelegateAndroid*>(
352 XWalkContentBrowserClient::Get()->
353 resource_dispatcher_host_delegate())),
354 new_render_process_id, new_render_frame_id));
358 void RuntimeResourceDispatcherHostDelegateAndroid::AddPendingThrottle(
359 int render_process_id,
361 IoThreadClientThrottle* pending_throttle) {
362 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
364 &RuntimeResourceDispatcherHostDelegateAndroid::
365 AddPendingThrottleOnIoThread,
367 static_cast<RuntimeResourceDispatcherHostDelegateAndroid*>(
368 XWalkContentBrowserClient::Get()->
369 resource_dispatcher_host_delegate())),
370 render_process_id, render_frame_id, pending_throttle));
373 void RuntimeResourceDispatcherHostDelegateAndroid::
374 AddPendingThrottleOnIoThread(
375 int render_process_id,
377 IoThreadClientThrottle* pending_throttle) {
378 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
379 pending_throttles_.insert(
380 std::pair<FrameRouteIDPair, IoThreadClientThrottle*>(
381 FrameRouteIDPair(render_process_id, render_frame_id),
385 void RuntimeResourceDispatcherHostDelegateAndroid::
386 OnIoThreadClientReadyInternal(
387 int new_render_process_id,
388 int new_render_frame_id) {
389 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
390 PendingThrottleMap::iterator it = pending_throttles_.find(
391 FrameRouteIDPair(new_render_process_id, new_render_frame_id));
393 if (it != pending_throttles_.end()) {
394 IoThreadClientThrottle* throttle = it->second;
395 throttle->OnIoThreadClientReady(new_render_process_id,
396 new_render_frame_id);
397 pending_throttles_.erase(it);