offline_resource_throttle.cc revision a3f6a49ab37290eeeb8db0f41ec0f1cb74a68be7
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 "chrome/common/url_constants.h" 17#include "content/public/browser/browser_thread.h" 18#include "content/public/browser/render_view_host.h" 19#include "content/public/browser/resource_controller.h" 20#include "content/public/browser/resource_request_info.h" 21#include "content/public/browser/web_contents.h" 22#include "net/base/net_errors.h" 23#include "net/base/net_util.h" 24#include "net/base/network_change_notifier.h" 25#include "net/url_request/url_request.h" 26#include "net/url_request/url_request_context.h" 27#include "webkit/browser/appcache/appcache_service.h" 28 29using content::BrowserThread; 30using content::RenderViewHost; 31using content::WebContents; 32 33namespace { 34 35void 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 61OfflineResourceThrottle::OfflineResourceThrottle( 62 net::URLRequest* request, 63 appcache::AppCacheService* appcache_service) 64 : request_(request), 65 appcache_service_(appcache_service) { 66 DCHECK(appcache_service); 67} 68 69OfflineResourceThrottle::~OfflineResourceThrottle() { 70 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 71 72 if (!appcache_completion_callback_.IsCancelled()) 73 appcache_completion_callback_.Cancel(); 74} 75 76void 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 106const char* OfflineResourceThrottle::GetNameForLogging() const { 107 return "OfflineResourceThrottle"; 108} 109 110void 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 120bool OfflineResourceThrottle::IsRemote(const GURL& url) const { 121 return !net::IsLocalhost(url.host()) && (url.SchemeIs(content::kFtpScheme) || 122 url.SchemeIs(content::kHttpScheme) || 123 url.SchemeIs(content::kHttpsScheme)); 124} 125 126bool 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 132void 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