• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/browser/aw_resource_context.h"
12 #include "android_webview/common/url_constants.h"
13 #include "base/memory/scoped_ptr.h"
14 #include "base/memory/scoped_vector.h"
15 #include "components/auto_login_parser/auto_login_parser.h"
16 #include "components/navigation_interception/intercept_navigation_delegate.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "content/public/browser/resource_controller.h"
19 #include "content/public/browser/resource_dispatcher_host.h"
20 #include "content/public/browser/resource_dispatcher_host_login_delegate.h"
21 #include "content/public/browser/resource_request_info.h"
22 #include "content/public/browser/resource_throttle.h"
23 #include "net/base/load_flags.h"
24 #include "net/base/net_errors.h"
25 #include "net/http/http_response_headers.h"
26 #include "net/url_request/url_request.h"
27 #include "url/url_constants.h"
28 
29 using android_webview::AwContentsIoThreadClient;
30 using content::BrowserThread;
31 using content::ResourceType;
32 using navigation_interception::InterceptNavigationDelegate;
33 
34 namespace {
35 
36 base::LazyInstance<android_webview::AwResourceDispatcherHostDelegate>
37     g_webview_resource_dispatcher_host_delegate = LAZY_INSTANCE_INITIALIZER;
38 
SetCacheControlFlag(net::URLRequest * request,int flag)39 void SetCacheControlFlag(
40     net::URLRequest* request, int flag) {
41   const int all_cache_control_flags = net::LOAD_BYPASS_CACHE |
42       net::LOAD_VALIDATE_CACHE |
43       net::LOAD_PREFERRING_CACHE |
44       net::LOAD_ONLY_FROM_CACHE;
45   DCHECK_EQ((flag & all_cache_control_flags), flag);
46   int load_flags = request->load_flags();
47   load_flags &= ~all_cache_control_flags;
48   load_flags |= flag;
49   request->SetLoadFlags(load_flags);
50 }
51 
52 }  // namespace
53 
54 namespace android_webview {
55 
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 {
62  public:
63   IoThreadClientThrottle(int render_process_id,
64                          int render_frame_id,
65                          net::URLRequest* request);
66   virtual ~IoThreadClientThrottle();
67 
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;
72 
73   void OnIoThreadClientReady(int new_render_process_id,
74                              int new_render_frame_id);
75   bool MaybeBlockRequest();
76   bool ShouldBlockRequest();
render_process_id() const77   int render_process_id() const { return render_process_id_; }
render_frame_id() const78   int render_frame_id() const { return render_frame_id_; }
79 
80  private:
81   int render_process_id_;
82   int render_frame_id_;
83   net::URLRequest* request_;
84 };
85 
IoThreadClientThrottle(int render_process_id,int render_frame_id,net::URLRequest * request)86 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
87                                                int render_frame_id,
88                                                net::URLRequest* request)
89     : render_process_id_(render_process_id),
90       render_frame_id_(render_frame_id),
91       request_(request) { }
92 
~IoThreadClientThrottle()93 IoThreadClientThrottle::~IoThreadClientThrottle() {
94   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
95   g_webview_resource_dispatcher_host_delegate.Get().
96       RemovePendingThrottleOnIoThread(this);
97 }
98 
GetNameForLogging() const99 const char* IoThreadClientThrottle::GetNameForLogging() const {
100   return "IoThreadClientThrottle";
101 }
102 
WillStartRequest(bool * defer)103 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
104   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
105   // TODO(sgurun): This block can be removed when crbug.com/277937 is fixed.
106   if (render_frame_id_ < 1) {
107     // OPTIONS is used for preflighted requests which are generated internally.
108     DCHECK_EQ("OPTIONS", request_->method());
109     return;
110   }
111   DCHECK(render_process_id_);
112   *defer = false;
113 
114   // Defer all requests of a pop up that is still not associated with Java
115   // client so that the client will get a chance to override requests.
116   scoped_ptr<AwContentsIoThreadClient> io_client =
117       AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
118   if (io_client && io_client->PendingAssociation()) {
119     *defer = true;
120     AwResourceDispatcherHostDelegate::AddPendingThrottle(
121         render_process_id_, render_frame_id_, this);
122   } else {
123     MaybeBlockRequest();
124   }
125 }
126 
WillRedirectRequest(const GURL & new_url,bool * defer)127 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
128                                                  bool* defer) {
129   WillStartRequest(defer);
130 }
131 
OnIoThreadClientReady(int new_render_process_id,int new_render_frame_id)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 
MaybeBlockRequest()141 bool IoThreadClientThrottle::MaybeBlockRequest() {
142   if (ShouldBlockRequest()) {
143     controller()->CancelWithError(net::ERR_ACCESS_DENIED);
144     return true;
145   }
146   return false;
147 }
148 
ShouldBlockRequest()149 bool IoThreadClientThrottle::ShouldBlockRequest() {
150   scoped_ptr<AwContentsIoThreadClient> io_client =
151       AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
152   if (!io_client)
153     return false;
154 
155   // Part of implementation of WebSettings.allowContentAccess.
156   if (request_->url().SchemeIs(android_webview::kContentScheme) &&
157       io_client->ShouldBlockContentUrls()) {
158     return true;
159   }
160 
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(android_webview::kAndroidResourcePath) != 0 &&
168          url.path().find(android_webview::kAndroidAssetPath) != 0)) {
169       return true;
170     }
171   }
172 
173   if (io_client->ShouldBlockNetworkLoads()) {
174     if (request_->url().SchemeIs(url::kFtpScheme)) {
175       return true;
176     }
177     SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
178   } else {
179     AwContentsIoThreadClient::CacheMode cache_mode = io_client->GetCacheMode();
180     switch(cache_mode) {
181       case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
182         SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
183         break;
184       case AwContentsIoThreadClient::LOAD_NO_CACHE:
185         SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
186         break;
187       case AwContentsIoThreadClient::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 // static
ResourceDispatcherHostCreated()198 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
199   content::ResourceDispatcherHost::Get()->SetDelegate(
200       &g_webview_resource_dispatcher_host_delegate.Get());
201 }
202 
AwResourceDispatcherHostDelegate()203 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
204     : content::ResourceDispatcherHostDelegate() {
205 }
206 
~AwResourceDispatcherHostDelegate()207 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
208 }
209 
RequestBeginning(net::URLRequest * request,content::ResourceContext * resource_context,content::AppCacheService * appcache_service,ResourceType resource_type,ScopedVector<content::ResourceThrottle> * throttles)210 void AwResourceDispatcherHostDelegate::RequestBeginning(
211     net::URLRequest* request,
212     content::ResourceContext* resource_context,
213     content::AppCacheService* appcache_service,
214     ResourceType resource_type,
215     ScopedVector<content::ResourceThrottle>* throttles) {
216 
217   AddExtraHeadersIfNeeded(request, resource_context);
218 
219   const content::ResourceRequestInfo* request_info =
220       content::ResourceRequestInfo::ForRequest(request);
221 
222   // We always push the throttles here. Checking the existence of io_client
223   // is racy when a popup window is created. That is because RequestBeginning
224   // is called whether or not requests are blocked via BlockRequestForRoute()
225   // however io_client may or may not be ready at the time depending on whether
226   // webcontents is created.
227   throttles->push_back(new IoThreadClientThrottle(
228       request_info->GetChildID(), request_info->GetRenderFrameID(), request));
229 
230   // We allow intercepting only navigations within main frames. This
231   // is used to post onPageStarted. We handle shouldOverrideUrlLoading
232   // via a sync IPC.
233   if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME)
234     throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
235         request));
236 }
237 
OnRequestRedirected(const GURL & redirect_url,net::URLRequest * request,content::ResourceContext * resource_context,content::ResourceResponse * response)238 void AwResourceDispatcherHostDelegate::OnRequestRedirected(
239     const GURL& redirect_url,
240     net::URLRequest* request,
241     content::ResourceContext* resource_context,
242     content::ResourceResponse* response) {
243   AddExtraHeadersIfNeeded(request, resource_context);
244 }
245 
246 
DownloadStarting(net::URLRequest * request,content::ResourceContext * resource_context,int child_id,int route_id,int request_id,bool is_content_initiated,bool must_download,ScopedVector<content::ResourceThrottle> * throttles)247 void AwResourceDispatcherHostDelegate::DownloadStarting(
248     net::URLRequest* request,
249     content::ResourceContext* resource_context,
250     int child_id,
251     int route_id,
252     int request_id,
253     bool is_content_initiated,
254     bool must_download,
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();
261 
262   request->extra_request_headers().GetHeader(
263       net::HttpRequestHeaders::kUserAgent, &user_agent);
264 
265 
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);
271   }
272 
273   request->Cancel();
274 
275   const content::ResourceRequestInfo* request_info =
276       content::ResourceRequestInfo::ForRequest(request);
277 
278   scoped_ptr<AwContentsIoThreadClient> io_client =
279       AwContentsIoThreadClient::FromID(
280           child_id, request_info->GetRenderFrameID());
281 
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,
286                            user_agent,
287                            content_disposition,
288                            mime_type,
289                            content_length);
290   }
291 }
292 
293 content::ResourceDispatcherHostLoginDelegate*
CreateLoginDelegate(net::AuthChallengeInfo * auth_info,net::URLRequest * request)294     AwResourceDispatcherHostDelegate::CreateLoginDelegate(
295         net::AuthChallengeInfo* auth_info,
296         net::URLRequest* request) {
297   return new AwLoginDelegate(auth_info, request);
298 }
299 
HandleExternalProtocol(const GURL & url,int child_id,int route_id)300 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL& url,
301                                                               int child_id,
302                                                               int route_id) {
303   // The AwURLRequestJobFactory implementation should ensure this method never
304   // gets called.
305   NOTREACHED();
306   return false;
307 }
308 
OnResponseStarted(net::URLRequest * request,content::ResourceContext * resource_context,content::ResourceResponse * response,IPC::Sender * sender)309 void AwResourceDispatcherHostDelegate::OnResponseStarted(
310     net::URLRequest* request,
311     content::ResourceContext* resource_context,
312     content::ResourceResponse* response,
313     IPC::Sender* sender) {
314   const content::ResourceRequestInfo* request_info =
315       content::ResourceRequestInfo::ForRequest(request);
316   if (!request_info) {
317     DLOG(FATAL) << "Started request without associated info: " <<
318         request->url();
319     return;
320   }
321 
322   if (request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
323     // Check for x-auto-login header.
324     auto_login_parser::HeaderData header_data;
325     if (auto_login_parser::ParserHeaderInResponse(
326             request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
327       scoped_ptr<AwContentsIoThreadClient> io_client =
328           AwContentsIoThreadClient::FromID(request_info->GetChildID(),
329                                            request_info->GetRenderFrameID());
330       if (io_client) {
331         io_client->NewLoginRequest(
332             header_data.realm, header_data.account, header_data.args);
333       }
334     }
335   }
336 }
337 
RemovePendingThrottleOnIoThread(IoThreadClientThrottle * throttle)338 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
339     IoThreadClientThrottle* throttle) {
340   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
341   PendingThrottleMap::iterator it = pending_throttles_.find(
342       FrameRouteIDPair(throttle->render_process_id(),
343                        throttle->render_frame_id()));
344   if (it != pending_throttles_.end()) {
345     pending_throttles_.erase(it);
346   }
347 }
348 
349 // static
OnIoThreadClientReady(int new_render_process_id,int new_render_frame_id)350 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
351     int new_render_process_id,
352     int new_render_frame_id) {
353   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
354       base::Bind(
355           &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal,
356           base::Unretained(
357               g_webview_resource_dispatcher_host_delegate.Pointer()),
358           new_render_process_id, new_render_frame_id));
359 }
360 
361 // static
AddPendingThrottle(int render_process_id,int render_frame_id,IoThreadClientThrottle * pending_throttle)362 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
363     int render_process_id,
364     int render_frame_id,
365     IoThreadClientThrottle* pending_throttle) {
366   BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
367       base::Bind(
368           &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread,
369           base::Unretained(
370               g_webview_resource_dispatcher_host_delegate.Pointer()),
371           render_process_id, render_frame_id, pending_throttle));
372 }
373 
AddPendingThrottleOnIoThread(int render_process_id,int render_frame_id_id,IoThreadClientThrottle * pending_throttle)374 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
375     int render_process_id,
376     int render_frame_id_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_id),
382           pending_throttle));
383 }
384 
OnIoThreadClientReadyInternal(int new_render_process_id,int new_render_frame_id)385 void AwResourceDispatcherHostDelegate::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, new_render_frame_id);
395     pending_throttles_.erase(it);
396   }
397 }
398 
AddExtraHeadersIfNeeded(net::URLRequest * request,content::ResourceContext * resource_context)399 void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
400     net::URLRequest* request,
401     content::ResourceContext* resource_context) {
402   const content::ResourceRequestInfo* request_info =
403       content::ResourceRequestInfo::ForRequest(request);
404   if (!request_info)
405     return;
406   if (request_info->GetResourceType() != content::RESOURCE_TYPE_MAIN_FRAME)
407     return;
408 
409   const ui::PageTransition transition = request_info->GetPageTransition();
410   const bool is_load_url =
411       transition & ui::PAGE_TRANSITION_FROM_API;
412   const bool is_go_back_forward =
413       transition & ui::PAGE_TRANSITION_FORWARD_BACK;
414   const bool is_reload = ui::PageTransitionCoreTypeIs(
415       transition, ui::PAGE_TRANSITION_RELOAD);
416   if (is_load_url || is_go_back_forward || is_reload) {
417     AwResourceContext* awrc = static_cast<AwResourceContext*>(resource_context);
418     std::string extra_headers = awrc->GetExtraHeaders(request->url());
419     if (!extra_headers.empty()) {
420       net::HttpRequestHeaders headers;
421       headers.AddHeadersFromString(extra_headers);
422       for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); ) {
423         request->SetExtraRequestHeaderByName(it.name(), it.value(), false);
424       }
425     }
426   }
427 }
428 
429 }  // namespace android_webview
430