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 navigation_interception::InterceptNavigationDelegate;
32
33 namespace {
34
35 base::LazyInstance<android_webview::AwResourceDispatcherHostDelegate>
36 g_webview_resource_dispatcher_host_delegate = LAZY_INSTANCE_INITIALIZER;
37
SetCacheControlFlag(net::URLRequest * request,int flag)38 void SetCacheControlFlag(
39 net::URLRequest* request, int flag) {
40 const int all_cache_control_flags = net::LOAD_BYPASS_CACHE |
41 net::LOAD_VALIDATE_CACHE |
42 net::LOAD_PREFERRING_CACHE |
43 net::LOAD_ONLY_FROM_CACHE;
44 DCHECK((flag & all_cache_control_flags) == flag);
45 int load_flags = request->load_flags();
46 load_flags &= ~all_cache_control_flags;
47 load_flags |= flag;
48 request->SetLoadFlags(load_flags);
49 }
50
51 } // namespace
52
53 namespace android_webview {
54
55 // Calls through the IoThreadClient to check the embedders settings to determine
56 // if the request should be cancelled. There may not always be an IoThreadClient
57 // available for the |render_process_id|, |render_frame_id| pair (in the case of
58 // newly created pop up windows, for example) and in that case the request and
59 // the client callbacks will be deferred the request until a client is ready.
60 class IoThreadClientThrottle : public content::ResourceThrottle {
61 public:
62 IoThreadClientThrottle(int render_process_id,
63 int render_frame_id,
64 net::URLRequest* request);
65 virtual ~IoThreadClientThrottle();
66
67 // From content::ResourceThrottle
68 virtual void WillStartRequest(bool* defer) OVERRIDE;
69 virtual void WillRedirectRequest(const GURL& new_url, bool* defer) OVERRIDE;
70 virtual const char* GetNameForLogging() const OVERRIDE;
71
72 void OnIoThreadClientReady(int new_render_process_id,
73 int new_render_frame_id);
74 bool MaybeBlockRequest();
75 bool ShouldBlockRequest();
render_process_id() const76 int render_process_id() const { return render_process_id_; }
render_frame_id() const77 int render_frame_id() const { return render_frame_id_; }
78
79 private:
80 int render_process_id_;
81 int render_frame_id_;
82 net::URLRequest* request_;
83 };
84
IoThreadClientThrottle(int render_process_id,int render_frame_id,net::URLRequest * request)85 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
86 int render_frame_id,
87 net::URLRequest* request)
88 : render_process_id_(render_process_id),
89 render_frame_id_(render_frame_id),
90 request_(request) { }
91
~IoThreadClientThrottle()92 IoThreadClientThrottle::~IoThreadClientThrottle() {
93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
94 g_webview_resource_dispatcher_host_delegate.Get().
95 RemovePendingThrottleOnIoThread(this);
96 }
97
GetNameForLogging() const98 const char* IoThreadClientThrottle::GetNameForLogging() const {
99 return "IoThreadClientThrottle";
100 }
101
WillStartRequest(bool * defer)102 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
103 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
104 // TODO(sgurun): This block can be removed when crbug.com/277937 is fixed.
105 if (render_frame_id_ < 1) {
106 // OPTIONS is used for preflighted requests which are generated internally.
107 DCHECK_EQ("OPTIONS", request_->method());
108 return;
109 }
110 DCHECK(render_process_id_);
111 *defer = false;
112
113 // Defer all requests of a pop up that is still not associated with Java
114 // client so that the client will get a chance to override requests.
115 scoped_ptr<AwContentsIoThreadClient> io_client =
116 AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
117 if (io_client && io_client->PendingAssociation()) {
118 *defer = true;
119 AwResourceDispatcherHostDelegate::AddPendingThrottle(
120 render_process_id_, render_frame_id_, this);
121 } else {
122 MaybeBlockRequest();
123 }
124 }
125
WillRedirectRequest(const GURL & new_url,bool * defer)126 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url,
127 bool* defer) {
128 WillStartRequest(defer);
129 }
130
OnIoThreadClientReady(int new_render_process_id,int new_render_frame_id)131 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id,
132 int new_render_frame_id) {
133 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
134
135 if (!MaybeBlockRequest()) {
136 controller()->Resume();
137 }
138 }
139
MaybeBlockRequest()140 bool IoThreadClientThrottle::MaybeBlockRequest() {
141 if (ShouldBlockRequest()) {
142 controller()->CancelWithError(net::ERR_ACCESS_DENIED);
143 return true;
144 }
145 return false;
146 }
147
ShouldBlockRequest()148 bool IoThreadClientThrottle::ShouldBlockRequest() {
149 scoped_ptr<AwContentsIoThreadClient> io_client =
150 AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
151 if (!io_client)
152 return false;
153
154 // Part of implementation of WebSettings.allowContentAccess.
155 if (request_->url().SchemeIs(android_webview::kContentScheme) &&
156 io_client->ShouldBlockContentUrls()) {
157 return true;
158 }
159
160 // Part of implementation of WebSettings.allowFileAccess.
161 if (request_->url().SchemeIsFile() &&
162 io_client->ShouldBlockFileUrls()) {
163 const GURL& url = request_->url();
164 if (!url.has_path() ||
165 // Application's assets and resources are always available.
166 (url.path().find(android_webview::kAndroidResourcePath) != 0 &&
167 url.path().find(android_webview::kAndroidAssetPath) != 0)) {
168 return true;
169 }
170 }
171
172 if (io_client->ShouldBlockNetworkLoads()) {
173 if (request_->url().SchemeIs(url::kFtpScheme)) {
174 return true;
175 }
176 SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
177 } else {
178 AwContentsIoThreadClient::CacheMode cache_mode = io_client->GetCacheMode();
179 switch(cache_mode) {
180 case AwContentsIoThreadClient::LOAD_CACHE_ELSE_NETWORK:
181 SetCacheControlFlag(request_, net::LOAD_PREFERRING_CACHE);
182 break;
183 case AwContentsIoThreadClient::LOAD_NO_CACHE:
184 SetCacheControlFlag(request_, net::LOAD_BYPASS_CACHE);
185 break;
186 case AwContentsIoThreadClient::LOAD_CACHE_ONLY:
187 SetCacheControlFlag(request_, net::LOAD_ONLY_FROM_CACHE);
188 break;
189 default:
190 break;
191 }
192 }
193 return false;
194 }
195
196 // static
ResourceDispatcherHostCreated()197 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
198 content::ResourceDispatcherHost::Get()->SetDelegate(
199 &g_webview_resource_dispatcher_host_delegate.Get());
200 }
201
AwResourceDispatcherHostDelegate()202 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
203 : content::ResourceDispatcherHostDelegate() {
204 }
205
~AwResourceDispatcherHostDelegate()206 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
207 }
208
RequestBeginning(net::URLRequest * request,content::ResourceContext * resource_context,appcache::AppCacheService * appcache_service,ResourceType::Type resource_type,int child_id,int route_id,ScopedVector<content::ResourceThrottle> * throttles)209 void AwResourceDispatcherHostDelegate::RequestBeginning(
210 net::URLRequest* request,
211 content::ResourceContext* resource_context,
212 appcache::AppCacheService* appcache_service,
213 ResourceType::Type resource_type,
214 int child_id,
215 int route_id,
216 ScopedVector<content::ResourceThrottle>* throttles) {
217
218 AddExtraHeadersIfNeeded(request, resource_context);
219
220 const content::ResourceRequestInfo* request_info =
221 content::ResourceRequestInfo::ForRequest(request);
222
223 // We always push the throttles here. Checking the existence of io_client
224 // is racy when a popup window is created. That is because RequestBeginning
225 // is called whether or not requests are blocked via BlockRequestForRoute()
226 // however io_client may or may not be ready at the time depending on whether
227 // webcontents is created.
228 throttles->push_back(new IoThreadClientThrottle(
229 child_id, request_info->GetRenderFrameID(), request));
230
231 // We allow intercepting only navigations within main frames. This
232 // is used to post onPageStarted. We handle shouldOverrideUrlLoading
233 // via a sync IPC.
234 if (resource_type == ResourceType::MAIN_FRAME)
235 throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
236 request));
237 }
238
OnRequestRedirected(const GURL & redirect_url,net::URLRequest * request,content::ResourceContext * resource_context,content::ResourceResponse * response)239 void AwResourceDispatcherHostDelegate::OnRequestRedirected(
240 const GURL& redirect_url,
241 net::URLRequest* request,
242 content::ResourceContext* resource_context,
243 content::ResourceResponse* response) {
244 AddExtraHeadersIfNeeded(request, resource_context);
245 }
246
247
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)248 void AwResourceDispatcherHostDelegate::DownloadStarting(
249 net::URLRequest* request,
250 content::ResourceContext* resource_context,
251 int child_id,
252 int route_id,
253 int request_id,
254 bool is_content_initiated,
255 bool must_download,
256 ScopedVector<content::ResourceThrottle>* throttles) {
257 GURL url(request->url());
258 std::string user_agent;
259 std::string content_disposition;
260 std::string mime_type;
261 int64 content_length = request->GetExpectedContentSize();
262
263 request->extra_request_headers().GetHeader(
264 net::HttpRequestHeaders::kUserAgent, &user_agent);
265
266
267 net::HttpResponseHeaders* response_headers = request->response_headers();
268 if (response_headers) {
269 response_headers->GetNormalizedHeader("content-disposition",
270 &content_disposition);
271 response_headers->GetMimeType(&mime_type);
272 }
273
274 request->Cancel();
275
276 const content::ResourceRequestInfo* request_info =
277 content::ResourceRequestInfo::ForRequest(request);
278
279 scoped_ptr<AwContentsIoThreadClient> io_client =
280 AwContentsIoThreadClient::FromID(
281 child_id, request_info->GetRenderFrameID());
282
283 // POST request cannot be repeated in general, so prevent client from
284 // retrying the same request, even if it is with a GET.
285 if ("GET" == request->method() && io_client) {
286 io_client->NewDownload(url,
287 user_agent,
288 content_disposition,
289 mime_type,
290 content_length);
291 }
292 }
293
294 content::ResourceDispatcherHostLoginDelegate*
CreateLoginDelegate(net::AuthChallengeInfo * auth_info,net::URLRequest * request)295 AwResourceDispatcherHostDelegate::CreateLoginDelegate(
296 net::AuthChallengeInfo* auth_info,
297 net::URLRequest* request) {
298 return new AwLoginDelegate(auth_info, request);
299 }
300
HandleExternalProtocol(const GURL & url,int child_id,int route_id)301 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(const GURL& url,
302 int child_id,
303 int route_id) {
304 // The AwURLRequestJobFactory implementation should ensure this method never
305 // gets called.
306 NOTREACHED();
307 return false;
308 }
309
OnResponseStarted(net::URLRequest * request,content::ResourceContext * resource_context,content::ResourceResponse * response,IPC::Sender * sender)310 void AwResourceDispatcherHostDelegate::OnResponseStarted(
311 net::URLRequest* request,
312 content::ResourceContext* resource_context,
313 content::ResourceResponse* response,
314 IPC::Sender* sender) {
315 const content::ResourceRequestInfo* request_info =
316 content::ResourceRequestInfo::ForRequest(request);
317 if (!request_info) {
318 DLOG(FATAL) << "Started request without associated info: " <<
319 request->url();
320 return;
321 }
322
323 if (request_info->GetResourceType() == ResourceType::MAIN_FRAME) {
324 // Check for x-auto-login header.
325 auto_login_parser::HeaderData header_data;
326 if (auto_login_parser::ParserHeaderInResponse(
327 request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
328 scoped_ptr<AwContentsIoThreadClient> io_client =
329 AwContentsIoThreadClient::FromID(request_info->GetChildID(),
330 request_info->GetRenderFrameID());
331 if (io_client) {
332 io_client->NewLoginRequest(
333 header_data.realm, header_data.account, header_data.args);
334 }
335 }
336 }
337 }
338
RemovePendingThrottleOnIoThread(IoThreadClientThrottle * throttle)339 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
340 IoThreadClientThrottle* throttle) {
341 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
342 PendingThrottleMap::iterator it = pending_throttles_.find(
343 FrameRouteIDPair(throttle->render_process_id(),
344 throttle->render_frame_id()));
345 if (it != pending_throttles_.end()) {
346 pending_throttles_.erase(it);
347 }
348 }
349
350 // static
OnIoThreadClientReady(int new_render_process_id,int new_render_frame_id)351 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
352 int new_render_process_id,
353 int new_render_frame_id) {
354 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
355 base::Bind(
356 &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal,
357 base::Unretained(
358 g_webview_resource_dispatcher_host_delegate.Pointer()),
359 new_render_process_id, new_render_frame_id));
360 }
361
362 // static
AddPendingThrottle(int render_process_id,int render_frame_id,IoThreadClientThrottle * pending_throttle)363 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
364 int render_process_id,
365 int render_frame_id,
366 IoThreadClientThrottle* pending_throttle) {
367 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
368 base::Bind(
369 &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread,
370 base::Unretained(
371 g_webview_resource_dispatcher_host_delegate.Pointer()),
372 render_process_id, render_frame_id, pending_throttle));
373 }
374
AddPendingThrottleOnIoThread(int render_process_id,int render_frame_id_id,IoThreadClientThrottle * pending_throttle)375 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
376 int render_process_id,
377 int render_frame_id_id,
378 IoThreadClientThrottle* pending_throttle) {
379 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
380 pending_throttles_.insert(
381 std::pair<FrameRouteIDPair, IoThreadClientThrottle*>(
382 FrameRouteIDPair(render_process_id, render_frame_id_id),
383 pending_throttle));
384 }
385
OnIoThreadClientReadyInternal(int new_render_process_id,int new_render_frame_id)386 void AwResourceDispatcherHostDelegate::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));
392
393 if (it != pending_throttles_.end()) {
394 IoThreadClientThrottle* throttle = it->second;
395 throttle->OnIoThreadClientReady(new_render_process_id, new_render_frame_id);
396 pending_throttles_.erase(it);
397 }
398 }
399
AddExtraHeadersIfNeeded(net::URLRequest * request,content::ResourceContext * resource_context)400 void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
401 net::URLRequest* request,
402 content::ResourceContext* resource_context) {
403 const content::ResourceRequestInfo* request_info =
404 content::ResourceRequestInfo::ForRequest(request);
405 if (!request_info) return;
406 if (request_info->GetResourceType() != ResourceType::MAIN_FRAME) return;
407
408 const content::PageTransition transition = request_info->GetPageTransition();
409 const bool is_load_url =
410 transition & content::PAGE_TRANSITION_FROM_API;
411 const bool is_go_back_forward =
412 transition & content::PAGE_TRANSITION_FORWARD_BACK;
413 const bool is_reload = content::PageTransitionCoreTypeIs(
414 transition, content::PAGE_TRANSITION_RELOAD);
415 if (is_load_url || is_go_back_forward || is_reload) {
416 AwResourceContext* awrc = static_cast<AwResourceContext*>(resource_context);
417 std::string extra_headers = awrc->GetExtraHeaders(request->url());
418 if (!extra_headers.empty()) {
419 net::HttpRequestHeaders headers;
420 headers.AddHeadersFromString(extra_headers);
421 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); ) {
422 request->SetExtraRequestHeaderByName(it.name(), it.value(), false);
423 }
424 }
425 }
426 }
427
428 } // namespace android_webview
429