chrome_resource_dispatcher_host_delegate.cc revision c2e0dbddbe15c98d52c4786dac06cb8952a8ae6d
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 "chrome/browser/renderer_host/chrome_resource_dispatcher_host_delegate.h"
6
7#include <string>
8
9#include "base/base64.h"
10#include "base/logging.h"
11#include "chrome/browser/browser_process.h"
12#include "chrome/browser/content_settings/host_content_settings_map.h"
13#include "chrome/browser/download/download_request_limiter.h"
14#include "chrome/browser/download/download_resource_throttle.h"
15#include "chrome/browser/download/download_util.h"
16#include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
17#include "chrome/browser/extensions/extension_info_map.h"
18#include "chrome/browser/extensions/user_script_listener.h"
19#include "chrome/browser/external_protocol/external_protocol_handler.h"
20#include "chrome/browser/google/google_util.h"
21#include "chrome/browser/metrics/variations/variations_http_header_provider.h"
22#include "chrome/browser/net/resource_prefetch_predictor_observer.h"
23#include "chrome/browser/prerender/prerender_manager.h"
24#include "chrome/browser/prerender/prerender_tracker.h"
25#include "chrome/browser/prerender/prerender_util.h"
26#include "chrome/browser/profiles/profile.h"
27#include "chrome/browser/profiles/profile_io_data.h"
28#include "chrome/browser/renderer_host/chrome_url_request_user_data.h"
29#include "chrome/browser/renderer_host/safe_browsing_resource_throttle_factory.h"
30#include "chrome/browser/safe_browsing/safe_browsing_service.h"
31#include "chrome/browser/ui/auto_login_prompter.h"
32#include "chrome/browser/ui/login/login_prompt.h"
33#include "chrome/browser/ui/sync/one_click_signin_helper.h"
34#include "chrome/common/chrome_notification_types.h"
35#include "chrome/common/extensions/mime_types_handler.h"
36#include "chrome/common/extensions/user_script.h"
37#include "chrome/common/render_messages.h"
38#include "content/public/browser/browser_thread.h"
39#include "content/public/browser/notification_service.h"
40#include "content/public/browser/render_view_host.h"
41#include "content/public/browser/resource_context.h"
42#include "content/public/browser/resource_dispatcher_host.h"
43#include "content/public/browser/resource_request_info.h"
44#include "content/public/browser/stream_handle.h"
45#include "content/public/common/resource_response.h"
46#include "extensions/common/constants.h"
47#include "net/base/load_flags.h"
48#include "net/base/load_timing_info.h"
49#include "net/http/http_response_headers.h"
50#include "net/ssl/ssl_config_service.h"
51#include "net/url_request/url_request.h"
52
53#if defined(ENABLE_MANAGED_USERS)
54#include "chrome/browser/managed_mode/managed_mode_resource_throttle.h"
55#endif
56
57#if defined(USE_SYSTEM_PROTOBUF)
58#include <google/protobuf/repeated_field.h>
59#else
60#include "third_party/protobuf/src/google/protobuf/repeated_field.h"
61#endif
62
63#if defined(OS_ANDROID)
64#include "chrome/browser/android/intercept_download_resource_throttle.h"
65#include "components/navigation_interception/intercept_navigation_delegate.h"
66#endif
67
68#if defined(OS_CHROMEOS)
69#include "chrome/browser/chromeos/login/merge_session_throttle.h"
70// TODO(oshima): Enable this for other platforms.
71#include "chrome/browser/renderer_host/offline_resource_throttle.h"
72#endif
73
74using content::BrowserThread;
75using content::RenderViewHost;
76using content::ResourceDispatcherHostLoginDelegate;
77using content::ResourceRequestInfo;
78using extensions::Extension;
79using extensions::StreamsPrivateAPI;
80
81namespace {
82
83void NotifyDownloadInitiatedOnUI(int render_process_id, int render_view_id) {
84  RenderViewHost* rvh = RenderViewHost::FromID(render_process_id,
85                                               render_view_id);
86  if (!rvh)
87    return;
88
89  content::NotificationService::current()->Notify(
90      chrome::NOTIFICATION_DOWNLOAD_INITIATED,
91      content::Source<RenderViewHost>(rvh),
92      content::NotificationService::NoDetails());
93}
94
95// Goes through the extension's file browser handlers and checks if there is one
96// that can handle the |mime_type|.
97// |extension| must not be NULL.
98bool ExtensionCanHandleMimeType(const Extension* extension,
99                                const std::string& mime_type) {
100  MimeTypesHandler* handler = MimeTypesHandler::GetHandler(extension);
101  if (!handler)
102    return false;
103
104  return handler->CanHandleMIMEType(mime_type);
105}
106
107void SendExecuteMimeTypeHandlerEvent(scoped_ptr<content::StreamHandle> stream,
108                                     int render_process_id,
109                                     int render_view_id,
110                                     const std::string& extension_id) {
111  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
112
113  content::RenderViewHost* render_view_host =
114      content::RenderViewHost::FromID(render_process_id, render_view_id);
115  if (!render_view_host)
116    return;
117
118  content::WebContents* web_contents =
119      content::WebContents::FromRenderViewHost(render_view_host);
120  if (!web_contents)
121    return;
122
123  content::BrowserContext* browser_context = web_contents->GetBrowserContext();
124  if (!browser_context)
125    return;
126
127  Profile* profile = Profile::FromBrowserContext(browser_context);
128  if (!profile)
129    return;
130
131  StreamsPrivateAPI* streams_private = StreamsPrivateAPI::Get(profile);
132  if (!streams_private)
133    return;
134  streams_private->ExecuteMimeTypeHandler(
135      extension_id, web_contents, stream.Pass());
136}
137
138}  // end namespace
139
140ChromeResourceDispatcherHostDelegate::ChromeResourceDispatcherHostDelegate(
141    prerender::PrerenderTracker* prerender_tracker)
142    : download_request_limiter_(g_browser_process->download_request_limiter()),
143      safe_browsing_(g_browser_process->safe_browsing_service()),
144      user_script_listener_(new extensions::UserScriptListener()),
145      prerender_tracker_(prerender_tracker) {
146}
147
148ChromeResourceDispatcherHostDelegate::~ChromeResourceDispatcherHostDelegate() {
149}
150
151bool ChromeResourceDispatcherHostDelegate::ShouldBeginRequest(
152    int child_id,
153    int route_id,
154    const std::string& method,
155    const GURL& url,
156    ResourceType::Type resource_type,
157    content::ResourceContext* resource_context,
158    const content::Referrer& referrer) {
159  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
160
161  // Handle a PREFETCH resource type. If prefetch is disabled, squelch the
162  // request.  Otherwise, do a normal request to warm the cache.
163  if (resource_type == ResourceType::PREFETCH) {
164    // All PREFETCH requests should be GETs, but be defensive about it.
165    if (method != "GET")
166      return false;
167
168    // If prefetch is disabled, kill the request.
169    if (!prerender::PrerenderManager::IsPrefetchEnabled())
170      return false;
171  }
172
173  // Abort any prerenders that spawn requests that use invalid HTTP methods
174  // or invalid schemes.
175  if (prerender_tracker_->IsPrerenderingOnIOThread(child_id, route_id)) {
176    if (!prerender::PrerenderManager::IsValidHttpMethod(method)) {
177      prerender_tracker_->TryCancelOnIOThread(
178          child_id, route_id, prerender::FINAL_STATUS_INVALID_HTTP_METHOD);
179      return false;
180    }
181    if (!prerender::PrerenderManager::DoesURLHaveValidScheme(url)) {
182      prerender_tracker_->TryCancelOnIOThread(
183          child_id, route_id, prerender::FINAL_STATUS_UNSUPPORTED_SCHEME);
184      return false;
185    }
186  }
187
188  return true;
189}
190
191void ChromeResourceDispatcherHostDelegate::RequestBeginning(
192    net::URLRequest* request,
193    content::ResourceContext* resource_context,
194    appcache::AppCacheService* appcache_service,
195    ResourceType::Type resource_type,
196    int child_id,
197    int route_id,
198    bool is_continuation_of_transferred_request,
199    ScopedVector<content::ResourceThrottle>* throttles) {
200  if (is_continuation_of_transferred_request)
201    ChromeURLRequestUserData::Delete(request);
202
203  ChromeURLRequestUserData* user_data =
204      ChromeURLRequestUserData::Create(request);
205  bool is_prerendering = prerender_tracker_->IsPrerenderingOnIOThread(
206      child_id, route_id);
207  if (is_prerendering) {
208    user_data->set_is_prerender(true);
209    request->SetPriority(net::IDLE);
210  }
211
212#if defined(OS_ANDROID)
213  if (!is_prerendering && resource_type == ResourceType::MAIN_FRAME) {
214    throttles->push_back(
215        components::InterceptNavigationDelegate::CreateThrottleFor(request));
216  }
217#endif
218#if defined(OS_CHROMEOS)
219  if (resource_type == ResourceType::MAIN_FRAME) {
220    // We check offline first, then check safe browsing so that we still can
221    // block unsafe site after we remove offline page.
222    throttles->push_back(new OfflineResourceThrottle(
223        child_id, route_id, request, appcache_service));
224    // Add interstitial page while merge session process (cookie
225    // reconstruction from OAuth2 refresh token in ChromeOS login) is still in
226    // progress while we are attempting to load a google property.
227    throttles->push_back(new MergeSessionThrottle(
228        child_id, route_id, request));
229  }
230#endif
231
232  // Don't attempt to append headers to requests that have already started.
233  // TODO(stevet): Remove this once the request ordering issues are resolved
234  // in crbug.com/128048.
235  if (!request->is_pending()) {
236    net::HttpRequestHeaders headers;
237    headers.CopyFrom(request->extra_request_headers());
238    ProfileIOData* io_data = ProfileIOData::FromResourceContext(
239        resource_context);
240    bool incognito = io_data->is_incognito();
241    chrome_variations::VariationsHttpHeaderProvider::GetInstance()->
242        AppendHeaders(request->url(),
243                      incognito,
244                      !incognito && io_data->GetMetricsEnabledStateOnIOThread(),
245                      &headers);
246    request->SetExtraRequestHeaders(headers);
247  }
248
249#if defined(ENABLE_ONE_CLICK_SIGNIN)
250  AppendChromeSyncGaiaHeader(request, resource_context);
251#endif
252
253  AppendStandardResourceThrottles(request,
254                                  resource_context,
255                                  child_id,
256                                  route_id,
257                                  resource_type,
258                                  throttles);
259
260  ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
261  if (io_data->resource_prefetch_predictor_observer()) {
262    io_data->resource_prefetch_predictor_observer()->OnRequestStarted(
263        request, resource_type, child_id, route_id);
264  }
265}
266
267void ChromeResourceDispatcherHostDelegate::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  BrowserThread::PostTask(
277      BrowserThread::UI, FROM_HERE,
278      base::Bind(&NotifyDownloadInitiatedOnUI, child_id, route_id));
279
280  // If it's from the web, we don't trust it, so we push the throttle on.
281  if (is_content_initiated) {
282    throttles->push_back(new DownloadResourceThrottle(
283        download_request_limiter_, child_id, route_id, request_id,
284        request->method()));
285#if defined(OS_ANDROID)
286    throttles->push_back(
287        new chrome::InterceptDownloadResourceThrottle(
288            request, child_id, route_id, request_id));
289#endif
290  }
291
292  // If this isn't a new request, we've seen this before and added the standard
293  //  resource throttles already so no need to add it again.
294  if (!request->is_pending()) {
295    AppendStandardResourceThrottles(request,
296                                    resource_context,
297                                    child_id,
298                                    route_id,
299                                    ResourceType::MAIN_FRAME,
300                                    throttles);
301  }
302}
303
304bool ChromeResourceDispatcherHostDelegate::AcceptSSLClientCertificateRequest(
305    net::URLRequest* request, net::SSLCertRequestInfo* cert_request_info) {
306  if (request->load_flags() & net::LOAD_PREFETCH)
307    return false;
308
309  ChromeURLRequestUserData* user_data = ChromeURLRequestUserData::Get(request);
310  if (user_data && user_data->is_prerender()) {
311    int child_id, route_id;
312    if (ResourceRequestInfo::ForRequest(request)->GetAssociatedRenderView(
313            &child_id, &route_id)) {
314      if (prerender_tracker_->TryCancel(
315              child_id, route_id,
316              prerender::FINAL_STATUS_SSL_CLIENT_CERTIFICATE_REQUESTED)) {
317        return false;
318      }
319    }
320  }
321
322  return true;
323}
324
325bool ChromeResourceDispatcherHostDelegate::AcceptAuthRequest(
326    net::URLRequest* request,
327    net::AuthChallengeInfo* auth_info) {
328  ChromeURLRequestUserData* user_data = ChromeURLRequestUserData::Get(request);
329  if (!user_data || !user_data->is_prerender())
330    return true;
331
332  int child_id, route_id;
333  if (!ResourceRequestInfo::ForRequest(request)->GetAssociatedRenderView(
334          &child_id, &route_id)) {
335    NOTREACHED();
336    return true;
337  }
338
339  if (!prerender_tracker_->TryCancelOnIOThread(
340          child_id, route_id, prerender::FINAL_STATUS_AUTH_NEEDED)) {
341    return true;
342  }
343
344  return false;
345}
346
347ResourceDispatcherHostLoginDelegate*
348    ChromeResourceDispatcherHostDelegate::CreateLoginDelegate(
349        net::AuthChallengeInfo* auth_info, net::URLRequest* request) {
350  return CreateLoginPrompt(auth_info, request);
351}
352
353bool ChromeResourceDispatcherHostDelegate::HandleExternalProtocol(
354    const GURL& url, int child_id, int route_id) {
355#if defined(OS_ANDROID)
356  // Android use a resource throttle to handle external as well as internal
357  // protocols.
358  return false;
359#else
360
361  if (prerender_tracker_->IsPrerenderingOnIOThread(child_id, route_id)) {
362    prerender_tracker_->TryCancel(
363        child_id, route_id, prerender::FINAL_STATUS_UNSUPPORTED_SCHEME);
364    return false;
365  }
366
367  BrowserThread::PostTask(
368      BrowserThread::UI, FROM_HERE,
369      base::Bind(&ExternalProtocolHandler::LaunchUrl, url, child_id, route_id));
370  return true;
371#endif
372}
373
374void ChromeResourceDispatcherHostDelegate::AppendStandardResourceThrottles(
375    net::URLRequest* request,
376    content::ResourceContext* resource_context,
377    int child_id,
378    int route_id,
379    ResourceType::Type resource_type,
380    ScopedVector<content::ResourceThrottle>* throttles) {
381  ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
382#if defined(FULL_SAFE_BROWSING) || defined(MOBILE_SAFE_BROWSING)
383  // Insert safe browsing at the front of the list, so it gets to decide on
384  // policies first.
385  if (io_data->safe_browsing_enabled()->GetValue()) {
386    bool is_subresource_request = resource_type != ResourceType::MAIN_FRAME;
387    content::ResourceThrottle* throttle =
388        SafeBrowsingResourceThrottleFactory::Create(request, child_id, route_id,
389            is_subresource_request, safe_browsing_);
390    if (throttle)
391      throttles->push_back(throttle);
392  }
393#endif
394
395#if defined(ENABLE_MANAGED_USERS)
396  bool is_subresource_request = resource_type != ResourceType::MAIN_FRAME;
397  throttles->push_back(new ManagedModeResourceThrottle(
398        request, child_id, route_id, !is_subresource_request,
399        io_data->managed_mode_url_filter()));
400#endif
401
402  content::ResourceThrottle* throttle =
403      user_script_listener_->CreateResourceThrottle(request->url(),
404                                                    resource_type);
405  if (throttle)
406    throttles->push_back(throttle);
407}
408
409#if defined(ENABLE_ONE_CLICK_SIGNIN)
410void ChromeResourceDispatcherHostDelegate::AppendChromeSyncGaiaHeader(
411    net::URLRequest* request,
412    content::ResourceContext* resource_context) {
413  static const char kAllowChromeSignIn[] = "Allow-Chrome-SignIn";
414
415  ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
416  OneClickSigninHelper::Offer offer =
417      OneClickSigninHelper::CanOfferOnIOThread(request, io_data);
418  switch (offer) {
419    case OneClickSigninHelper::CAN_OFFER:
420      request->SetExtraRequestHeaderByName(kAllowChromeSignIn, "1", false);
421      break;
422    case OneClickSigninHelper::DONT_OFFER:
423      request->RemoveRequestHeaderByName(kAllowChromeSignIn);
424      break;
425    case OneClickSigninHelper::IGNORE_REQUEST:
426      break;
427  }
428}
429#endif
430
431bool ChromeResourceDispatcherHostDelegate::ShouldForceDownloadResource(
432    const GURL& url, const std::string& mime_type) {
433  // Special-case user scripts to get downloaded instead of viewed.
434  return extensions::UserScript::IsURLUserScript(url, mime_type);
435}
436
437bool ChromeResourceDispatcherHostDelegate::ShouldInterceptResourceAsStream(
438    content::ResourceContext* resource_context,
439    const GURL& url,
440    const std::string& mime_type,
441    GURL* security_origin,
442    std::string* target_id) {
443#if !defined(OS_ANDROID)
444  ProfileIOData* io_data =
445      ProfileIOData::FromResourceContext(resource_context);
446  bool profile_is_incognito = io_data->is_incognito();
447  const scoped_refptr<const ExtensionInfoMap> extension_info_map(
448      io_data->GetExtensionInfoMap());
449  std::vector<std::string> whitelist = MimeTypesHandler::GetMIMETypeWhitelist();
450  // Go through the white-listed extensions and try to use them to intercept
451  // the URL request.
452  for (size_t i = 0; i < whitelist.size(); ++i) {
453    const char* extension_id = whitelist[i].c_str();
454    const Extension* extension =
455        extension_info_map->extensions().GetByID(extension_id);
456    // The white-listed extension may not be installed, so we have to NULL check
457    // |extension|.
458    if (!extension ||
459        (profile_is_incognito &&
460         !extension_info_map->IsIncognitoEnabled(extension_id))) {
461      continue;
462    }
463
464    if (ExtensionCanHandleMimeType(extension, mime_type)) {
465      *security_origin = Extension::GetBaseURLFromExtensionId(extension_id);
466      *target_id = extension_id;
467      return true;
468    }
469  }
470#endif
471  return false;
472}
473
474void ChromeResourceDispatcherHostDelegate::OnStreamCreated(
475    content::ResourceContext* resource_context,
476    int render_process_id,
477    int render_view_id,
478    const std::string& target_id,
479    scoped_ptr<content::StreamHandle> stream) {
480#if !defined(OS_ANDROID)
481  content::BrowserThread::PostTask(
482      content::BrowserThread::UI, FROM_HERE,
483      base::Bind(&SendExecuteMimeTypeHandlerEvent, base::Passed(&stream),
484                 render_process_id, render_view_id,
485                 target_id));
486#endif
487}
488
489void ChromeResourceDispatcherHostDelegate::OnResponseStarted(
490    net::URLRequest* request,
491    content::ResourceContext* resource_context,
492    content::ResourceResponse* response,
493    IPC::Sender* sender) {
494  // TODO(mmenke):  Figure out if LOAD_ENABLE_LOAD_TIMING is safe to remove.
495  if (request->load_flags() & net::LOAD_ENABLE_LOAD_TIMING)
496    request->GetLoadTimingInfo(&response->head.load_timing);
497
498  const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
499
500  if (request->url().SchemeIsSecure()) {
501    const net::URLRequestContext* context = request->context();
502    net::TransportSecurityState* state = context->transport_security_state();
503    if (state) {
504      net::TransportSecurityState::DomainState domain_state;
505      bool has_sni = net::SSLConfigService::IsSNIAvailable(
506          context->ssl_config_service());
507      if (state->GetDomainState(request->url().host(), has_sni,
508                                &domain_state) &&
509          domain_state.ShouldUpgradeToSSL()) {
510        sender->Send(new ChromeViewMsg_AddStrictSecurityHost(
511            info->GetRouteID(), request->url().host()));
512      }
513    }
514  }
515
516  // See if the response contains the X-Auto-Login header.  If so, this was
517  // a request for a login page, and the server is allowing the browser to
518  // suggest auto-login, if available.
519  AutoLoginPrompter::ShowInfoBarIfPossible(request, info->GetChildID(),
520                                           info->GetRouteID());
521
522  ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
523
524#if defined(ENABLE_ONE_CLICK_SIGNIN)
525  // See if the response contains the Google-Accounts-SignIn header.  If so,
526  // then the user has just finished signing in, and the server is allowing the
527  // browser to suggest connecting the user's profile to the account.
528  OneClickSigninHelper::ShowInfoBarIfPossible(request, io_data,
529                                              info->GetChildID(),
530                                              info->GetRouteID());
531#endif
532
533  // Build in additional protection for the chrome web store origin.
534  GURL webstore_url(extension_urls::GetWebstoreLaunchURL());
535  if (request->url().DomainIs(webstore_url.host().c_str())) {
536    net::HttpResponseHeaders* response_headers = request->response_headers();
537    if (!response_headers->HasHeaderValue("x-frame-options", "deny") &&
538        !response_headers->HasHeaderValue("x-frame-options", "sameorigin")) {
539      response_headers->RemoveHeader("x-frame-options");
540      response_headers->AddHeader("x-frame-options: sameorigin");
541    }
542  }
543
544  if (io_data->resource_prefetch_predictor_observer())
545    io_data->resource_prefetch_predictor_observer()->OnResponseStarted(request);
546
547  prerender::URLRequestResponseStarted(request);
548}
549
550void ChromeResourceDispatcherHostDelegate::OnRequestRedirected(
551    const GURL& redirect_url,
552    net::URLRequest* request,
553    content::ResourceContext* resource_context,
554    content::ResourceResponse* response) {
555  // TODO(mmenke):  Figure out if LOAD_ENABLE_LOAD_TIMING is safe to remove.
556  if (request->load_flags() & net::LOAD_ENABLE_LOAD_TIMING)
557    request->GetLoadTimingInfo(&response->head.load_timing);
558
559  ProfileIOData* io_data = ProfileIOData::FromResourceContext(resource_context);
560
561#if defined(ENABLE_ONE_CLICK_SIGNIN)
562  const ResourceRequestInfo* info = ResourceRequestInfo::ForRequest(request);
563
564  // See if the response contains the Google-Accounts-SignIn header.  If so,
565  // then the user has just finished signing in, and the server is allowing the
566  // browser to suggest connecting the user's profile to the account.
567  OneClickSigninHelper::ShowInfoBarIfPossible(request, io_data,
568                                              info->GetChildID(),
569                                              info->GetRouteID());
570  AppendChromeSyncGaiaHeader(request, resource_context);
571#endif
572
573  if (io_data->resource_prefetch_predictor_observer()) {
574    io_data->resource_prefetch_predictor_observer()->OnRequestRedirected(
575        redirect_url, request);
576  }
577
578  int child_id, route_id;
579  if (!prerender::PrerenderManager::DoesURLHaveValidScheme(redirect_url) &&
580      ResourceRequestInfo::ForRequest(request)->GetAssociatedRenderView(
581          &child_id, &route_id) &&
582      prerender_tracker_->IsPrerenderingOnIOThread(child_id, route_id)) {
583    prerender_tracker_->TryCancel(
584        child_id, route_id, prerender::FINAL_STATUS_UNSUPPORTED_SCHEME);
585    request->Cancel();
586  }
587}
588