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