1 // Copyright (c) 2011 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_handler.h" 6 7 #include <vector> 8 9 #include "base/logging.h" 10 #include "base/memory/singleton.h" 11 #include "base/metrics/histogram.h" 12 #include "base/string_util.h" 13 #include "chrome/browser/chromeos/network_state_notifier.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/browser/browser_thread.h" 18 #include "content/browser/renderer_host/resource_dispatcher_host.h" 19 #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" 20 #include "net/base/net_errors.h" 21 #include "net/url_request/url_request.h" 22 #include "net/url_request/url_request_context.h" 23 24 OfflineResourceHandler::OfflineResourceHandler( 25 ResourceHandler* handler, 26 int host_id, 27 int route_id, 28 ResourceDispatcherHost* rdh, 29 net::URLRequest* request) 30 : next_handler_(handler), 31 process_host_id_(host_id), 32 render_view_id_(route_id), 33 rdh_(rdh), 34 request_(request), 35 deferred_request_id_(-1) { 36 } 37 38 OfflineResourceHandler::~OfflineResourceHandler() { 39 DCHECK(!appcache_completion_callback_.get()); 40 } 41 42 bool OfflineResourceHandler::OnUploadProgress(int request_id, 43 uint64 position, 44 uint64 size) { 45 return next_handler_->OnUploadProgress(request_id, position, size); 46 } 47 48 bool OfflineResourceHandler::OnRequestRedirected(int request_id, 49 const GURL& new_url, 50 ResourceResponse* response, 51 bool* defer) { 52 return next_handler_->OnRequestRedirected( 53 request_id, new_url, response, defer); 54 } 55 56 bool OfflineResourceHandler::OnResponseStarted(int request_id, 57 ResourceResponse* response) { 58 return next_handler_->OnResponseStarted(request_id, response); 59 } 60 61 bool OfflineResourceHandler::OnResponseCompleted( 62 int request_id, 63 const net::URLRequestStatus& status, 64 const std::string& security_info) { 65 return next_handler_->OnResponseCompleted(request_id, status, security_info); 66 } 67 68 void OfflineResourceHandler::OnRequestClosed() { 69 if (appcache_completion_callback_) { 70 appcache_completion_callback_->Cancel(); 71 appcache_completion_callback_.release(); 72 Release(); // Balanced with OnWillStart 73 } 74 next_handler_->OnRequestClosed(); 75 } 76 77 void OfflineResourceHandler::OnCanHandleOfflineComplete(int rv) { 78 CHECK(appcache_completion_callback_); 79 appcache_completion_callback_ = NULL; 80 if (deferred_request_id_ == -1) { 81 LOG(WARNING) << "OnCanHandleOfflineComplete called after completion: " 82 << " this=" << this; 83 NOTREACHED(); 84 return; 85 } 86 if (rv == net::OK) { 87 Resume(); 88 Release(); // Balanced with OnWillStart 89 } else { 90 // Skipping AddRef/Release because they're redundant. 91 BrowserThread::PostTask( 92 BrowserThread::UI, FROM_HERE, 93 NewRunnableMethod(this, &OfflineResourceHandler::ShowOfflinePage)); 94 } 95 } 96 97 bool OfflineResourceHandler::OnWillStart(int request_id, 98 const GURL& url, 99 bool* defer) { 100 if (ShouldShowOfflinePage(url)) { 101 deferred_request_id_ = request_id; 102 deferred_url_ = url; 103 DVLOG(1) << "OnWillStart: this=" << this << ", request id=" << request_id 104 << ", url=" << url; 105 AddRef(); // Balanced with OnCanHandleOfflineComplete 106 DCHECK(!appcache_completion_callback_); 107 appcache_completion_callback_ = 108 new net::CancelableCompletionCallback<OfflineResourceHandler>( 109 this, &OfflineResourceHandler::OnCanHandleOfflineComplete); 110 ChromeURLRequestContext* url_request_context = 111 static_cast<ChromeURLRequestContext*>(request_->context()); 112 url_request_context->appcache_service()->CanHandleMainResourceOffline( 113 url, appcache_completion_callback_); 114 115 *defer = true; 116 return true; 117 } 118 return next_handler_->OnWillStart(request_id, url, defer); 119 } 120 121 // We'll let the original event handler provide a buffer, and reuse it for 122 // subsequent reads until we're done buffering. 123 bool OfflineResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, 124 int* buf_size, int min_size) { 125 return next_handler_->OnWillRead(request_id, buf, buf_size, min_size); 126 } 127 128 bool OfflineResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { 129 return next_handler_->OnReadCompleted(request_id, bytes_read); 130 } 131 132 void OfflineResourceHandler::OnBlockingPageComplete(bool proceed) { 133 if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { 134 BrowserThread::PostTask( 135 BrowserThread::IO, FROM_HERE, 136 NewRunnableMethod(this, 137 &OfflineResourceHandler::OnBlockingPageComplete, 138 proceed)); 139 return; 140 } 141 if (deferred_request_id_ == -1) { 142 LOG(WARNING) << "OnBlockingPageComplete called after completion: " 143 << " this=" << this; 144 NOTREACHED(); 145 return; 146 } 147 if (proceed) { 148 Resume(); 149 } else { 150 int request_id = deferred_request_id_; 151 ClearRequestInfo(); 152 rdh_->CancelRequest(process_host_id_, request_id, false); 153 } 154 Release(); // Balanced with OnWillStart 155 } 156 157 void OfflineResourceHandler::ClearRequestInfo() { 158 deferred_url_ = GURL(); 159 deferred_request_id_ = -1; 160 } 161 162 bool OfflineResourceHandler::IsRemote(const GURL& url) const { 163 return url.SchemeIs(chrome::kFtpScheme) || 164 url.SchemeIs(chrome::kHttpScheme) || 165 url.SchemeIs(chrome::kHttpsScheme); 166 } 167 168 bool OfflineResourceHandler::ShouldShowOfflinePage(const GURL& url) const { 169 // Only check main frame. If the network is disconnected while 170 // loading other resources, we'll simply show broken link/images. 171 return IsRemote(url) && 172 !chromeos::NetworkStateNotifier::is_connected() && 173 ResourceDispatcherHost::InfoForRequest(request_)->resource_type() 174 == ResourceType::MAIN_FRAME; 175 } 176 177 void OfflineResourceHandler::Resume() { 178 const GURL url = deferred_url_; 179 int request_id = deferred_request_id_; 180 ClearRequestInfo(); 181 182 bool defer = false; 183 DVLOG(1) << "Resume load: this=" << this << ", request id=" << request_id; 184 next_handler_->OnWillStart(request_id, url, &defer); 185 if (!defer) 186 rdh_->StartDeferredRequest(process_host_id_, request_id); 187 } 188 189 void OfflineResourceHandler::ShowOfflinePage() { 190 chromeos::OfflineLoadPage::Show( 191 process_host_id_, render_view_id_, deferred_url_, this); 192 } 193