Home | History | Annotate | Download | only in renderer_host
      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/offline_resource_throttle.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/bind.h"
     10 #include "base/logging.h"
     11 #include "base/memory/singleton.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/strings/string_util.h"
     14 #include "chrome/browser/chromeos/offline/offline_load_page.h"
     15 #include "content/public/browser/appcache_service.h"
     16 #include "content/public/browser/browser_thread.h"
     17 #include "content/public/browser/render_view_host.h"
     18 #include "content/public/browser/resource_controller.h"
     19 #include "content/public/browser/resource_request_info.h"
     20 #include "content/public/browser/web_contents.h"
     21 #include "net/base/net_errors.h"
     22 #include "net/base/net_util.h"
     23 #include "net/base/network_change_notifier.h"
     24 #include "net/url_request/url_request.h"
     25 #include "net/url_request/url_request_context.h"
     26 #include "url/url_constants.h"
     27 
     28 using content::BrowserThread;
     29 using content::RenderViewHost;
     30 using content::WebContents;
     31 
     32 namespace {
     33 
     34 void ShowOfflinePage(
     35     int render_process_id,
     36     int render_view_id,
     37     const GURL& url,
     38     const chromeos::OfflineLoadPage::CompletionCallback& callback) {
     39   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     40 
     41   // Check again on UI thread and proceed if it's connected.
     42   if (!net::NetworkChangeNotifier::IsOffline()) {
     43     BrowserThread::PostTask(
     44         BrowserThread::IO, FROM_HERE, base::Bind(callback, true));
     45   } else {
     46     RenderViewHost* render_view_host =
     47         RenderViewHost::FromID(render_process_id, render_view_id);
     48     WebContents* web_contents = render_view_host ?
     49         WebContents::FromRenderViewHost(render_view_host) : NULL;
     50     // There is a chance that the tab closed after we decided to show
     51     // the offline page on the IO thread and before we actually show the
     52     // offline page here on the UI thread.
     53     if (web_contents)
     54       (new chromeos::OfflineLoadPage(web_contents, url, callback))->Show();
     55   }
     56 }
     57 
     58 }  // namespace
     59 
     60 OfflineResourceThrottle::OfflineResourceThrottle(
     61     net::URLRequest* request,
     62     content::AppCacheService* appcache_service)
     63     : request_(request),
     64       appcache_service_(appcache_service) {
     65   DCHECK(appcache_service);
     66 }
     67 
     68 OfflineResourceThrottle::~OfflineResourceThrottle() {
     69   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     70 
     71   if (!appcache_completion_callback_.IsCancelled())
     72     appcache_completion_callback_.Cancel();
     73 }
     74 
     75 void OfflineResourceThrottle::WillStartRequest(bool* defer) {
     76   if (!ShouldShowOfflinePage(request_->url()))
     77     return;
     78 
     79   DVLOG(1) << "WillStartRequest: this=" << this << ", url=" << request_->url();
     80 
     81   const GURL* url = &(request_->url());
     82   const GURL* first_party = &(request_->first_party_for_cookies());
     83 
     84   // Anticipate a client-side HSTS based redirect from HTTP to HTTPS, and
     85   // ask the appcache about the HTTPS url instead of the HTTP url.
     86   GURL redirect_url;
     87   if (request_->GetHSTSRedirect(&redirect_url)) {
     88     if (url->GetOrigin() == first_party->GetOrigin())
     89       first_party = &redirect_url;
     90     url = &redirect_url;
     91   }
     92 
     93   DCHECK(appcache_completion_callback_.IsCancelled());
     94 
     95   appcache_completion_callback_.Reset(
     96       base::Bind(&OfflineResourceThrottle::OnCanHandleOfflineComplete,
     97                  AsWeakPtr()));
     98   appcache_service_->CanHandleMainResourceOffline(
     99       *url, *first_party,
    100       appcache_completion_callback_.callback());
    101 
    102   *defer = true;
    103 }
    104 
    105 const char* OfflineResourceThrottle::GetNameForLogging() const {
    106   return "OfflineResourceThrottle";
    107 }
    108 
    109 void OfflineResourceThrottle::OnBlockingPageComplete(bool proceed) {
    110   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    111 
    112   if (proceed) {
    113     controller()->Resume();
    114   } else {
    115     controller()->Cancel();
    116   }
    117 }
    118 
    119 bool OfflineResourceThrottle::IsRemote(const GURL& url) const {
    120   return !net::IsLocalhost(url.host()) && (url.SchemeIs(url::kFtpScheme) ||
    121                                            url.SchemeIs(url::kHttpScheme) ||
    122                                            url.SchemeIs(url::kHttpsScheme));
    123 }
    124 
    125 bool OfflineResourceThrottle::ShouldShowOfflinePage(const GURL& url) const {
    126   // If the network is disconnected while loading other resources, we'll simply
    127   // show broken link/images.
    128   return IsRemote(url) && net::NetworkChangeNotifier::IsOffline();
    129 }
    130 
    131 void OfflineResourceThrottle::OnCanHandleOfflineComplete(int rv) {
    132   appcache_completion_callback_.Cancel();
    133 
    134   if (rv == net::OK) {
    135     controller()->Resume();
    136   } else {
    137     const content::ResourceRequestInfo* info =
    138         content::ResourceRequestInfo::ForRequest(request_);
    139     BrowserThread::PostTask(
    140         BrowserThread::UI,
    141         FROM_HERE,
    142         base::Bind(
    143             &ShowOfflinePage,
    144             info->GetChildID(),
    145             info->GetRouteID(),
    146             request_->url(),
    147             base::Bind(
    148                 &OfflineResourceThrottle::OnBlockingPageComplete,
    149                 AsWeakPtr())));
    150   }
    151 }
    152