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   bool MaybeDeferRequest(bool* defer);
     70   void OnIoThreadClientReady(int new_child_id, int new_route_id);
     71   bool MaybeBlockRequest();
     72   bool ShouldBlockRequest();
     73   int get_child_id() const { return child_id_; }
     74   int get_route_id() const { return route_id_; }
     75 
     76 private:
     77   int child_id_;
     78   int route_id_;
     79   net::URLRequest* request_;
     80 };
     81 
     82 IoThreadClientThrottle::IoThreadClientThrottle(int child_id,
     83                                                int route_id,
     84                                                net::URLRequest* request)
     85     : child_id_(child_id),
     86       route_id_(route_id),
     87       request_(request) { }
     88 
     89 IoThreadClientThrottle::~IoThreadClientThrottle() {
     90   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     91   g_webview_resource_dispatcher_host_delegate.Get().
     92       RemovePendingThrottleOnIoThread(this);
     93 }
     94 
     95 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
     96   // TODO(sgurun): This block can be removed when crbug.com/277937 is fixed.
     97   if (route_id_ < 1) {
     98     // OPTIONS is used for preflighted requests which are generated internally.
     99     DCHECK_EQ("OPTIONS", request_->method());
    100     return;
    101   }
    102   DCHECK(child_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 bool IoThreadClientThrottle::MaybeDeferRequest(bool* defer) {
    114   *defer = false;
    115 
    116   // Defer all requests of a pop up that is still not associated with Java
    117   // client so that the client will get a chance to override requests.
    118   scoped_ptr<AwContentsIoThreadClient> io_client =
    119       AwContentsIoThreadClient::FromID(child_id_, route_id_);
    120   if (io_client && io_client->PendingAssociation()) {
    121     *defer = true;
    122     AwResourceDispatcherHostDelegate::AddPendingThrottle(
    123         child_id_, route_id_, this);
    124   }
    125   return *defer;
    126 }
    127 
    128 void IoThreadClientThrottle::OnIoThreadClientReady(int new_child_id,
    129                                                    int new_route_id) {
    130   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    131 
    132   if (!MaybeBlockRequest()) {
    133     controller()->Resume();
    134   }
    135 }
    136 
    137 bool IoThreadClientThrottle::MaybeBlockRequest() {
    138   if (ShouldBlockRequest()) {
    139     controller()->CancelWithError(net::ERR_ACCESS_DENIED);
    140     return true;
    141   }
    142   return false;
    143 }
    144 
    145 bool IoThreadClientThrottle::ShouldBlockRequest() {
    146   scoped_ptr<AwContentsIoThreadClient> io_client =
    147       AwContentsIoThreadClient::FromID(child_id_, route_id_);
    148   if (!io_client)
    149     return false;
    150 
    151   // Part of implementation of WebSettings.allowContentAccess.
    152   if (request_->url().SchemeIs(android_webview::kContentScheme) &&
    153       io_client->ShouldBlockContentUrls()) {
    154     return true;
    155   }
    156 
    157   // Part of implementation of WebSettings.allowFileAccess.
    158   if (request_->url().SchemeIsFile() &&
    159       io_client->ShouldBlockFileUrls()) {
    160     const GURL& url = request_->url();
    161     if (!url.has_path() ||
    162         // Application's assets and resources are always available.
    163         (url.path().find(android_webview::kAndroidResourcePath) != 0 &&
    164          url.path().find(android_webview::kAndroidAssetPath) != 0)) {
    165       return true;
    166     }
    167   }
    168 
    169   if (io_client->ShouldBlockNetworkLoads()) {
    170     if (request_->url().SchemeIs(chrome::kFtpScheme)) {
    171       return true;
    172     }
    173     SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
    174   } else {
    175     AwContentsIoThreadClient::CacheMode cache_mode = io_client->GetCacheMode();
    176     switch(cache_mode) {
    177       case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
    178         SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
    179         break;
    180       case AwContentsIoThreadClient::LOAD_NO_CACHE:
    181         SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
    182         break;
    183       case AwContentsIoThreadClient::LOAD_CACHE_ONLY:
    184         SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
    185         break;
    186       default:
    187         break;
    188     }
    189   }
    190   return false;
    191 }
    192 
    193 // static
    194 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
    195   content::ResourceDispatcherHost::Get()->SetDelegate(
    196       &g_webview_resource_dispatcher_host_delegate.Get());
    197 }
    198 
    199 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
    200     : content::ResourceDispatcherHostDelegate() {
    201 }
    202 
    203 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
    204 }
    205 
    206 void AwResourceDispatcherHostDelegate::RequestBeginning(
    207     net::URLRequest* request,
    208     content::ResourceContext* resource_context,
    209     appcache::AppCacheService* appcache_service,
    210     ResourceType::Type resource_type,
    211     int child_id,
    212     int route_id,
    213     bool is_continuation_of_transferred_request,
    214     ScopedVector<content::ResourceThrottle>* throttles) {
    215   // If io_client is NULL, then the browser side objects have already been
    216   // destroyed, so do not do anything to the request. Conversely if the
    217   // request relates to a not-yet-created popup window, then the client will
    218   // be non-NULL but PopupPendingAssociation() will be set.
    219   scoped_ptr<AwContentsIoThreadClient> io_client =
    220       AwContentsIoThreadClient::FromID(child_id, route_id);
    221   if (!io_client)
    222     return;
    223 
    224   throttles->push_back(new IoThreadClientThrottle(
    225       child_id, route_id, request));
    226 
    227   // We only intercept navigations with main frames since this throttle is
    228   // exclusively for posting onPageStarted's.
    229   if (resource_type == ResourceType::MAIN_FRAME) {
    230     throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
    231         request));
    232   }
    233 }
    234 
    235 void AwResourceDispatcherHostDelegate::DownloadStarting(
    236     net::URLRequest* request,
    237     content::ResourceContext* resource_context,
    238     int child_id,
    239     int route_id,
    240     int request_id,
    241     bool is_content_initiated,
    242     bool must_download,
    243     ScopedVector<content::ResourceThrottle>* throttles) {
    244   GURL url(request->url());
    245   std::string user_agent;
    246   std::string content_disposition;
    247   std::string mime_type;
    248   int64 content_length = request->GetExpectedContentSize();
    249 
    250   request->extra_request_headers().GetHeader(
    251       net::HttpRequestHeaders::kUserAgent, &user_agent);
    252 
    253 
    254   net::HttpResponseHeaders* response_headers = request->response_headers();
    255   if (response_headers) {
    256     response_headers->GetNormalizedHeader("content-disposition",
    257         &content_disposition);
    258     response_headers->GetMimeType(&mime_type);
    259   }
    260 
    261   request->Cancel();
    262 
    263   scoped_ptr<AwContentsIoThreadClient> io_client =
    264       AwContentsIoThreadClient::FromID(child_id, route_id);
    265 
    266   // POST request cannot be repeated in general, so prevent client from
    267   // retrying the same request, even if it is with a GET.
    268   if ("GET" == request->method() && io_client) {
    269     io_client->NewDownload(url,
    270                            user_agent,
    271                            content_disposition,
    272                            mime_type,
    273                            content_length);
    274   }
    275 }
    276 
    277 bool AwResourceDispatcherHostDelegate::AcceptAuthRequest(
    278     net::URLRequest* request,
    279     net::AuthChallengeInfo* auth_info) {
    280   return true;
    281 }
    282 
    283 content::ResourceDispatcherHostLoginDelegate*
    284     AwResourceDispatcherHostDelegate::CreateLoginDelegate(
    285         net::AuthChallengeInfo* auth_info,
    286         net::URLRequest* request) {
    287   return new AwLoginDelegate(auth_info, request);
    288 }
    289 
    290 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL& url,
    291                                                               int child_id,
    292                                                               int route_id) {
    293   // The AwURLRequestJobFactory implementation should ensure this method never
    294   // gets called.
    295   NOTREACHED();
    296   return false;
    297 }
    298 
    299 void AwResourceDispatcherHostDelegate::OnResponseStarted(
    300     net::URLRequest* request,
    301     content::ResourceContext* resource_context,
    302     content::ResourceResponse* response,
    303     IPC::Sender* sender) {
    304   const content::ResourceRequestInfo* request_info =
    305       content::ResourceRequestInfo::ForRequest(request);
    306   if (!request_info) {
    307     DLOG(FATAL) << "Started request without associated info: " <<
    308         request->url();
    309     return;
    310   }
    311 
    312   if (request_info->GetResourceType() == ResourceType::MAIN_FRAME) {
    313     // Check for x-auto-login header.
    314     auto_login_parser::HeaderData header_data;
    315     if (auto_login_parser::ParserHeaderInResponse(
    316             request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
    317       scoped_ptr<AwContentsIoThreadClient> io_client =
    318           AwContentsIoThreadClient::FromID(request_info->GetChildID(),
    319                                            request_info->GetRouteID());
    320       if (io_client) {
    321         io_client->NewLoginRequest(
    322             header_data.realm, header_data.account, header_data.args);
    323       }
    324     }
    325   }
    326 }
    327 
    328 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
    329     IoThreadClientThrottle* throttle) {
    330   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    331   PendingThrottleMap::iterator it = pending_throttles_.find(
    332       ChildRouteIDPair(throttle->get_child_id(), throttle->get_route_id()));
    333   if (it != pending_throttles_.end()) {
    334     pending_throttles_.erase(it);
    335   }
    336 }
    337 
    338 // static
    339 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
    340     int new_child_id,
    341     int new_route_id) {
    342   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    343       base::Bind(
    344           &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal,
    345           base::Unretained(
    346               g_webview_resource_dispatcher_host_delegate.Pointer()),
    347           new_child_id, new_route_id));
    348 }
    349 
    350 // static
    351 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
    352     int child_id,
    353     int route_id,
    354     IoThreadClientThrottle* pending_throttle) {
    355   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
    356       base::Bind(
    357           &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread,
    358           base::Unretained(
    359               g_webview_resource_dispatcher_host_delegate.Pointer()),
    360           child_id, route_id, pending_throttle));
    361 }
    362 
    363 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
    364     int child_id,
    365     int route_id,
    366     IoThreadClientThrottle* pending_throttle) {
    367   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    368   pending_throttles_.insert(
    369       std::pair<ChildRouteIDPair, IoThreadClientThrottle*>(
    370           ChildRouteIDPair(child_id, route_id), pending_throttle));
    371 }
    372 
    373 void AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal(
    374     int new_child_id,
    375     int new_route_id) {
    376   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    377   PendingThrottleMap::iterator it = pending_throttles_.find(
    378       ChildRouteIDPair(new_child_id, new_route_id));
    379 
    380   if (it != pending_throttles_.end()) {
    381     IoThreadClientThrottle* throttle = it->second;
    382     throttle->OnIoThreadClientReady(new_child_id, new_route_id);
    383     pending_throttles_.erase(it);
    384   }
    385 }
    386 
    387 }  // namespace android_webview
    388