offline_resource_handler.cc revision ddb351dbec246cf1fab5ec20d2d5520909041de1
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
24OfflineResourceHandler::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
38OfflineResourceHandler::~OfflineResourceHandler() {
39  DCHECK(!appcache_completion_callback_.get());
40}
41
42bool OfflineResourceHandler::OnUploadProgress(int request_id,
43                                               uint64 position,
44                                               uint64 size) {
45  return next_handler_->OnUploadProgress(request_id, position, size);
46}
47
48bool 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
56bool OfflineResourceHandler::OnResponseStarted(int request_id,
57                                                ResourceResponse* response) {
58  return next_handler_->OnResponseStarted(request_id, response);
59}
60
61bool 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
68void 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
77void 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
97bool 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.
123bool 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
128bool OfflineResourceHandler::OnReadCompleted(int request_id, int* bytes_read) {
129  return next_handler_->OnReadCompleted(request_id, bytes_read);
130}
131
132void 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
157void OfflineResourceHandler::ClearRequestInfo() {
158  deferred_url_ = GURL();
159  deferred_request_id_ = -1;
160}
161
162bool OfflineResourceHandler::IsRemote(const GURL& url) const {
163  return url.SchemeIs(chrome::kFtpScheme) ||
164         url.SchemeIs(chrome::kHttpScheme) ||
165         url.SchemeIs(chrome::kHttpsScheme);
166}
167
168bool 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
177void 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
189void OfflineResourceHandler::ShowOfflinePage() {
190  chromeos::OfflineLoadPage::Show(
191      process_host_id_, render_view_id_, deferred_url_, this);
192}
193