Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / android_webview / browser / renderer_host / aw_resource_dispatcher_host_delegate.cc
blobda8a60455d103711968efbb0f22ad91c9bd3e2bc
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.
5 #include "android_webview/browser/renderer_host/aw_resource_dispatcher_host_delegate.h"
7 #include <string>
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 "net/url_request/url_request_status.h"
28 #include "url/url_constants.h"
30 using android_webview::AwContentsIoThreadClient;
31 using content::BrowserThread;
32 using content::ResourceType;
33 using navigation_interception::InterceptNavigationDelegate;
35 namespace {
37 base::LazyInstance<android_webview::AwResourceDispatcherHostDelegate>
38 g_webview_resource_dispatcher_host_delegate = LAZY_INSTANCE_INITIALIZER;
40 void SetCacheControlFlag(
41 net::URLRequest* request, int flag) {
42 const int all_cache_control_flags = net::LOAD_BYPASS_CACHE |
43 net::LOAD_VALIDATE_CACHE |
44 net::LOAD_PREFERRING_CACHE |
45 net::LOAD_ONLY_FROM_CACHE;
46 DCHECK_EQ((flag & all_cache_control_flags), flag);
47 int load_flags = request->load_flags();
48 load_flags &= ~all_cache_control_flags;
49 load_flags |= flag;
50 request->SetLoadFlags(load_flags);
53 } // namespace
55 namespace android_webview {
57 // Calls through the IoThreadClient to check the embedders settings to determine
58 // if the request should be cancelled. There may not always be an IoThreadClient
59 // available for the |render_process_id|, |render_frame_id| pair (in the case of
60 // newly created pop up windows, for example) and in that case the request and
61 // the client callbacks will be deferred the request until a client is ready.
62 class IoThreadClientThrottle : public content::ResourceThrottle {
63 public:
64 IoThreadClientThrottle(int render_process_id,
65 int render_frame_id,
66 net::URLRequest* request);
67 ~IoThreadClientThrottle() override;
69 // From content::ResourceThrottle
70 void WillStartRequest(bool* defer) override;
71 void WillRedirectRequest(const net::RedirectInfo& redirect_info,
72 bool* defer) override;
73 const char* GetNameForLogging() const override;
75 void OnIoThreadClientReady(int new_render_process_id,
76 int new_render_frame_id);
77 bool MaybeBlockRequest();
78 bool ShouldBlockRequest();
79 int render_process_id() const { return render_process_id_; }
80 int render_frame_id() const { return render_frame_id_; }
82 private:
83 int render_process_id_;
84 int render_frame_id_;
85 net::URLRequest* request_;
88 IoThreadClientThrottle::IoThreadClientThrottle(int render_process_id,
89 int render_frame_id,
90 net::URLRequest* request)
91 : render_process_id_(render_process_id),
92 render_frame_id_(render_frame_id),
93 request_(request) { }
95 IoThreadClientThrottle::~IoThreadClientThrottle() {
96 DCHECK_CURRENTLY_ON(BrowserThread::IO);
97 g_webview_resource_dispatcher_host_delegate.Get().
98 RemovePendingThrottleOnIoThread(this);
101 const char* IoThreadClientThrottle::GetNameForLogging() const {
102 return "IoThreadClientThrottle";
105 void IoThreadClientThrottle::WillStartRequest(bool* defer) {
106 DCHECK_CURRENTLY_ON(BrowserThread::IO);
107 if (render_frame_id_ < 1)
108 return;
109 DCHECK(render_process_id_);
110 *defer = false;
112 // Defer all requests of a pop up that is still not associated with Java
113 // client so that the client will get a chance to override requests.
114 scoped_ptr<AwContentsIoThreadClient> io_client =
115 AwContentsIoThreadClient::FromID(render_process_id_, render_frame_id_);
116 if (io_client && io_client->PendingAssociation()) {
117 *defer = true;
118 AwResourceDispatcherHostDelegate::AddPendingThrottle(
119 render_process_id_, render_frame_id_, this);
120 } else {
121 MaybeBlockRequest();
125 void IoThreadClientThrottle::WillRedirectRequest(
126 const net::RedirectInfo& redirect_info,
127 bool* defer) {
128 WillStartRequest(defer);
131 void IoThreadClientThrottle::OnIoThreadClientReady(int new_render_process_id,
132 int new_render_frame_id) {
133 DCHECK_CURRENTLY_ON(BrowserThread::IO);
135 if (!MaybeBlockRequest()) {
136 controller()->Resume();
140 bool IoThreadClientThrottle::MaybeBlockRequest() {
141 if (ShouldBlockRequest()) {
142 controller()->CancelWithError(net::ERR_ACCESS_DENIED);
143 return true;
145 return false;
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;
154 // Part of implementation of WebSettings.allowContentAccess.
155 if (request_->url().SchemeIs(android_webview::kContentScheme) &&
156 io_client->ShouldBlockContentUrls()) {
157 return true;
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;
172 if (io_client->ShouldBlockNetworkLoads()) {
173 if (request_->url().SchemeIs(url::kFtpScheme)) {
174 return true;
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;
193 return false;
196 // static
197 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() {
198 content::ResourceDispatcherHost::Get()->SetDelegate(
199 &g_webview_resource_dispatcher_host_delegate.Get());
202 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate()
203 : content::ResourceDispatcherHostDelegate() {
206 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() {
209 void AwResourceDispatcherHostDelegate::RequestBeginning(
210 net::URLRequest* request,
211 content::ResourceContext* resource_context,
212 content::AppCacheService* appcache_service,
213 ResourceType resource_type,
214 ScopedVector<content::ResourceThrottle>* throttles) {
216 AddExtraHeadersIfNeeded(request, resource_context);
218 const content::ResourceRequestInfo* request_info =
219 content::ResourceRequestInfo::ForRequest(request);
221 // We always push the throttles here. Checking the existence of io_client
222 // is racy when a popup window is created. That is because RequestBeginning
223 // is called whether or not requests are blocked via BlockRequestForRoute()
224 // however io_client may or may not be ready at the time depending on whether
225 // webcontents is created.
226 throttles->push_back(new IoThreadClientThrottle(
227 request_info->GetChildID(), request_info->GetRenderFrameID(), request));
229 if (resource_type == content::RESOURCE_TYPE_MAIN_FRAME ||
230 (resource_type == content::RESOURCE_TYPE_SUB_FRAME &&
231 !request->url().SchemeIs(url::kHttpScheme) &&
232 !request->url().SchemeIs(url::kHttpsScheme) &&
233 !request->url().SchemeIs(url::kAboutScheme))) {
234 throttles->push_back(InterceptNavigationDelegate::CreateThrottleFor(
235 request));
237 if (resource_type != content::RESOURCE_TYPE_MAIN_FRAME)
238 InterceptNavigationDelegate::UpdateUserGestureCarryoverInfo(request);
241 void AwResourceDispatcherHostDelegate::OnRequestRedirected(
242 const GURL& redirect_url,
243 net::URLRequest* request,
244 content::ResourceContext* resource_context,
245 content::ResourceResponse* response) {
246 AddExtraHeadersIfNeeded(request, resource_context);
249 void AwResourceDispatcherHostDelegate::RequestComplete(
250 net::URLRequest* request) {
251 if (request && !request->status().is_success()) {
252 const content::ResourceRequestInfo* request_info =
253 content::ResourceRequestInfo::ForRequest(request);
254 scoped_ptr<AwContentsIoThreadClient> io_client =
255 AwContentsIoThreadClient::FromID(request_info->GetChildID(),
256 request_info->GetRenderFrameID());
257 if (io_client) {
258 io_client->OnReceivedError(request);
259 } else {
260 DLOG(WARNING) << "io_client is null, onReceivedError dropped for " <<
261 request->url();
267 void AwResourceDispatcherHostDelegate::DownloadStarting(
268 net::URLRequest* request,
269 content::ResourceContext* resource_context,
270 int child_id,
271 int route_id,
272 int request_id,
273 bool is_content_initiated,
274 bool must_download,
275 ScopedVector<content::ResourceThrottle>* throttles) {
276 GURL url(request->url());
277 std::string user_agent;
278 std::string content_disposition;
279 std::string mime_type;
280 int64 content_length = request->GetExpectedContentSize();
282 request->extra_request_headers().GetHeader(
283 net::HttpRequestHeaders::kUserAgent, &user_agent);
286 net::HttpResponseHeaders* response_headers = request->response_headers();
287 if (response_headers) {
288 response_headers->GetNormalizedHeader("content-disposition",
289 &content_disposition);
290 response_headers->GetMimeType(&mime_type);
293 request->Cancel();
295 const content::ResourceRequestInfo* request_info =
296 content::ResourceRequestInfo::ForRequest(request);
298 scoped_ptr<AwContentsIoThreadClient> io_client =
299 AwContentsIoThreadClient::FromID(
300 child_id, request_info->GetRenderFrameID());
302 // POST request cannot be repeated in general, so prevent client from
303 // retrying the same request, even if it is with a GET.
304 if ("GET" == request->method() && io_client) {
305 io_client->NewDownload(url,
306 user_agent,
307 content_disposition,
308 mime_type,
309 content_length);
313 content::ResourceDispatcherHostLoginDelegate*
314 AwResourceDispatcherHostDelegate::CreateLoginDelegate(
315 net::AuthChallengeInfo* auth_info,
316 net::URLRequest* request) {
317 return new AwLoginDelegate(auth_info, request);
320 bool AwResourceDispatcherHostDelegate::HandleExternalProtocol(
321 const GURL& url,
322 int child_id,
323 int route_id,
324 bool is_main_frame,
325 ui::PageTransition page_transition,
326 bool has_user_gesture) {
327 // The AwURLRequestJobFactory implementation should ensure this method never
328 // gets called.
329 NOTREACHED();
330 return false;
333 void AwResourceDispatcherHostDelegate::OnResponseStarted(
334 net::URLRequest* request,
335 content::ResourceContext* resource_context,
336 content::ResourceResponse* response,
337 IPC::Sender* sender) {
338 const content::ResourceRequestInfo* request_info =
339 content::ResourceRequestInfo::ForRequest(request);
340 if (!request_info) {
341 DLOG(FATAL) << "Started request without associated info: " <<
342 request->url();
343 return;
346 if (request_info->GetResourceType() == content::RESOURCE_TYPE_MAIN_FRAME) {
347 // Check for x-auto-login header.
348 auto_login_parser::HeaderData header_data;
349 if (auto_login_parser::ParserHeaderInResponse(
350 request, auto_login_parser::ALLOW_ANY_REALM, &header_data)) {
351 scoped_ptr<AwContentsIoThreadClient> io_client =
352 AwContentsIoThreadClient::FromID(request_info->GetChildID(),
353 request_info->GetRenderFrameID());
354 if (io_client) {
355 io_client->NewLoginRequest(
356 header_data.realm, header_data.account, header_data.args);
362 void AwResourceDispatcherHostDelegate::RemovePendingThrottleOnIoThread(
363 IoThreadClientThrottle* throttle) {
364 DCHECK_CURRENTLY_ON(BrowserThread::IO);
365 PendingThrottleMap::iterator it = pending_throttles_.find(
366 FrameRouteIDPair(throttle->render_process_id(),
367 throttle->render_frame_id()));
368 if (it != pending_throttles_.end()) {
369 pending_throttles_.erase(it);
373 // static
374 void AwResourceDispatcherHostDelegate::OnIoThreadClientReady(
375 int new_render_process_id,
376 int new_render_frame_id) {
377 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
378 base::Bind(
379 &AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal,
380 base::Unretained(
381 g_webview_resource_dispatcher_host_delegate.Pointer()),
382 new_render_process_id, new_render_frame_id));
385 // static
386 void AwResourceDispatcherHostDelegate::AddPendingThrottle(
387 int render_process_id,
388 int render_frame_id,
389 IoThreadClientThrottle* pending_throttle) {
390 BrowserThread::PostTask(BrowserThread::IO, FROM_HERE,
391 base::Bind(
392 &AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread,
393 base::Unretained(
394 g_webview_resource_dispatcher_host_delegate.Pointer()),
395 render_process_id, render_frame_id, pending_throttle));
398 void AwResourceDispatcherHostDelegate::AddPendingThrottleOnIoThread(
399 int render_process_id,
400 int render_frame_id_id,
401 IoThreadClientThrottle* pending_throttle) {
402 DCHECK_CURRENTLY_ON(BrowserThread::IO);
403 pending_throttles_.insert(
404 std::pair<FrameRouteIDPair, IoThreadClientThrottle*>(
405 FrameRouteIDPair(render_process_id, render_frame_id_id),
406 pending_throttle));
409 void AwResourceDispatcherHostDelegate::OnIoThreadClientReadyInternal(
410 int new_render_process_id,
411 int new_render_frame_id) {
412 DCHECK_CURRENTLY_ON(BrowserThread::IO);
413 PendingThrottleMap::iterator it = pending_throttles_.find(
414 FrameRouteIDPair(new_render_process_id, new_render_frame_id));
416 if (it != pending_throttles_.end()) {
417 IoThreadClientThrottle* throttle = it->second;
418 throttle->OnIoThreadClientReady(new_render_process_id, new_render_frame_id);
419 pending_throttles_.erase(it);
423 void AwResourceDispatcherHostDelegate::AddExtraHeadersIfNeeded(
424 net::URLRequest* request,
425 content::ResourceContext* resource_context) {
426 const content::ResourceRequestInfo* request_info =
427 content::ResourceRequestInfo::ForRequest(request);
428 if (!request_info)
429 return;
430 if (request_info->GetResourceType() != content::RESOURCE_TYPE_MAIN_FRAME)
431 return;
433 const ui::PageTransition transition = request_info->GetPageTransition();
434 const bool is_load_url =
435 transition & ui::PAGE_TRANSITION_FROM_API;
436 const bool is_go_back_forward =
437 transition & ui::PAGE_TRANSITION_FORWARD_BACK;
438 const bool is_reload = ui::PageTransitionCoreTypeIs(
439 transition, ui::PAGE_TRANSITION_RELOAD);
440 if (is_load_url || is_go_back_forward || is_reload) {
441 AwResourceContext* awrc = static_cast<AwResourceContext*>(resource_context);
442 std::string extra_headers = awrc->GetExtraHeaders(request->url());
443 if (!extra_headers.empty()) {
444 net::HttpRequestHeaders headers;
445 headers.AddHeadersFromString(extra_headers);
446 for (net::HttpRequestHeaders::Iterator it(headers); it.GetNext(); ) {
447 request->SetExtraRequestHeaderByName(it.name(), it.value(), false);
453 } // namespace android_webview