// Copyright (c) 2011 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/renderer_host/offline_resource_handler.h" #include <vector> #include "base/logging.h" #include "base/memory/singleton.h" #include "base/metrics/histogram.h" #include "base/string_util.h" #include "chrome/browser/chromeos/network_state_notifier.h" #include "chrome/browser/chromeos/offline/offline_load_page.h" #include "chrome/browser/net/chrome_url_request_context.h" #include "chrome/common/url_constants.h" #include "content/browser/browser_thread.h" #include "content/browser/renderer_host/resource_dispatcher_host.h" #include "content/browser/renderer_host/resource_dispatcher_host_request_info.h" #include "net/base/net_errors.h" #include "net/url_request/url_request.h" #include "net/url_request/url_request_context.h" OfflineResourceHandler::OfflineResourceHandler( ResourceHandler* handler, int host_id, int route_id, ResourceDispatcherHost* rdh, net::URLRequest* request) : next_handler_(handler), process_host_id_(host_id), render_view_id_(route_id), rdh_(rdh), request_(request), deferred_request_id_(-1) { } OfflineResourceHandler::~OfflineResourceHandler() { DCHECK(!appcache_completion_callback_.get()); } bool OfflineResourceHandler::OnUploadProgress(int request_id, uint64 position, uint64 size) { return next_handler_->OnUploadProgress(request_id, position, size); } bool OfflineResourceHandler::OnRequestRedirected(int request_id, const GURL& new_url, ResourceResponse* response, bool* defer) { return next_handler_->OnRequestRedirected( request_id, new_url, response, defer); } bool OfflineResourceHandler::OnResponseStarted(int request_id, ResourceResponse* response) { return next_handler_->OnResponseStarted(request_id, response); } bool OfflineResourceHandler::OnResponseCompleted( int request_id, const net::URLRequestStatus& status, const std::string& security_info) { return next_handler_->OnResponseCompleted(request_id, status, security_info); } void OfflineResourceHandler::OnRequestClosed() { if (appcache_completion_callback_) { appcache_completion_callback_->Cancel(); appcache_completion_callback_.release(); Release(); // Balanced with OnWillStart } next_handler_->OnRequestClosed(); } void OfflineResourceHandler::OnCanHandleOfflineComplete(int rv) { CHECK(appcache_completion_callback_); appcache_completion_callback_ = NULL; if (deferred_request_id_ == -1) { LOG(WARNING) << "OnCanHandleOfflineComplete called after completion: " << " this=" << this; NOTREACHED(); return; } if (rv == net::OK) { Resume(); Release(); // Balanced with OnWillStart } else { // Skipping AddRef/Release because they're redundant. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, NewRunnableMethod(this, &OfflineResourceHandler::ShowOfflinePage)); } } bool OfflineResourceHandler::OnWillStart(int request_id, const GURL& url, bool* defer) { if (ShouldShowOfflinePage(url)) { deferred_request_id_ = request_id; deferred_url_ = url; DVLOG(1) << "OnWillStart: this=" << this << ", request id=" << request_id << ", url=" << url; AddRef(); // Balanced with OnCanHandleOfflineComplete DCHECK(!appcache_completion_callback_); appcache_completion_callback_ = new net::CancelableCompletionCallback<OfflineResourceHandler>( this, &OfflineResourceHandler::OnCanHandleOfflineComplete); ChromeURLRequestContext* url_request_context = static_cast<ChromeURLRequestContext*>(request_->context()); url_request_context->appcache_service()->CanHandleMainResourceOffline( url, appcache_completion_callback_); *defer = true; return true; } return next_handler_->OnWillStart(request_id, url, defer); } // We'll let the original event handler provide a buffer, and reuse it for // subsequent reads until we're done buffering. bool OfflineResourceHandler::OnWillRead(int request_id, net::IOBuffer** buf, int* buf_size, int min_size) { return next_handler_->OnWillRead(request_id, buf, buf_size, min_size); } bool OfflineResourceHandler::OnReadCompleted(int request_id, int* bytes_read) { return next_handler_->OnReadCompleted(request_id, bytes_read); } void OfflineResourceHandler::OnBlockingPageComplete(bool proceed) { if (!BrowserThread::CurrentlyOn(BrowserThread::IO)) { BrowserThread::PostTask( BrowserThread::IO, FROM_HERE, NewRunnableMethod(this, &OfflineResourceHandler::OnBlockingPageComplete, proceed)); return; } if (deferred_request_id_ == -1) { LOG(WARNING) << "OnBlockingPageComplete called after completion: " << " this=" << this; NOTREACHED(); return; } if (proceed) { Resume(); } else { int request_id = deferred_request_id_; ClearRequestInfo(); rdh_->CancelRequest(process_host_id_, request_id, false); } Release(); // Balanced with OnWillStart } void OfflineResourceHandler::ClearRequestInfo() { deferred_url_ = GURL(); deferred_request_id_ = -1; } bool OfflineResourceHandler::IsRemote(const GURL& url) const { return url.SchemeIs(chrome::kFtpScheme) || url.SchemeIs(chrome::kHttpScheme) || url.SchemeIs(chrome::kHttpsScheme); } bool OfflineResourceHandler::ShouldShowOfflinePage(const GURL& url) const { // Only check main frame. If the network is disconnected while // loading other resources, we'll simply show broken link/images. return IsRemote(url) && !chromeos::NetworkStateNotifier::is_connected() && ResourceDispatcherHost::InfoForRequest(request_)->resource_type() == ResourceType::MAIN_FRAME; } void OfflineResourceHandler::Resume() { const GURL url = deferred_url_; int request_id = deferred_request_id_; ClearRequestInfo(); bool defer = false; DVLOG(1) << "Resume load: this=" << this << ", request id=" << request_id; next_handler_->OnWillStart(request_id, url, &defer); if (!defer) rdh_->StartDeferredRequest(process_host_id_, request_id); } void OfflineResourceHandler::ShowOfflinePage() { chromeos::OfflineLoadPage::Show( process_host_id_, render_view_id_, deferred_url_, this); }