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