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
OfflineResourceHandler(ResourceHandler * handler,int host_id,int route_id,ResourceDispatcherHost * rdh,net::URLRequest * request)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
~OfflineResourceHandler()38 OfflineResourceHandler::~OfflineResourceHandler() {
39 DCHECK(!appcache_completion_callback_.get());
40 }
41
OnUploadProgress(int request_id,uint64 position,uint64 size)42 bool OfflineResourceHandler::OnUploadProgress(int request_id,
43 uint64 position,
44 uint64 size) {
45 return next_handler_->OnUploadProgress(request_id, position, size);
46 }
47
OnRequestRedirected(int request_id,const GURL & new_url,ResourceResponse * response,bool * defer)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
OnResponseStarted(int request_id,ResourceResponse * response)56 bool OfflineResourceHandler::OnResponseStarted(int request_id,
57 ResourceResponse* response) {
58 return next_handler_->OnResponseStarted(request_id, response);
59 }
60
OnResponseCompleted(int request_id,const net::URLRequestStatus & status,const std::string & security_info)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
OnRequestClosed()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
OnCanHandleOfflineComplete(int rv)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
OnWillStart(int request_id,const GURL & url,bool * defer)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.
OnWillRead(int request_id,net::IOBuffer ** buf,int * buf_size,int min_size)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
OnReadCompleted(int request_id,int * bytes_read)128 bool OfflineResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
129 return next_handler_->OnReadCompleted(request_id, bytes_read);
130 }
131
OnBlockingPageComplete(bool proceed)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
ClearRequestInfo()157 void OfflineResourceHandler::ClearRequestInfo() {
158 deferred_url_ = GURL();
159 deferred_request_id_ = -1;
160 }
161
IsRemote(const GURL & url) const162 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
ShouldShowOfflinePage(const GURL & url) const168 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
Resume()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
ShowOfflinePage()189 void OfflineResourceHandler::ShowOfflinePage() {
190 chromeos::OfflineLoadPage::Show(
191 process_host_id_, render_view_id_, deferred_url_, this);
192 }
193