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 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(); 76 int render_process_id() const { return render_process_id_; } 77 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 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 92 IoThreadClientThrottle::~IoThreadClientThrottle() { 93 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 94 g_webview_resource_dispatcher_host_delegate.Get(). 95 RemovePendingThrottleOnIoThread(this); 96 } 97 98 const char* IoThreadClientThrottle::GetNameForLogging() const { 99 return "IoThreadClientThrottle"; 100 } 101 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 126 void IoThreadClientThrottle::WillRedirectRequest(const GURL& new_url, 127 bool* defer) { 128 WillStartRequest(defer); 129 } 130 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 140 bool IoThreadClientThrottle::MaybeBlockRequest() { 141 if (ShouldBlockRequest()) { 142 controller()->CancelWithError(net::ERR_ACCESS_DENIED); 143 return true; 144 } 145 return false; 146 } 147 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 197 void AwResourceDispatcherHostDelegate::ResourceDispatcherHostCreated() { 198 content::ResourceDispatcherHost::Get()->SetDelegate( 199 &g_webview_resource_dispatcher_host_delegate.Get()); 200 } 201 202 AwResourceDispatcherHostDelegate::AwResourceDispatcherHostDelegate() 203 : content::ResourceDispatcherHostDelegate() { 204 } 205 206 AwResourceDispatcherHostDelegate::~AwResourceDispatcherHostDelegate() { 207 } 208 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 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 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* 295 AwResourceDispatcherHostDelegate::CreateLoginDelegate( 296 net::AuthChallengeInfo* auth_info, 297 net::URLRequest* request) { 298 return new AwLoginDelegate(auth_info, request); 299 } 300 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 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 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 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 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 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 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 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