Home | History | Annotate | Download | only in renderer_host
      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/common/url_constants.h"
     12 #include "base/memory/scoped_ptr.h"
     13 #include "base/memory/scoped_vector.h"
     14 #include "components/auto_login_parser/auto_login_parser.h"
     15 #include "components/navigation_interception/intercept_navigation_delegate.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "content/public/browser/resource_controller.h"
     18 #include "content/public/browser/resource_dispatcher_host.h"
     19 #include "content/public/browser/resource_dispatcher_host_login_delegate.h"
     20 #include "content/public/browser/resource_request_info.h"
     21 #include "content/public/browser/resource_throttle.h"
     22 #include "content/public/common/url_constants.h"
     23 #include "net/base/load_flags.h"
     24 #include "net/http/http_response_headers.h"
     25 #include "net/url_request/url_request.h"
     26 
     27 using android_webview::AwContentsIoThreadClient;
     28 using content::BrowserThread;
     29 using navigation_interception::InterceptNavigationDelegate;
     30 
     31 namespace {
     32 
     33 base::LazyInstance<android_webview::AwResourceDispatcherHostDelegate>
     34     g_webview_resource_dispatcher_host_delegate = LAZY_INSTANCE_INITIALIZER;
     35 
     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;
     45   load_flags |= flag;
     46   request->set_load_flags(load_flags);
     47 }
     48 
     49 }  // namespace
     50 
     51 namespace android_webview {
     52 
     53 // Calls through the IoThreadClient to check the embedders settings to determine
     54 // if the request should be cancelled. There may not always be an IoThreadClient
     55 // available for the |child_id|, |route_id| pair (in the case of newly created
     56 // pop up windows, for example) and in that case the request and the client
     57 // callbacks will be deferred the request until a client is ready.
     58 class IoThreadClientThrottle : public content::ResourceThrottle {
     59  public:
     60   IoThreadClientThrottle(int child_id,
     61                          int route_id,
     62                          net::URLRequest* request);
     63   virtual ~IoThreadClientThrottle();
     64 
     65   // From content::ResourceThrottle
     66   virtual void WillStartRequest(bool* defer) OVERRIDE;
     67   virtual void WillRedirectRequest(const GURL& new_url, bool* defer) OVERRIDE;
     68 
     69   void OnIoThreadClientReady(int new_child_id, int new_route_id);
     70   bool MaybeBlockRequest();
     71   bool ShouldBlockRequest();
     72   int get_child_id() const { return child_id_; }
     73   int get_route_id() const { return route_id_; }
     74 
     75 private:
     76   int child_id_;
     77   int route_id_;
     78   net::URLRequest* request_;
     79 };
     80 
     81 IoThreadClientThrottle::IoThreadClientThrottle(int child_id,
     82                                                int route_id,
     83                                                net::URLRequest* request)
     84     : child_id_(child_id),
     85       route_id_(route_id),
     86       request_(request) { }
     87 
     88 IoThreadClientThrottle::~IoThreadClientThrottle() {
     89   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     90   g_webview_resource_dispatcher_host_delegate.Get().
     91       RemovePendingThrottleOnIoThread(this);
     92 }
     93 
     94 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
     95   // TODO(sgurun): This block can be removed when crbug.com/277937 is fixed.
     96   if (route_id_ < 1) {
     97     // OPTIONS is used for preflighted requests which are generated internally.
     98     DCHECK_EQ("OPTIONS", request_->method());
     99     return;
    100   }
    101   DCHECK(child_id_);
    102   *defer = false;
    103   // Defer all requests of a pop up that is still not associated with Java
    104   // client so that the client will get a chance to override requests.
    105   scoped_ptr<AwContentsIoThreadClient> io_client =
    106       AwContentsIoThreadClient::FromID(child_id_, route_id_);
    107   if (io_client && io_client->PendingAssociation()) {
    108     *defer = true;
    109     AwResourceDispatcherHostDelegate::AddPendingThrottle(
    110         child_id_, route_id_, this);
    111   } else {
    112     MaybeBlockRequest();
    113   }
    114 }
    115 
    116 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
    117                                                  bool* defer) {
    118   WillStartRequest(defer);
    119 }
    120 
    121 void IoThreadClientThrottle::OnIoThreadClientReady(int new_child_id,
    122                                                    int new_route_id) {
    123   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    124 
    125   if (!MaybeBlockRequest()) {
    126     controller()->Resume();
    127   }
    128 }
    129 
    130 bool IoThreadClientThrottle::MaybeBlockRequest() {
    131   if (ShouldBlockRequest()) {
    132     controller()->CancelWithError(net::ERR_ACCESS_DENIED);
    133     return true;
    134   }
    135   return false;
    136 }
    137 
    138 bool IoThreadClientThrottle::ShouldBlockRequest() {
    139   scoped_ptr<AwContentsIoThreadClient> io_client =
    140       AwContentsIoThreadClient::FromID(child_id_, route_id_);
    141   if (!io_client)
    142     return false;
    143 
    144   // Part of implementation of WebSettings.allowContentAccess.
    145   if (request_->url().SchemeIs(android_webview::kContentScheme) &&
    146       io_client->ShouldBlockContentUrls()) {
    147     return true;
    148   }
    149 
    150   // Part of implementation of WebSettings.allowFileAccess.
    151   if (request_->url().SchemeIsFile() &&
    152       io_client->ShouldBlockFileUrls()) {
    153     const GURL& url = request_->url();
    154     if (!url.has_path() ||
    155         // Application's assets and resources are always available.
    156         (url.path().find(android_webview::kAndroidResourcePath) != 0 &&
    157          url.path().find(android_webview::kAndroidAssetPath) != 0)) {
    158       return true;
    159     }
    160   }
    161 
    162   if (io_client->ShouldBlockNetworkLoads()) {
    163     if (request_->url().SchemeIs(chrome::kFtpScheme)) {
    164       return true;
    165     }
    166     SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
    167   } else {
    168     AwContentsIoThreadClient::CacheMode cache_mode = io_client->GetCacheMode();
    169     switch(cache_mode) {
    170       case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
    171         SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
    172         break;
    173       case AwContentsIoThreadClient::LOAD_NO_CACHE:
    174         SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
    175         break;
    176       case AwContentsIoThreadClient::LOAD_CACHE_ONLY:
    177         SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
    178         break;
    179       default:
    180         break;
    181     }
    182   }
    183   return false;
    184 }
    185 
    186 // static
    187 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
    188   content::ResourceDispatcherHost::Get()->SetDelegate(
    189       &g_webview_resource_dispatcher_host_delegate.Get());
    190 }
    191 
    192 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
    193     : content::ResourceDispatcherHostDelegate() {
    194 }
    195 
    196 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
    197 }
    198 
    199 void AwResourceDispatcherHostDelegate::RequestBeginning(
    200     net::URLRequest* request,
    201     content::ResourceContext* resource_context,
    202     appcache::AppCacheService* appcache_service,
    203     ResourceType::Type resource_type,
    204     int child_id,
    205     int route_id,
    206     bool is_continuation_of_transferred_request,
    207     ScopedVector<content::ResourceThrottle>* throttles) {
    208 
    209   // We always push the throttles here. Checking the existence of io_client
    210   // is racy when a popup window is created. That is because RequestBeginning
    211   // is called whether or not requests are blocked via BlockRequestForRoute()
    212   // however io_client may or may not be ready at the time depending on whether
    213   // webcontents is created.
    214   throttles->push_back(new IoThreadClientThrottle(
    215       child_id, route_id, request));
    216 
    217   // We only intercept navigations with main frames since this throttle is
    218   // exclusively for posting onPageStarted's.
    219   if (resource_type == ResourceType::MAIN_FRAME) {
    220     throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
    221         request));
    222   }
    223 }
    224 
    225 void AwResourceDispatcherHostDelegate::DownloadStarting(
    226     net::URLRequest* request,
    227     content::ResourceContext* resource_context,
    228     int child_id,
    229     int route_id,
    230     int request_id,
    231     bool is_content_initiated,
    232     bool must_download,
    233     ScopedVector<content::ResourceThrottle>* throttles) {
    234   GURL url(request->url());
    235   std::string user_agent;
    236   std::string content_disposition;
    237   std::string mime_type;
    238   int64 content_length = request->GetExpectedContentSize();
    239 
    240   request->extra_request_headers().GetHeader(
    241       net::HttpRequestHeaders::kUserAgent, &user_agent);
    242 
    243 
    244   net::HttpResponseHeaders* response_headers = request->response_headers();
    245   if (response_headers) {
    246     response_headers->GetNormalizedHeader("content-disposition",
    247         &content_disposition);
    248     response_headers->GetMimeType(&mime_type);
    249   }
    250 
    251   request->Cancel();
    252 
    253   scoped_ptr<AwContentsIoThreadClient> io_client =
    254       AwContentsIoThreadClient::FromID(child_id, route_id);
    255 
    256   // POST request cannot be repeated in general, so prevent client from
    257   // retrying the same request, even if it is with a GET.
    258   if ("GET" == request->method() && io_client) {
    259     io_client->NewDownload(url,
    260                            user_agent,
    261                            content_disposition,
    262                            mime_type,
    263                            content_length);
    264   }
    265 }
    266 
    267 bool AwResourceDispatcherHostDelegate::AcceptAuthRequest(
    268     net::URLRequest* request,
    269     net::AuthChallengeInfo* auth_info) {
    270   return true;
    271 }
    272 
    273 bool AwResourceDispatcherHostDelegate::AcceptSSLClientCertificateRequest(
    274     net::URLRequest* request,
    275     net::SSLCertRequestInfo* cert_info) {
    276   // WebView does not support client certificate selection, however it does
    277   // send a no-certificate response to the server to allow it decide how to
    278   // proceed. The base class returns false here, which causes the entire
    279   // resource request to be abort. We don't want that, so we must return true
    280   // here (and subsequently complete the request in
    281   // AwContentBrowserClient::SelectClientCertificate) to get the intended
    282   // behavior.
    283   return true;
    284 }
    285 
    286 content::ResourceDispatcherHostLoginDelegate*
    287     AwResourceDispatcherHostDelegate::CreateLoginDelegate(
    288         net::AuthChallengeInfo* auth_info,
    289         net::URLRequest* request) {
    290   return new AwLoginDelegate(auth_info, request);
    291 }
    292 
    293 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL& url,
    294                                                               int child_id,
    295                                                               int route_id) {
    296   // The AwURLRequestJobFactory implementation should ensure this method never
    297   // gets called.
    298   NOTREACHED();
    299   return false;
    300 }
    301 
    302 void AwResourceDispatcherHostDelegate::OnResponseStarted(
    303     net::URLRequest* request,
    304     content::ResourceContext* resource_context,
    305     content::ResourceResponse* response,
    306     IPC::Sender* sender) {
    307   const content::ResourceRequestInfo* request_info =
    308       content::ResourceRequestInfo::ForRequest(request);
    309   if (!request_info) {
    310     DLOG(FATAL) << "Started request without associated info: " <<
    311         request->url();
    312     return;
    313   }
    314 
    315   if (request_info->GetResourceType() == ResourceType::MAIN_FRAME) {
    316     // Check for x-auto-login header.
    317     auto_login_parser::HeaderData header_data;
    318     if (auto_login_parser::ParserHeaderInResponse(
    319             request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
    320       scoped_ptr<AwContentsIoThreadClient> io_client =
    321           AwContentsIoThreadClient::FromID(request_info->GetChildID(),
    322                                            request_info->GetRouteID());
    323       if (io_client) {
    324         io_client->NewLoginRequest(
    325             header_data.realm, header_data.account, header_data.args);
    326       }
    327     }
    328   }
    329 }
    330 
    331 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
    332     IoThreadClientThrottle* throttle) {
    333   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    334   PendingThrottleMap::iterator it = pending_throttles_.find(
    335       ChildRouteIDPair(throttle->get_child_id(), throttle->get_route_id()));
    336   if (it != pending_throttles_.end()) {
    337     pending_throttles_.erase(it);
    338   }
    339 }
    340 
    341 // static
    342 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
    343     int new_child_id,
    344     int new_route_id) {
    345   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    346       base::Bind(
    347           &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal,
    348           base::Unretained(
    349               g_webview_resource_dispatcher_host_delegate.Pointer()),
    350           new_child_id, new_route_id));
    351 }
    352 
    353 // static
    354 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
    355     int child_id,
    356     int route_id,
    357     IoThreadClientThrottle* pending_throttle) {
    358   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    359       base::Bind(
    360           &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread,
    361           base::Unretained(
    362               g_webview_resource_dispatcher_host_delegate.Pointer()),
    363           child_id, route_id, pending_throttle));
    364 }
    365 
    366 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
    367     int child_id,
    368     int route_id,
    369     IoThreadClientThrottle* pending_throttle) {
    370   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    371   pending_throttles_.insert(
    372       std::pair<ChildRouteIDPair, IoThreadClientThrottle*>(
    373           ChildRouteIDPair(child_id, route_id), pending_throttle));
    374 }
    375 
    376 void AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal(
    377     int new_child_id,
    378     int new_route_id) {
    379   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    380   PendingThrottleMap::iterator it = pending_throttles_.find(
    381       ChildRouteIDPair(new_child_id, new_route_id));
    382 
    383   if (it != pending_throttles_.end()) {
    384     IoThreadClientThrottle* throttle = it->second;
    385     throttle->OnIoThreadClientReady(new_child_id, new_route_id);
    386     pending_throttles_.erase(it);
    387   }
    388 }
    389 
    390 }  // namespace android_webview
    391