chrome_resource_dispatcher_host_delegate.cc revision c5cede9ae108bb15f6b7a8aea21c7e1fefa2834c
15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved. 25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be 35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file. 45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h" 65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string> 85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) 95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/base64.h" 10868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/logging.h" 115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/browser_process.h" 125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/chrome_notification_types.h" 135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/component_updater/component_updater_service.h" 145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/component_updater/pnacl/pnacl_component_installer.h" 155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/content_settings/host_content_settings_map.h" 165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/download/download_request_limiter.h" 175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/download/download_resource_throttle.h" 185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/api/streams_private/streams_private_api.h" 195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/extension_renderer_state.h" 205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/extensions/user_script_listener.h" 211320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/browser/google/google_util.h" 221320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/browser/metrics/variations/variations_http_header_provider.h" 235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prefetch/prefetch.h" 245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prerender/prerender_manager.h" 255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prerender/prerender_manager_factory.h" 265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prerender/prerender_pending_swap_throttle.h" 275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prerender/prerender_resource_throttle.h" 285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prerender/prerender_tracker.h" 295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/prerender/prerender_util.h" 305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile.h" 315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/profiles/profile_io_data.h" 325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/renderer_host/safe_browsing_resource_throttle_factory.h" 335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/safe_browsing/safe_browsing_service.h" 345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/signin/signin_header_helper.h" 355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/tab_contents/tab_util.h" 365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/auto_login_prompter.h" 375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/login/login_prompt.h" 385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/browser/ui/sync/one_click_signin_helper.h" 395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/extension_constants.h" 405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/extensions/mime_types_handler.h" 415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/render_messages.h" 425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "chrome/common/url_constants.h" 435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/browser_thread.h" 445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/notification_service.h" 455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_process_host.h" 465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/render_view_host.h" 475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/resource_context.h" 485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/resource_dispatcher_host.h" 495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "content/public/browser/resource_request_info.h" 50#include "content/public/browser/stream_handle.h" 51#include "content/public/browser/web_contents.h" 52#include "content/public/common/resource_response.h" 53#include "extensions/browser/info_map.h" 54#include "extensions/common/constants.h" 55#include "extensions/common/user_script.h" 56#include "net/base/load_flags.h" 57#include "net/base/load_timing_info.h" 58#include "net/base/request_priority.h" 59#include "net/http/http_response_headers.h" 60#include "net/url_request/url_request.h" 61 62#if defined(ENABLE_CONFIGURATION_POLICY) 63#include "components/policy/core/common/cloud/policy_header_io_helper.h" 64#endif 65 66#if defined(ENABLE_MANAGED_USERS) 67#include "chrome/browser/managed_mode/managed_mode_resource_throttle.h" 68#endif 69 70#if defined(USE_SYSTEM_PROTOBUF) 71#include <google/protobuf/repeated_field.h> 72#else 73#include "third_party/protobuf/src/google/protobuf/repeated_field.h" 74#endif 75 76#if defined(OS_ANDROID) 77#include "chrome/browser/android/intercept_download_resource_throttle.h" 78#include "components/navigation_interception/intercept_navigation_delegate.h" 79#else 80#include "chrome/browser/apps/app_url_redirector.h" 81#include "chrome/browser/apps/ephemeral_app_throttle.h" 82#endif 83 84#if defined(OS_CHROMEOS) 85#include "chrome/browser/chromeos/login/merge_session_throttle.h" 86// TODO(oshima): Enable this for other platforms. 87#include "chrome/browser/renderer_host/offline_resource_throttle.h" 88#endif 89 90using content::BrowserThread; 91using content::RenderViewHost; 92using content::ResourceDispatcherHostLoginDelegate; 93using content::ResourceRequestInfo; 94using extensions::Extension; 95using extensions::StreamsPrivateAPI; 96 97#if defined(OS_ANDROID) 98using navigation_interception::InterceptNavigationDelegate; 99#endif 100 101namespace { 102 103ExternalProtocolHandler::Delegate* g_external_protocol_handler_delegate = NULL; 104 105void NotifyDownloadInitiatedOnUI(int render_process_id, int render_view_id) { 106 RenderViewHost* rvh = RenderViewHost::FromID(render_process_id, 107 render_view_id); 108 if (!rvh) 109 return; 110 111 content::NotificationService::current()->Notify( 112 chrome::NOTIFICATION_DOWNLOAD_INITIATED, 113 content::Source<RenderViewHost>(rvh), 114 content::NotificationService::NoDetails()); 115} 116 117prerender::PrerenderManager* GetPrerenderManager(int render_process_id, 118 int render_view_id) { 119 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 120 121 content::WebContents* web_contents = 122 tab_util::GetWebContentsByID(render_process_id, render_view_id); 123 if (!web_contents) 124 return NULL; 125 126 content::BrowserContext* browser_context = web_contents->GetBrowserContext(); 127 if (!browser_context) 128 return NULL; 129 130 Profile* profile = Profile::FromBrowserContext(browser_context); 131 if (!profile) 132 return NULL; 133 134 return prerender::PrerenderManagerFactory::GetForProfile(profile); 135} 136 137void UpdatePrerenderNetworkBytesCallback(int render_process_id, 138 int render_view_id, 139 int64 bytes) { 140 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 141 142 content::WebContents* web_contents = 143 tab_util::GetWebContentsByID(render_process_id, render_view_id); 144 // PrerenderContents::FromWebContents handles the NULL case. 145 prerender::PrerenderContents* prerender_contents = 146 prerender::PrerenderContents::FromWebContents(web_contents); 147 148 if (prerender_contents) 149 prerender_contents->AddNetworkBytes(bytes); 150 151 prerender::PrerenderManager* prerender_manager = 152 GetPrerenderManager(render_process_id, render_view_id); 153 if (prerender_manager) 154 prerender_manager->AddProfileNetworkBytesIfEnabled(bytes); 155} 156 157#if !defined(OS_ANDROID) 158// Goes through the extension's file browser handlers and checks if there is one 159// that can handle the |mime_type|. 160// |extension| must not be NULL. 161bool ExtensionCanHandleMimeType(const Extension* extension, 162 const std::string& mime_type) { 163 MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension); 164 if (!handler) 165 return false; 166 167 return handler->CanHandleMIMEType(mime_type); 168} 169 170void SendExecuteMimeTypeHandlerEvent(scoped_ptr<content::StreamHandle> stream, 171 int64 expected_content_size, 172 int render_process_id, 173 int render_view_id, 174 const std::string& extension_id) { 175 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 176 177 content::WebContents* web_contents = 178 tab_util::GetWebContentsByID(render_process_id, render_view_id); 179 if (!web_contents) 180 return; 181 182 // If the request was for a prerender, abort the prerender and do not 183 // continue. 184 prerender::PrerenderContents* prerender_contents = 185 prerender::PrerenderContents::FromWebContents(web_contents); 186 if (prerender_contents) { 187 prerender_contents->Destroy(prerender::FINAL_STATUS_DOWNLOAD); 188 return; 189 } 190 191 Profile* profile = 192 Profile::FromBrowserContext(web_contents->GetBrowserContext()); 193 194 StreamsPrivateAPI* streams_private = StreamsPrivateAPI::Get(profile); 195 if (!streams_private) 196 return; 197 streams_private->ExecuteMimeTypeHandler( 198 extension_id, web_contents, stream.Pass(), expected_content_size); 199} 200 201void LaunchURL(const GURL& url, int render_process_id, int render_view_id, 202 bool user_gesture) { 203 // If there is no longer a WebContents, the request may have raced with tab 204 // closing. Don't fire the external request. (It may have been a prerender.) 205 content::WebContents* web_contents = 206 tab_util::GetWebContentsByID(render_process_id, render_view_id); 207 if (!web_contents) 208 return; 209 210 // Do not launch external requests attached to unswapped prerenders. 211 prerender::PrerenderContents* prerender_contents = 212 prerender::PrerenderContents::FromWebContents(web_contents); 213 if (prerender_contents) { 214 prerender_contents->Destroy(prerender::FINAL_STATUS_UNSUPPORTED_SCHEME); 215 prerender::ReportPrerenderExternalURL(); 216 return; 217 } 218 219 ExternalProtocolHandler::LaunchUrlWithDelegate( 220 url, render_process_id, render_view_id, 221 g_external_protocol_handler_delegate, 222 user_gesture); 223} 224#endif // !defined(OS_ANDROID) 225 226void AppendComponentUpdaterThrottles( 227 net::URLRequest* request, 228 content::ResourceContext* resource_context, 229 ResourceType::Type resource_type, 230 ScopedVector<content::ResourceThrottle>* throttles) { 231 const char* crx_id = NULL; 232 component_updater::ComponentUpdateService* cus = 233 g_browser_process->component_updater(); 234 if (!cus) 235 return; 236 // Check for PNaCl pexe request. 237 if (resource_type == ResourceType::OBJECT) { 238 const net::HttpRequestHeaders& headers = request->extra_request_headers(); 239 std::string accept_headers; 240 if (headers.GetHeader("Accept", &accept_headers)) { 241 if (accept_headers.find("application/x-pnacl") != std::string::npos && 242 pnacl::NeedsOnDemandUpdate()) 243 crx_id = "hnimpnehoodheedghdeeijklkeaacbdc"; 244 } 245 } 246 247 if (crx_id) { 248 // We got a component we need to install, so throttle the resource 249 // until the component is installed. 250 throttles->push_back(cus->GetOnDemandResourceThrottle(request, crx_id)); 251 } 252} 253 254} // end namespace 255 256ChromeResourceDispatcherHostDelegate::ChromeResourceDispatcherHostDelegate( 257 prerender::PrerenderTracker* prerender_tracker) 258 : download_request_limiter_(g_browser_process->download_request_limiter()), 259 safe_browsing_(g_browser_process->safe_browsing_service()), 260 user_script_listener_(new extensions::UserScriptListener()), 261 prerender_tracker_(prerender_tracker) { 262} 263 264ChromeResourceDispatcherHostDelegate::~ChromeResourceDispatcherHostDelegate() { 265} 266 267bool ChromeResourceDispatcherHostDelegate::ShouldBeginRequest( 268 int child_id, 269 int route_id, 270 const std::string& method, 271 const GURL& url, 272 ResourceType::Type resource_type, 273 content::ResourceContext* resource_context) { 274 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 275 276 // Handle a PREFETCH resource type. If prefetch is disabled, squelch the 277 // request. Otherwise, do a normal request to warm the cache. 278 if (resource_type == ResourceType::PREFETCH) { 279 // All PREFETCH requests should be GETs, but be defensive about it. 280 if (method != "GET") 281 return false; 282 283 // If prefetch is disabled, kill the request. 284 if (!prefetch::IsPrefetchEnabled(resource_context)) 285 return false; 286 } 287 288 return true; 289} 290 291void ChromeResourceDispatcherHostDelegate::RequestBeginning( 292 net::URLRequest* request, 293 content::ResourceContext* resource_context, 294 appcache::AppCacheService* appcache_service, 295 ResourceType::Type resource_type, 296 int child_id, 297 int route_id, 298 ScopedVector<content::ResourceThrottle>* throttles) { 299 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); 300 bool is_prerendering = 301 info->GetVisibilityState() == blink::WebPageVisibilityStatePrerender; 302 if (is_prerendering) { 303 // Requests with the IGNORE_LIMITS flag set (i.e., sync XHRs) 304 // should remain at MAXIMUM_PRIORITY. 305 if (request->load_flags() & net::LOAD_IGNORE_LIMITS) { 306 DCHECK_EQ(request->priority(), net::MAXIMUM_PRIORITY); 307 } else { 308 request->SetPriority(net::IDLE); 309 } 310 } 311 312 ProfileIOData* io_data = ProfileIOData::FromResourceContext( 313 resource_context); 314 315 if (!is_prerendering && resource_type == ResourceType::MAIN_FRAME) { 316#if defined(OS_ANDROID) 317 throttles->push_back( 318 InterceptNavigationDelegate::CreateThrottleFor(request)); 319#else 320 // Redirect some navigations to apps that have registered matching URL 321 // handlers ('url_handlers' in the manifest). 322 content::ResourceThrottle* url_to_app_throttle = 323 AppUrlRedirector::MaybeCreateThrottleFor(request, io_data); 324 if (url_to_app_throttle) 325 throttles->push_back(url_to_app_throttle); 326 327 // Experimental: Launch ephemeral apps from search results. 328 content::ResourceThrottle* ephemeral_app_throttle = 329 EphemeralAppThrottle::MaybeCreateThrottleForLaunch( 330 request, io_data); 331 if (ephemeral_app_throttle) 332 throttles->push_back(ephemeral_app_throttle); 333#endif 334 } 335 336#if defined(OS_CHROMEOS) 337 // Check if we need to add offline throttle. This should be done only 338 // for main frames. 339 if (resource_type == ResourceType::MAIN_FRAME) { 340 // We check offline first, then check safe browsing so that we still can 341 // block unsafe site after we remove offline page. 342 throttles->push_back(new OfflineResourceThrottle(request, 343 appcache_service)); 344 } 345 346 // Check if we need to add merge session throttle. This throttle will postpone 347 // loading of main frames and XHR request. 348 if (resource_type == ResourceType::MAIN_FRAME || 349 resource_type == ResourceType::XHR) { 350 // Add interstitial page while merge session process (cookie 351 // reconstruction from OAuth2 refresh token in ChromeOS login) is still in 352 // progress while we are attempting to load a google property. 353 if (!MergeSessionThrottle::AreAllSessionMergedAlready() && 354 request->url().SchemeIsHTTPOrHTTPS()) { 355 throttles->push_back(new MergeSessionThrottle(request, resource_type)); 356 } 357 } 358#endif 359 360 // Don't attempt to append headers to requests that have already started. 361 // TODO(stevet): Remove this once the request ordering issues are resolved 362 // in crbug.com/128048. 363 if (!request->is_pending()) { 364 net::HttpRequestHeaders headers; 365 headers.CopyFrom(request->extra_request_headers()); 366 bool is_off_the_record = io_data->IsOffTheRecord(); 367 chrome_variations::VariationsHttpHeaderProvider::GetInstance()-> 368 AppendHeaders(request->url(), 369 is_off_the_record, 370 !is_off_the_record && 371 io_data->GetMetricsEnabledStateOnIOThread(), 372 &headers); 373 request->SetExtraRequestHeaders(headers); 374 } 375 376#if defined(ENABLE_ONE_CLICK_SIGNIN) 377 AppendChromeSyncGaiaHeader(request, resource_context); 378#endif 379 380#if defined(ENABLE_CONFIGURATION_POLICY) 381 if (io_data->policy_header_helper()) 382 io_data->policy_header_helper()->AddPolicyHeaders(request); 383#endif 384 385 signin::AppendMirrorRequestHeaderIfPossible( 386 request, GURL() /* redirect_url */, 387 io_data, info->GetChildID(), info->GetRouteID()); 388 389 AppendStandardResourceThrottles(request, 390 resource_context, 391 resource_type, 392 throttles); 393 if (!is_prerendering) { 394 AppendComponentUpdaterThrottles(request, 395 resource_context, 396 resource_type, 397 throttles); 398 } 399} 400 401void ChromeResourceDispatcherHostDelegate::DownloadStarting( 402 net::URLRequest* request, 403 content::ResourceContext* resource_context, 404 int child_id, 405 int route_id, 406 int request_id, 407 bool is_content_initiated, 408 bool must_download, 409 ScopedVector<content::ResourceThrottle>* throttles) { 410 BrowserThread::PostTask( 411 BrowserThread::UI, FROM_HERE, 412 base::Bind(&NotifyDownloadInitiatedOnUI, child_id, route_id)); 413 414 // If it's from the web, we don't trust it, so we push the throttle on. 415 if (is_content_initiated) { 416 throttles->push_back( 417 new DownloadResourceThrottle(download_request_limiter_.get(), 418 child_id, 419 route_id, 420 request_id, 421 request->method())); 422#if defined(OS_ANDROID) 423 throttles->push_back( 424 new chrome::InterceptDownloadResourceThrottle( 425 request, child_id, route_id, request_id)); 426#endif 427 } 428 429 // If this isn't a new request, we've seen this before and added the standard 430 // resource throttles already so no need to add it again. 431 if (!request->is_pending()) { 432 AppendStandardResourceThrottles(request, 433 resource_context, 434 ResourceType::MAIN_FRAME, 435 throttles); 436 } 437} 438 439ResourceDispatcherHostLoginDelegate* 440 ChromeResourceDispatcherHostDelegate::CreateLoginDelegate( 441 net::AuthChallengeInfo* auth_info, net::URLRequest* request) { 442 return CreateLoginPrompt(auth_info, request); 443} 444 445bool ChromeResourceDispatcherHostDelegate::HandleExternalProtocol( 446 const GURL& url, 447 int child_id, 448 int route_id, 449 bool initiated_by_user_gesture) { 450#if defined(OS_ANDROID) 451 // Android use a resource throttle to handle external as well as internal 452 // protocols. 453 return false; 454#else 455 456 ExtensionRendererState::WebViewInfo info; 457 if (ExtensionRendererState::GetInstance()->GetWebViewInfo(child_id, 458 route_id, 459 &info)) { 460 return false; 461 } 462 463 BrowserThread::PostTask( 464 BrowserThread::UI, FROM_HERE, 465 base::Bind(&LaunchURL, url, child_id, route_id, 466 initiated_by_user_gesture)); 467 return true; 468#endif 469} 470 471void ChromeResourceDispatcherHostDelegate::AppendStandardResourceThrottles( 472 net::URLRequest* request, 473 content::ResourceContext* resource_context, 474 ResourceType::Type resource_type, 475 ScopedVector<content::ResourceThrottle>* throttles) { 476 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context); 477#if defined(FULL_SAFE_BROWSING) || defined(MOBILE_SAFE_BROWSING) 478 // Insert safe browsing at the front of the list, so it gets to decide on 479 // policies first. 480 if (io_data->safe_browsing_enabled()->GetValue()) { 481 bool is_subresource_request = resource_type != ResourceType::MAIN_FRAME; 482 content::ResourceThrottle* throttle = 483 SafeBrowsingResourceThrottleFactory::Create(request, 484 is_subresource_request, 485 safe_browsing_.get()); 486 if (throttle) 487 throttles->push_back(throttle); 488 } 489#endif 490 491#if defined(ENABLE_MANAGED_USERS) 492 bool is_subresource_request = resource_type != ResourceType::MAIN_FRAME; 493 throttles->push_back(new ManagedModeResourceThrottle( 494 request, !is_subresource_request, 495 io_data->managed_mode_url_filter())); 496#endif 497 498 content::ResourceThrottle* throttle = 499 user_script_listener_->CreateResourceThrottle(request->url(), 500 resource_type); 501 if (throttle) 502 throttles->push_back(throttle); 503 504 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); 505 if (info->GetVisibilityState() == blink::WebPageVisibilityStatePrerender) { 506 throttles->push_back(new prerender::PrerenderResourceThrottle(request)); 507 } 508 if (prerender_tracker_->IsPendingSwapRequestOnIOThread( 509 info->GetChildID(), info->GetRenderFrameID(), request->url())) { 510 throttles->push_back(new prerender::PrerenderPendingSwapThrottle( 511 request, prerender_tracker_)); 512 } 513} 514 515#if defined(ENABLE_ONE_CLICK_SIGNIN) 516void ChromeResourceDispatcherHostDelegate::AppendChromeSyncGaiaHeader( 517 net::URLRequest* request, 518 content::ResourceContext* resource_context) { 519 static const char kAllowChromeSignIn[] = "Allow-Chrome-SignIn"; 520 521 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context); 522 OneClickSigninHelper::Offer offer = 523 OneClickSigninHelper::CanOfferOnIOThread(request, io_data); 524 switch (offer) { 525 case OneClickSigninHelper::CAN_OFFER: 526 request->SetExtraRequestHeaderByName(kAllowChromeSignIn, "1", false); 527 break; 528 case OneClickSigninHelper::DONT_OFFER: 529 request->RemoveRequestHeaderByName(kAllowChromeSignIn); 530 break; 531 case OneClickSigninHelper::IGNORE_REQUEST: 532 break; 533 } 534} 535#endif 536 537bool ChromeResourceDispatcherHostDelegate::ShouldForceDownloadResource( 538 const GURL& url, const std::string& mime_type) { 539 // Special-case user scripts to get downloaded instead of viewed. 540 return extensions::UserScript::IsURLUserScript(url, mime_type); 541} 542 543bool ChromeResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream( 544 content::ResourceContext* resource_context, 545 const GURL& url, 546 const std::string& mime_type, 547 GURL* origin, 548 std::string* target_id) { 549#if !defined(OS_ANDROID) 550 ProfileIOData* io_data = 551 ProfileIOData::FromResourceContext(resource_context); 552 bool profile_is_off_the_record = io_data->IsOffTheRecord(); 553 const scoped_refptr<const extensions::InfoMap> extension_info_map( 554 io_data->GetExtensionInfoMap()); 555 std::vector<std::string> whitelist = MimeTypesHandler::GetMIMETypeWhitelist(); 556 // Go through the white-listed extensions and try to use them to intercept 557 // the URL request. 558 for (size_t i = 0; i < whitelist.size(); ++i) { 559 const char* extension_id = whitelist[i].c_str(); 560 const Extension* extension = 561 extension_info_map->extensions().GetByID(extension_id); 562 // The white-listed extension may not be installed, so we have to NULL check 563 // |extension|. 564 if (!extension || 565 (profile_is_off_the_record && 566 !extension_info_map->IsIncognitoEnabled(extension_id))) { 567 continue; 568 } 569 570 if (ExtensionCanHandleMimeType(extension, mime_type)) { 571 *origin = Extension::GetBaseURLFromExtensionId(extension_id); 572 *target_id = extension_id; 573 return true; 574 } 575 } 576#endif 577 return false; 578} 579 580void ChromeResourceDispatcherHostDelegate::OnStreamCreated( 581 content::ResourceContext* resource_context, 582 int render_process_id, 583 int render_view_id, 584 const std::string& target_id, 585 scoped_ptr<content::StreamHandle> stream, 586 int64 expected_content_size) { 587#if !defined(OS_ANDROID) 588 content::BrowserThread::PostTask( 589 content::BrowserThread::UI, FROM_HERE, 590 base::Bind(&SendExecuteMimeTypeHandlerEvent, base::Passed(&stream), 591 expected_content_size, render_process_id, render_view_id, 592 target_id)); 593#endif 594} 595 596void ChromeResourceDispatcherHostDelegate::OnResponseStarted( 597 net::URLRequest* request, 598 content::ResourceContext* resource_context, 599 content::ResourceResponse* response, 600 IPC::Sender* sender) { 601 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); 602 603 // See if the response contains the X-Auto-Login header. If so, this was 604 // a request for a login page, and the server is allowing the browser to 605 // suggest auto-login, if available. 606 AutoLoginPrompter::ShowInfoBarIfPossible(request, info->GetChildID(), 607 info->GetRouteID()); 608 609 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context); 610 611#if defined(ENABLE_ONE_CLICK_SIGNIN) 612 // See if the response contains the Google-Accounts-SignIn header. If so, 613 // then the user has just finished signing in, and the server is allowing the 614 // browser to suggest connecting the user's profile to the account. 615 OneClickSigninHelper::ShowInfoBarIfPossible(request, io_data, 616 info->GetChildID(), 617 info->GetRouteID()); 618#endif 619 620 // See if the response contains the X-Chrome-Manage-Accounts header. If so 621 // show the profile avatar bubble so that user can complete signin/out action 622 // the native UI. 623 signin::ProcessMirrorResponseHeaderIfExists(request, io_data, 624 info->GetChildID(), 625 info->GetRouteID()); 626 627 // Build in additional protection for the chrome web store origin. 628 GURL webstore_url(extension_urls::GetWebstoreLaunchURL()); 629 if (request->url().DomainIs(webstore_url.host().c_str())) { 630 net::HttpResponseHeaders* response_headers = request->response_headers(); 631 if (!response_headers->HasHeaderValue("x-frame-options", "deny") && 632 !response_headers->HasHeaderValue("x-frame-options", "sameorigin")) { 633 response_headers->RemoveHeader("x-frame-options"); 634 response_headers->AddHeader("x-frame-options: sameorigin"); 635 } 636 } 637 638 // Ignores x-frame-options for the chrome signin UI. 639 const std::string request_spec( 640 request->first_party_for_cookies().GetOrigin().spec()); 641#if defined(OS_CHROMEOS) 642 if (request_spec == chrome::kChromeUIOobeURL || 643 request_spec == chrome::kChromeUIChromeSigninURL) { 644#else 645 if (request_spec == chrome::kChromeUIChromeSigninURL) { 646#endif 647 net::HttpResponseHeaders* response_headers = request->response_headers(); 648 if (response_headers->HasHeader("x-frame-options")) 649 response_headers->RemoveHeader("x-frame-options"); 650 } 651 652 prerender::URLRequestResponseStarted(request); 653} 654 655void ChromeResourceDispatcherHostDelegate::OnRequestRedirected( 656 const GURL& redirect_url, 657 net::URLRequest* request, 658 content::ResourceContext* resource_context, 659 content::ResourceResponse* response) { 660 ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context); 661 const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request); 662 663#if defined(ENABLE_ONE_CLICK_SIGNIN) 664 // See if the response contains the Google-Accounts-SignIn header. If so, 665 // then the user has just finished signing in, and the server is allowing the 666 // browser to suggest connecting the user's profile to the account. 667 OneClickSigninHelper::ShowInfoBarIfPossible(request, io_data, 668 info->GetChildID(), 669 info->GetRouteID()); 670 AppendChromeSyncGaiaHeader(request, resource_context); 671#endif 672 673 // In the Mirror world, Chrome should append a X-Chrome-Connected header to 674 // all Gaia requests from a connected profile so Gaia could return a 204 675 // response and let Chrome handle the action with native UI. The only 676 // exception is requests from gaia webview, since the native profile 677 // management UI is built on top of it. 678 signin::AppendMirrorRequestHeaderIfPossible(request, redirect_url, io_data, 679 info->GetChildID(), info->GetRouteID()); 680} 681 682// Notification that a request has completed. 683void ChromeResourceDispatcherHostDelegate::RequestComplete( 684 net::URLRequest* url_request) { 685 // Jump on the UI thread and inform the prerender about the bytes. 686 const ResourceRequestInfo* info = 687 ResourceRequestInfo::ForRequest(url_request); 688 if (url_request && !url_request->was_cached()) { 689 BrowserThread::PostTask(BrowserThread::UI, 690 FROM_HERE, 691 base::Bind(&UpdatePrerenderNetworkBytesCallback, 692 info->GetChildID(), 693 info->GetRouteID(), 694 url_request->GetTotalReceivedBytes())); 695 } 696} 697 698// static 699void ChromeResourceDispatcherHostDelegate:: 700 SetExternalProtocolHandlerDelegateForTesting( 701 ExternalProtocolHandler::Delegate* delegate) { 702 g_external_protocol_handler_delegate = delegate; 703} 704