diff options
-rw-r--r-- | net/disk_cache/backend_impl.cc | 10 | ||||
-rw-r--r-- | net/disk_cache/histogram_macros.h | 3 | ||||
-rw-r--r-- | webkit/appcache/appcache_database.cc | 5 | ||||
-rw-r--r-- | webkit/appcache/appcache_disk_cache.cc | 155 | ||||
-rw-r--r-- | webkit/appcache/appcache_disk_cache.h | 99 | ||||
-rw-r--r-- | webkit/appcache/appcache_response.cc | 191 | ||||
-rw-r--r-- | webkit/appcache/appcache_response.h | 75 | ||||
-rw-r--r-- | webkit/appcache/appcache_response_unittest.cc | 4 | ||||
-rw-r--r-- | webkit/appcache/appcache_storage.cc | 3 | ||||
-rw-r--r-- | webkit/appcache/appcache_storage_impl.cc | 54 | ||||
-rw-r--r-- | webkit/appcache/appcache_storage_impl.h | 13 | ||||
-rw-r--r-- | webkit/appcache/appcache_storage_unittest.cc | 3 | ||||
-rw-r--r-- | webkit/appcache/appcache_url_request_job.cc | 4 | ||||
-rw-r--r-- | webkit/appcache/mock_appcache_storage.h | 9 | ||||
-rw-r--r-- | webkit/appcache/webkit_appcache.gypi | 2 |
15 files changed, 538 insertions, 92 deletions
diff --git a/net/disk_cache/backend_impl.cc b/net/disk_cache/backend_impl.cc index 194a74e..534c33f 100644 --- a/net/disk_cache/backend_impl.cc +++ b/net/disk_cache/backend_impl.cc @@ -173,6 +173,16 @@ Backend* CreateCacheBackend(const FilePath& full_path, bool force, return BackendImpl::CreateBackend(full_path, force, max_bytes, type, kNone); } +int CreateCacheBackend(net::CacheType type, const FilePath& path, int max_bytes, + bool force, Backend** backend, + CompletionCallback* callback) { + if (type == net::MEMORY_CACHE) + *backend = CreateInMemoryCacheBackend(max_bytes); + else + *backend = BackendImpl::CreateBackend(path, force, max_bytes, type, kNone); + return *backend ? net::OK : net::ERR_FAILED; +} + int PreferedCacheSize(int64 available) { // If there is not enough space to use kDefaultCacheSize, use 80% of the // available space. diff --git a/net/disk_cache/histogram_macros.h b/net/disk_cache/histogram_macros.h index 27610f5..8d5966c 100644 --- a/net/disk_cache/histogram_macros.h +++ b/net/disk_cache/histogram_macros.h @@ -53,6 +53,9 @@ case net::MEDIA_CACHE:\ UMA_HISTOGRAM_##type(my_name.data(), sample);\ break;\ + case net::APP_CACHE:\ + UMA_HISTOGRAM_##type(my_name.data(), sample);\ + break;\ default:\ NOTREACHED();\ break;\ diff --git a/webkit/appcache/appcache_database.cc b/webkit/appcache/appcache_database.cc index c4124c59..a1aa59b 100644 --- a/webkit/appcache/appcache_database.cc +++ b/webkit/appcache/appcache_database.cc @@ -18,8 +18,8 @@ // Schema ------------------------------------------------------------------- namespace { -const int kCurrentVersion = 2; -const int kCompatibleVersion = 2; +const int kCurrentVersion = 3; +const int kCompatibleVersion = 3; const char* kGroupsTable = "Groups"; const char* kCachesTable = "Caches"; @@ -1081,6 +1081,7 @@ bool AppCacheDatabase::DeleteExistingAndCreateNewDatabase() { ResetConnectionAndTables(); + // This also deletes the disk cache data. FilePath directory = db_file_path_.DirName(); if (!file_util::Delete(directory, true) || !file_util::CreateDirectory(directory)) { diff --git a/webkit/appcache/appcache_disk_cache.cc b/webkit/appcache/appcache_disk_cache.cc new file mode 100644 index 0000000..b8b25bf --- /dev/null +++ b/webkit/appcache/appcache_disk_cache.cc @@ -0,0 +1,155 @@ +// Copyright (c) 2010 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 "webkit/appcache/appcache_disk_cache.h" + +#include "base/file_path.h" +#include "base/logging.h" +#include "base/string_util.h" +#include "net/base/net_errors.h" + +namespace appcache { + +AppCacheDiskCache::AppCacheDiskCache() + : is_disabled_(false), init_callback_(NULL) { +} + +AppCacheDiskCache::~AppCacheDiskCache() { + if (create_backend_callback_) { + create_backend_callback_->Cancel(); + create_backend_callback_.release(); + OnCreateBackendComplete(net::ERR_ABORTED); + } +} + +int AppCacheDiskCache::InitWithDiskBackend( + const FilePath& disk_cache_directory, int disk_cache_size, bool force, + net::CompletionCallback* callback) { + return Init(net::APP_CACHE, disk_cache_directory, + disk_cache_size, force, callback); +} + +int AppCacheDiskCache::InitWithMemBackend( + int mem_cache_size, net::CompletionCallback* callback) { + return Init(net::MEMORY_CACHE, FilePath(), mem_cache_size, false, callback); +} + +void AppCacheDiskCache::Disable() { + if (is_disabled_) + return; + + is_disabled_ = true; + + if (create_backend_callback_) { + create_backend_callback_->Cancel(); + create_backend_callback_.release(); + OnCreateBackendComplete(net::ERR_ABORTED); + } +} + +int AppCacheDiskCache::CreateEntry(int64 key, disk_cache::Entry** entry, + net::CompletionCallback* callback) { + if (is_disabled_) + return net::ERR_ABORTED; + + if (is_initializing()) { + pending_calls_.push_back(PendingCall(CREATE, key, entry, callback)); + return net::ERR_IO_PENDING; + } + + if (!disk_cache_.get()) + return net::ERR_FAILED; + + return disk_cache_->CreateEntry(Int64ToString(key), entry, callback); +} + +int AppCacheDiskCache::OpenEntry(int64 key, disk_cache::Entry** entry, + net::CompletionCallback* callback) { + if (is_disabled_) + return net::ERR_ABORTED; + + if (is_initializing()) { + pending_calls_.push_back(PendingCall(OPEN, key, entry, callback)); + return net::ERR_IO_PENDING; + } + + if (!disk_cache_.get()) + return net::ERR_FAILED; + + return disk_cache_->OpenEntry(Int64ToString(key), entry, callback); +} + +int AppCacheDiskCache::DoomEntry(int64 key, + net::CompletionCallback* callback) { + if (is_disabled_) + return net::ERR_ABORTED; + + if (is_initializing()) { + pending_calls_.push_back(PendingCall(DOOM, key, NULL, callback)); + return net::ERR_IO_PENDING; + } + + if (!disk_cache_.get()) + return net::ERR_FAILED; + + return disk_cache_->DoomEntry(Int64ToString(key), callback); +} + +int AppCacheDiskCache::Init(net::CacheType cache_type, + const FilePath& cache_directory, + int cache_size, bool force, + net::CompletionCallback* callback) { + DCHECK(!is_initializing() && !disk_cache_.get()); + is_disabled_ = false; + create_backend_callback_ = new CreateBackendCallback( + this, &AppCacheDiskCache::OnCreateBackendComplete); + int rv = disk_cache::CreateCacheBackend( + cache_type, cache_directory, cache_size, force, + &(create_backend_callback_->backend_ptr_), create_backend_callback_); + if (rv == net::ERR_IO_PENDING) + init_callback_ = callback; + else + OnCreateBackendComplete(rv); + return rv; +} + +void AppCacheDiskCache::OnCreateBackendComplete(int rv) { + if (rv == net::OK) { + disk_cache_.reset(create_backend_callback_->backend_ptr_); + create_backend_callback_->backend_ptr_ = NULL; + } + create_backend_callback_ = NULL; + + // Invoke our clients callback function. + if (init_callback_) { + init_callback_->Run(rv); + init_callback_ = NULL; + } + + // Service pending calls that were queued up while we were initailizating. + for (PendingCalls::const_iterator iter = pending_calls_.begin(); + iter < pending_calls_.end(); ++iter) { + int rv = net::ERR_FAILED; + switch (iter->call_type) { + case CREATE: + rv = CreateEntry(iter->key, iter->entry, iter->callback); + break; + case OPEN: + rv = OpenEntry(iter->key, iter->entry, iter->callback); + break; + case DOOM: + rv = DoomEntry(iter->key, iter->callback); + break; + default: + NOTREACHED(); + break; + } + if (rv != net::ERR_IO_PENDING) + iter->callback->Run(rv); + } + pending_calls_.clear(); +} + +} // namespace appcache + diff --git a/webkit/appcache/appcache_disk_cache.h b/webkit/appcache/appcache_disk_cache.h new file mode 100644 index 0000000..cc54fd9 --- /dev/null +++ b/webkit/appcache/appcache_disk_cache.h @@ -0,0 +1,99 @@ +// Copyright (c) 2010 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. + +#ifndef WEBKIT_APPCACHE_APPCACHE_DISK_CACHE_H_ +#define WEBKIT_APPCACHE_APPCACHE_DISK_CACHE_H_ + +#include <vector> + +#include "base/scoped_ptr.h" +#include "net/disk_cache/disk_cache.h" + +namespace appcache { + +// A thin wrapper around net::DiskCache that does a couple of things for us. +// +// 1. Translates int64 keys used by the appcache into std::string +// keys used by net::DiskCache. +// 2. Allows CreateEntry, OpenEntry, and DoomEntry to be called +// immediately after construction, without waiting for the +// underlying disk_cache::Backend to be fully constructed. Early +// calls are queued up and serviced once the disk_cache::Backend is +// really ready to go. +class AppCacheDiskCache { + public: + AppCacheDiskCache(); + ~AppCacheDiskCache(); + + // Initializes the object to use disk backed storage. + int InitWithDiskBackend(const FilePath& disk_cache_directory, + int disk_cache_size, bool force, + net::CompletionCallback* callback); + + // Initializes the object to use memory only storage. + // This is used for Chrome's incognito browsing. + int InitWithMemBackend(int disk_cache_size, + net::CompletionCallback* callback); + + void Disable(); + bool is_disabled() const { return is_disabled_; } + + int CreateEntry(int64 key, disk_cache::Entry** entry, + net::CompletionCallback* callback); + int OpenEntry(int64 key, disk_cache::Entry** entry, + net::CompletionCallback* callback); + int DoomEntry(int64 key, net::CompletionCallback* callback); + + private: + class CreateBackendCallback + : public net::CancelableCompletionCallback<AppCacheDiskCache> { + public: + typedef net::CancelableCompletionCallback<AppCacheDiskCache> BaseClass; + CreateBackendCallback(AppCacheDiskCache* object, + void (AppCacheDiskCache::* method)(int)) + : BaseClass(object, method), backend_ptr_(NULL) {} + + disk_cache::Backend* backend_ptr_; // Accessed directly. + private: + ~CreateBackendCallback() { + delete backend_ptr_; + } + }; + + enum PendingCallType { + CREATE, + OPEN, + DOOM + }; + struct PendingCall { + PendingCallType call_type; + int64 key; + disk_cache::Entry** entry; + net::CompletionCallback* callback; + + PendingCall() + : call_type(CREATE), key(0), entry(NULL), callback(NULL) {} + + PendingCall(PendingCallType call_type, int64 key, + disk_cache::Entry** entry, net::CompletionCallback* callback) + : call_type(call_type), key(key), entry(entry), callback(callback) {} + }; + typedef std::vector<PendingCall> PendingCalls; + + bool is_initializing() const { return create_backend_callback_.get(); } + int Init(net::CacheType cache_type, const FilePath& directory, + int cache_size, bool force, net::CompletionCallback* callback); + void OnCreateBackendComplete(int rv); + + bool is_disabled_; + net::CompletionCallback* init_callback_; + scoped_refptr<CreateBackendCallback> create_backend_callback_; + PendingCalls pending_calls_; + scoped_ptr<disk_cache::Backend> disk_cache_; +}; + +} // namespace appcache + +#endif // WEBKIT_APPCACHE_APPCACHE_DISK_CACHE_H_ + diff --git a/webkit/appcache/appcache_response.cc b/webkit/appcache/appcache_response.cc index 68b7f09..c44eac6 100644 --- a/webkit/appcache/appcache_response.cc +++ b/webkit/appcache/appcache_response.cc @@ -11,6 +11,7 @@ #include "net/base/net_errors.h" #include "net/base/io_buffer.h" #include "net/disk_cache/disk_cache.h" +#include "webkit/appcache/appcache_disk_cache.h" #include "webkit/appcache/appcache_service.h" using disk_cache::Entry; @@ -25,11 +26,6 @@ enum { kResponseContentIndex }; -// Disk cache entry keys. -std::string response_key(int64 response_id) { - return Int64ToString(response_id); -} - // An IOBuffer that wraps a pickle's data. Ownership of the // pickle is transfered to the WrappedPickleIOBuffer object. class WrappedPickleIOBuffer : public net::WrappedIOBuffer { @@ -53,9 +49,11 @@ class WrappedPickleIOBuffer : public net::WrappedIOBuffer { AppCacheResponseInfo::AppCacheResponseInfo( AppCacheService* service, const GURL& manifest_url, - int64 response_id, net::HttpResponseInfo* http_info) + int64 response_id, net::HttpResponseInfo* http_info, + int64 response_data_size) : manifest_url_(manifest_url), response_id_(response_id), - http_response_info_(http_info), service_(service) { + http_response_info_(http_info), response_data_size_(response_data_size), + service_(service) { DCHECK(http_info); DCHECK(response_id != kNoResponseId); service_->storage()->working_set()->AddResponseInfo(this); @@ -69,9 +67,9 @@ AppCacheResponseInfo::~AppCacheResponseInfo() { // AppCacheResponseIO ---------------------------------------------- AppCacheResponseIO::AppCacheResponseIO( - int64 response_id, disk_cache::Backend* disk_cache) + int64 response_id, AppCacheDiskCache* disk_cache) : response_id_(response_id), disk_cache_(disk_cache), - entry_(NULL), user_callback_(NULL), + entry_(NULL), buffer_len_(0), user_callback_(NULL), ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)), ALLOW_THIS_IN_INITIALIZER_LIST(raw_callback_( new net::CancelableCompletionCallback<AppCacheResponseIO>( @@ -132,15 +130,31 @@ void AppCacheResponseIO::OnRawIOComplete(int result) { // AppCacheResponseReader ---------------------------------------------- +AppCacheResponseReader::AppCacheResponseReader( + int64 response_id, AppCacheDiskCache* disk_cache) + : AppCacheResponseIO(response_id, disk_cache), + range_offset_(0), range_length_(kint32max), + read_position_(0) { +} + +AppCacheResponseReader::~AppCacheResponseReader() { + if (open_callback_) + open_callback_.release()->Cancel(); +} + void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf, net::CompletionCallback* callback) { DCHECK(callback && !IsReadPending()); DCHECK(info_buf && !info_buf->http_info.get()); DCHECK(!buffer_.get() && !info_buffer_.get()); + info_buffer_ = info_buf; user_callback_ = callback; // cleared on completion + OpenEntryIfNeededAndContinue(); +} - if (!OpenEntryIfNeeded()) { +void AppCacheResponseReader::ContinueReadInfo() { + if (!entry_) { ScheduleIOCompletionCallback(net::ERR_CACHE_MISS); return; } @@ -151,7 +165,6 @@ void AppCacheResponseReader::ReadInfo(HttpResponseInfoIOBuffer* info_buf, return; } - info_buffer_ = info_buf; buffer_ = new net::IOBuffer(size); ReadRaw(kResponseInfoIndex, 0, buffer_.get(), size); } @@ -162,27 +175,25 @@ void AppCacheResponseReader::ReadData(net::IOBuffer* buf, int buf_len, DCHECK(buf && (buf_len >= 0)); DCHECK(!buffer_.get() && !info_buffer_.get()); + buffer_ = buf; + buffer_len_ = buf_len; user_callback_ = callback; // cleared on completion + OpenEntryIfNeededAndContinue(); +} - if (!OpenEntryIfNeeded()) { +void AppCacheResponseReader::ContinueReadData() { + if (!entry_) { ScheduleIOCompletionCallback(net::ERR_CACHE_MISS); return; } - buffer_ = buf; - if (read_position_ + buf_len > range_length_) { + if (read_position_ + buffer_len_ > range_length_) { // TODO(michaeln): What about integer overflows? DCHECK(range_length_ >= read_position_); - buf_len = range_length_ - read_position_; + buffer_len_ = range_length_ - read_position_; } ReadRaw(kResponseContentIndex, range_offset_ + read_position_, - buf, buf_len); -} - -int AppCacheResponseReader::GetResourceSize() { - if (!OpenEntryIfNeeded()) - return -1; - return entry_->GetDataSize(kResponseContentIndex); + buffer_, buffer_len_); } void AppCacheResponseReader::SetReadRange(int offset, int length) { @@ -200,6 +211,11 @@ void AppCacheResponseReader::OnIOComplete(int result) { info_buffer_->http_info.reset(new net::HttpResponseInfo); info_buffer_->http_info->InitFromPickle(pickle, &response_truncated); DCHECK(!response_truncated); + + // Also return the size of the response body + DCHECK(entry_); + info_buffer_->response_data_size = + entry_->GetDataSize(kResponseContentIndex); } else { read_position_ += result; } @@ -207,24 +223,67 @@ void AppCacheResponseReader::OnIOComplete(int result) { InvokeUserCompletionCallback(result); } -bool AppCacheResponseReader::OpenEntryIfNeeded() { - if (!entry_ && disk_cache_) - disk_cache_->OpenEntry(response_key(response_id_), &entry_); - return entry_ ? true : false; +void AppCacheResponseReader::OpenEntryIfNeededAndContinue() { + int rv; + if (entry_) { + rv = net::OK; + } else if (!disk_cache_) { + rv = net::ERR_FAILED; + } else { + open_callback_ = new EntryCallback<AppCacheResponseReader>( + this, &AppCacheResponseReader::OnOpenEntryComplete); + rv = disk_cache_->OpenEntry(response_id_, &open_callback_->entry_ptr_, + open_callback_.get()); + } + + if (rv != net::ERR_IO_PENDING) + OnOpenEntryComplete(rv); } +void AppCacheResponseReader::OnOpenEntryComplete(int rv) { + DCHECK(info_buffer_.get() || buffer_.get()); + + if (open_callback_) { + if (rv == net::OK) { + entry_ = open_callback_->entry_ptr_; + open_callback_->entry_ptr_ = NULL; + } + open_callback_ = NULL; + } + + if (info_buffer_) + ContinueReadInfo(); + else + ContinueReadData(); +} // AppCacheResponseWriter ---------------------------------------------- +AppCacheResponseWriter::AppCacheResponseWriter( + int64 response_id, AppCacheDiskCache* disk_cache) + : AppCacheResponseIO(response_id, disk_cache), + info_size_(0), write_position_(0), write_amount_(0), + creation_phase_(INITIAL_ATTEMPT) { +} + +AppCacheResponseWriter::~AppCacheResponseWriter() { + if (create_callback_) + create_callback_.release()->Cancel(); +} + void AppCacheResponseWriter::WriteInfo(HttpResponseInfoIOBuffer* info_buf, net::CompletionCallback* callback) { DCHECK(callback && !IsWritePending()); DCHECK(info_buf && info_buf->http_info.get()); DCHECK(!buffer_.get() && !info_buffer_.get()); + info_buffer_ = info_buf; user_callback_ = callback; // cleared on completion + CreateEntryIfNeededAndContinue(); +} - if (!CreateEntryIfNeeded()) { +void AppCacheResponseWriter::ContinueWriteInfo() { + if (!entry_) { ScheduleIOCompletionCallback(net::ERR_FAILED); return; } @@ -232,8 +291,7 @@ void AppCacheResponseWriter::WriteInfo(HttpResponseInfoIOBuffer* info_buf, const bool kSkipTransientHeaders = true; const bool kTruncated = false; Pickle* pickle = new Pickle; - info_buf->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated); - info_buffer_ = info_buf; + info_buffer_->http_info->Persist(pickle, kSkipTransientHeaders, kTruncated); write_amount_ = static_cast<int>(pickle->size()); buffer_ = new WrappedPickleIOBuffer(pickle); // takes ownership of pickle WriteRaw(kResponseInfoIndex, 0, buffer_, write_amount_); @@ -245,16 +303,18 @@ void AppCacheResponseWriter::WriteData(net::IOBuffer* buf, int buf_len, DCHECK(buf && (buf_len >= 0)); DCHECK(!buffer_.get() && !info_buffer_.get()); + buffer_ = buf; + write_amount_ = buf_len; user_callback_ = callback; // cleared on completion + CreateEntryIfNeededAndContinue(); +} - if (!CreateEntryIfNeeded()) { +void AppCacheResponseWriter::ContinueWriteData() { + if (!entry_) { ScheduleIOCompletionCallback(net::ERR_FAILED); return; } - - buffer_ = buf; - write_amount_ = buf_len; - WriteRaw(kResponseContentIndex, write_position_, buf, buf_len); + WriteRaw(kResponseContentIndex, write_position_, buffer_, write_amount_); } void AppCacheResponseWriter::OnIOComplete(int result) { @@ -268,19 +328,58 @@ void AppCacheResponseWriter::OnIOComplete(int result) { InvokeUserCompletionCallback(result); } -bool AppCacheResponseWriter::CreateEntryIfNeeded() { - if (entry_) - return true; - if (!disk_cache_) - return false; - std::string key(response_key(response_id_)); - if (!disk_cache_->CreateEntry(key, &entry_)) { - // We may try to overrite existing entries. - DCHECK(!entry_); - disk_cache_->DoomEntry(key); - disk_cache_->CreateEntry(key, &entry_); +void AppCacheResponseWriter::CreateEntryIfNeededAndContinue() { + int rv; + if (entry_) { + creation_phase_ = NO_ATTEMPT; + rv = net::OK; + } else if (!disk_cache_) { + creation_phase_ = NO_ATTEMPT; + rv = net::ERR_FAILED; + } else { + creation_phase_ = INITIAL_ATTEMPT; + create_callback_ = new EntryCallback<AppCacheResponseWriter>( + this, &AppCacheResponseWriter::OnCreateEntryComplete); + rv = disk_cache_->CreateEntry(response_id_, &create_callback_->entry_ptr_, + create_callback_.get()); } - return entry_ ? true : false; + if (rv != net::ERR_IO_PENDING) + OnCreateEntryComplete(rv); +} + +void AppCacheResponseWriter::OnCreateEntryComplete(int rv) { + DCHECK(info_buffer_.get() || buffer_.get()); + + if (creation_phase_ == INITIAL_ATTEMPT) { + if (rv != net::OK) { + // We may try to overrite existing entries. + creation_phase_ = DOOM_EXISTING; + rv = disk_cache_->DoomEntry(response_id_, create_callback_.get()); + if (rv != net::ERR_IO_PENDING) + OnCreateEntryComplete(rv); + return; + } + } else if (creation_phase_ == DOOM_EXISTING) { + creation_phase_ = SECOND_ATTEMPT; + rv = disk_cache_->CreateEntry(response_id_, &create_callback_->entry_ptr_, + create_callback_.get()); + if (rv != net::ERR_IO_PENDING) + OnCreateEntryComplete(rv); + return; + } + + if (create_callback_) { + if (rv == net::OK) { + entry_ = create_callback_->entry_ptr_; + create_callback_->entry_ptr_ = NULL; + } + create_callback_ = NULL; + } + + if (info_buffer_) + ContinueWriteInfo(); + else + ContinueWriteData(); } } // namespace appcache diff --git a/webkit/appcache/appcache_response.h b/webkit/appcache/appcache_response.h index 45f2251..1f8ecce 100644 --- a/webkit/appcache/appcache_response.h +++ b/webkit/appcache/appcache_response.h @@ -11,6 +11,7 @@ #include "base/task.h" #include "googleurl/src/gurl.h" #include "net/base/completion_callback.h" +#include "net/disk_cache/disk_cache.h" #include "net/http/http_response_info.h" #include "webkit/appcache/appcache_interfaces.h" @@ -19,13 +20,15 @@ class IOBuffer; } namespace disk_cache { class Entry; -class Backend; }; namespace appcache { +class AppCacheDiskCache; class AppCacheService; +static const int kUnkownResponseDataSize = -1; + // Response info for a particular response id. Instances are tracked in // the working set. class AppCacheResponseInfo @@ -33,14 +36,15 @@ class AppCacheResponseInfo public: // AppCacheResponseInfo takes ownership of the http_info. AppCacheResponseInfo(AppCacheService* service, const GURL& manifest_url, - int64 response_id, net::HttpResponseInfo* http_info); - // TODO(michaeln): should the ctor be hidden from public view? + int64 response_id, net::HttpResponseInfo* http_info, + int64 response_data_size); const GURL& manifest_url() const { return manifest_url_; } int64 response_id() const { return response_id_; } const net::HttpResponseInfo* http_response_info() const { return http_response_info_.get(); } + int64 response_data_size() const { return response_data_size_; } private: friend class base::RefCounted<AppCacheResponseInfo>; @@ -49,6 +53,7 @@ class AppCacheResponseInfo const GURL manifest_url_; const int64 response_id_; const scoped_ptr<net::HttpResponseInfo> http_response_info_; + const int64 response_data_size_; const AppCacheService* service_; }; @@ -57,9 +62,12 @@ class AppCacheResponseInfo struct HttpResponseInfoIOBuffer : public base::RefCountedThreadSafe<HttpResponseInfoIOBuffer> { scoped_ptr<net::HttpResponseInfo> http_info; + int response_data_size; - HttpResponseInfoIOBuffer() {} - HttpResponseInfoIOBuffer(net::HttpResponseInfo* info) : http_info(info) {} + HttpResponseInfoIOBuffer() + : response_data_size(kUnkownResponseDataSize) {} + explicit HttpResponseInfoIOBuffer(net::HttpResponseInfo* info) + : http_info(info), response_data_size(kUnkownResponseDataSize) {} private: friend class base::RefCountedThreadSafe<HttpResponseInfoIOBuffer>; @@ -76,7 +84,22 @@ class AppCacheResponseIO { protected: friend class ScopedRunnableMethodFactory<AppCacheResponseIO>; - AppCacheResponseIO(int64 response_id, disk_cache::Backend* disk_cache); + template <class T> + class EntryCallback : public net::CancelableCompletionCallback<T> { + public: + typedef net::CancelableCompletionCallback<T> BaseClass; + EntryCallback(T* object, void (T::* method)(int)) + : BaseClass(object, method), entry_ptr_(NULL) {} + + disk_cache::Entry* entry_ptr_; // Accessed directly. + private: + ~EntryCallback() { + if (entry_ptr_) + entry_ptr_->Close(); + } + }; + + AppCacheResponseIO(int64 response_id, AppCacheDiskCache* disk_cache); virtual void OnIOComplete(int result) = 0; @@ -87,10 +110,11 @@ class AppCacheResponseIO { void WriteRaw(int index, int offset, net::IOBuffer* buf, int buf_len); const int64 response_id_; - disk_cache::Backend* disk_cache_; + AppCacheDiskCache* disk_cache_; disk_cache::Entry* entry_; scoped_refptr<HttpResponseInfoIOBuffer> info_buffer_; scoped_refptr<net::IOBuffer> buffer_; + int buffer_len_; net::CompletionCallback* user_callback_; private: @@ -107,6 +131,8 @@ class AppCacheResponseIO { // operation. In other words, instances are safe to delete at will. class AppCacheResponseReader : public AppCacheResponseIO { public: + virtual ~AppCacheResponseReader(); + // Reads http info from storage. Always returns the result of the read // asynchronously through the 'callback'. Returns the number of bytes read // or a net:: error code. Guaranteed to not perform partial reads of @@ -132,10 +158,6 @@ class AppCacheResponseReader : public AppCacheResponseIO { // Returns true if there is a read operation, for data or info, pending. bool IsReadPending() { return IsIOPending(); } - // Returns the size of the resource in the disk cache or a negative value - // if there is no disk cache entry. - int GetResourceSize(); - // Used to support range requests. If not called, the reader will // read the entire response body. If called, this must be called prior // to the first call to the ReadData method. @@ -146,17 +168,18 @@ class AppCacheResponseReader : public AppCacheResponseIO { friend class MockAppCacheStorage; // Should only be constructed by the storage class. - AppCacheResponseReader(int64 response_id, disk_cache::Backend* disk_cache) - : AppCacheResponseIO(response_id, disk_cache), - range_offset_(0), range_length_(kint32max), - read_position_(0) {} + AppCacheResponseReader(int64 response_id, AppCacheDiskCache* disk_cache); virtual void OnIOComplete(int result); - bool OpenEntryIfNeeded(); + void ContinueReadInfo(); + void ContinueReadData(); + void OpenEntryIfNeededAndContinue(); + void OnOpenEntryComplete(int rv); int range_offset_; int range_length_; int read_position_; + scoped_refptr<EntryCallback<AppCacheResponseReader> > open_callback_; }; // Writes new response data to storage. If the object is deleted @@ -165,6 +188,8 @@ class AppCacheResponseReader : public AppCacheResponseIO { // operation. In other words, instances are safe to delete at will. class AppCacheResponseWriter : public AppCacheResponseIO { public: + virtual ~AppCacheResponseWriter(); + // Writes the http info to storage. Always returns the result of the write // asynchronously through the 'callback'. Returns the number of bytes written // or a net:: error code. The writer acquires a reference to the 'info_buf' @@ -196,17 +221,27 @@ class AppCacheResponseWriter : public AppCacheResponseIO { friend class AppCacheStorageImpl; friend class MockAppCacheStorage; + enum CreationPhase { + NO_ATTEMPT, + INITIAL_ATTEMPT, + DOOM_EXISTING, + SECOND_ATTEMPT + }; + // Should only be constructed by the storage class. - AppCacheResponseWriter(int64 response_id, disk_cache::Backend* disk_cache) - : AppCacheResponseIO(response_id, disk_cache), - info_size_(0), write_position_(0), write_amount_(0) {} + AppCacheResponseWriter(int64 response_id, AppCacheDiskCache* disk_cache); virtual void OnIOComplete(int result); - bool CreateEntryIfNeeded(); + void ContinueWriteInfo(); + void ContinueWriteData(); + void CreateEntryIfNeededAndContinue(); + void OnCreateEntryComplete(int rv); int info_size_; int write_position_; int write_amount_; + CreationPhase creation_phase_; + scoped_refptr<EntryCallback<AppCacheResponseWriter> > create_callback_; }; } // namespace appcache diff --git a/webkit/appcache/appcache_response_unittest.cc b/webkit/appcache/appcache_response_unittest.cc index 8954e47..d96d2e4 100644 --- a/webkit/appcache/appcache_response_unittest.cc +++ b/webkit/appcache/appcache_response_unittest.cc @@ -174,6 +174,8 @@ class AppCacheResponseTest : public testing::Test { WriteResponse(MakeHttpResponseInfo(raw_headers), body, strlen(kHttpBody)); } + int basic_response_size() { return 5; } // should match kHttpBody above + void WriteResponse(net::HttpResponseInfo* head, IOBuffer* body, int body_len) { DCHECK(body); @@ -369,6 +371,8 @@ class AppCacheResponseTest : public testing::Test { EXPECT_TRUE(CompareHttpResponseInfos( write_info_buffer_->http_info.get(), storage_delegate_->loaded_info_->http_response_info())); + EXPECT_EQ(basic_response_size(), + storage_delegate_->loaded_info_->response_data_size()); TestFinished(); } diff --git a/webkit/appcache/appcache_storage.cc b/webkit/appcache/appcache_storage.cc index 50565af..05b6269 100644 --- a/webkit/appcache/appcache_storage.cc +++ b/webkit/appcache/appcache_storage.cc @@ -50,7 +50,8 @@ void AppCacheStorage::ResponseInfoLoadTask::OnReadComplete(int result) { if (result >= 0) { info = new AppCacheResponseInfo(storage_->service(), manifest_url_, response_id_, - info_buffer_->http_info.release()); + info_buffer_->http_info.release(), + info_buffer_->response_data_size); } FOR_EACH_DELEGATE(delegates_, OnResponseInfoLoaded(info.get(), response_id_)); delete this; diff --git a/webkit/appcache/appcache_storage_impl.cc b/webkit/appcache/appcache_storage_impl.cc index 54afc9a..d700d24c 100644 --- a/webkit/appcache/appcache_storage_impl.cc +++ b/webkit/appcache/appcache_storage_impl.cc @@ -815,8 +815,12 @@ AppCacheStorageImpl::AppCacheStorageImpl(AppCacheService* service) : AppCacheStorage(service), is_incognito_(false), is_response_deletion_scheduled_(false), did_start_deleting_responses_(false), - last_deletable_response_rowid_(0), database_(NULL), - is_disabled_(false), + last_deletable_response_rowid_(0), + ALLOW_THIS_IN_INITIALIZER_LIST(doom_callback_( + this, &AppCacheStorageImpl::OnDeletedOneResponse)), + ALLOW_THIS_IN_INITIALIZER_LIST(init_callback_( + this, &AppCacheStorageImpl::OnDiskCacheInitialized)), + database_(NULL), is_disabled_(false), ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { } @@ -851,6 +855,8 @@ void AppCacheStorageImpl::Disable() { is_disabled_ = true; origins_with_groups_.clear(); working_set()->Disable(); + if (disk_cache_.get()) + disk_cache_->Disable(); scoped_refptr<DisableDatabaseTask> task = new DisableDatabaseTask(this); task->Schedule(); } @@ -1159,19 +1165,29 @@ void AppCacheStorageImpl::DeleteOneResponse() { DCHECK(is_response_deletion_scheduled_); DCHECK(!deletable_response_ids_.empty()); - is_response_deletion_scheduled_ = false; - if (!disk_cache()) { DCHECK(is_disabled_); deletable_response_ids_.clear(); deleted_response_ids_.clear(); + is_response_deletion_scheduled_ = false; return; } int64 id = deletable_response_ids_.front(); + int rv = disk_cache_->DoomEntry(id, &doom_callback_); + if (rv != net::ERR_IO_PENDING) + OnDeletedOneResponse(rv); +} + +void AppCacheStorageImpl::OnDeletedOneResponse(int rv) { + is_response_deletion_scheduled_ = false; + if (is_disabled_) + return; + + int64 id = deletable_response_ids_.front(); deletable_response_ids_.pop_front(); - disk_cache_->DoomEntry(Int64ToString(id)); - deleted_response_ids_.push_back(id); + if (rv != net::ERR_ABORTED) + deleted_response_ids_.push_back(id); const size_t kBatchSize = 50U; if (deleted_response_ids_.size() >= kBatchSize || @@ -1233,24 +1249,36 @@ void AppCacheStorageImpl::RunOnePendingSimpleTask() { delete task; } -disk_cache::Backend* AppCacheStorageImpl::disk_cache() { +AppCacheDiskCache* AppCacheStorageImpl::disk_cache() { + DCHECK(IsInitTaskComplete()); + if (is_disabled_) return NULL; if (!disk_cache_.get()) { + int rv = net::OK; + disk_cache_.reset(new AppCacheDiskCache); if (is_incognito_) { - disk_cache_.reset( - disk_cache::CreateInMemoryCacheBackend(kMaxMemDiskCacheSize)); + rv = disk_cache_->InitWithMemBackend( + kMaxMemDiskCacheSize, &init_callback_); } else { - disk_cache_.reset(disk_cache::CreateCacheBackend( + rv = disk_cache_->InitWithDiskBackend( cache_directory_.AppendASCII(kDiskCacheDirectoryName), - false, kMaxDiskCacheSize, net::DISK_CACHE)); + kMaxDiskCacheSize, false, &init_callback_); } - if (!disk_cache_.get()) - Disable(); + if (rv != net::ERR_IO_PENDING) + OnDiskCacheInitialized(rv); } return disk_cache_.get(); } +void AppCacheStorageImpl::OnDiskCacheInitialized(int rv) { + if (rv != net::OK) { + // TODO(michaeln): We're unable to open the disk cache, how + // do we recover from this error? + Disable(); + } +} + } // namespace appcache diff --git a/webkit/appcache/appcache_storage_impl.h b/webkit/appcache/appcache_storage_impl.h index 5771dd4..4fea4d4 100644 --- a/webkit/appcache/appcache_storage_impl.h +++ b/webkit/appcache/appcache_storage_impl.h @@ -12,8 +12,8 @@ #include "base/file_path.h" #include "base/task.h" -#include "net/disk_cache/disk_cache.h" #include "webkit/appcache/appcache_database.h" +#include "webkit/appcache/appcache_disk_cache.h" #include "webkit/appcache/appcache_storage.h" namespace appcache { @@ -95,6 +95,9 @@ class AppCacheStorageImpl : public AppCacheStorage { void ScheduleDeleteOneResponse(); void DeleteOneResponse(); + void OnDeletedOneResponse(int rv); + void OnDiskCacheInitialized(int rv); + // Sometimes we can respond without having to query the database. void DeliverShortCircuitedFindMainResponse( const GURL& url, AppCacheEntry found_entry, @@ -106,7 +109,7 @@ class AppCacheStorageImpl : public AppCacheStorage { const AppCacheEntry& entry, const AppCacheEntry& fallback_entry, int64 cache_id, const GURL& manifest_url); - disk_cache::Backend* disk_cache(); + AppCacheDiskCache* disk_cache(); // The directory in which we place files in the file system. FilePath cache_directory_; @@ -125,6 +128,10 @@ class AppCacheStorageImpl : public AppCacheStorage { bool did_start_deleting_responses_; int64 last_deletable_response_rowid_; + // AppCacheDiskCache async callbacks + net::CompletionCallbackImpl<AppCacheStorageImpl> doom_callback_; + net::CompletionCallbackImpl<AppCacheStorageImpl> init_callback_; + // Created on the IO thread, but only used on the DB thread. AppCacheDatabase* database_; @@ -133,7 +140,7 @@ class AppCacheStorageImpl : public AppCacheStorage { bool is_disabled_; // TODO(michaeln): use a disk_cache per group (manifest or group_id). - scoped_ptr<disk_cache::Backend> disk_cache_; + scoped_ptr<AppCacheDiskCache> disk_cache_; // Used to short-circuit certain operations without having to schedule // any tasks on the background database thread. diff --git a/webkit/appcache/appcache_storage_unittest.cc b/webkit/appcache/appcache_storage_unittest.cc index 65f3064..bdb1111 100644 --- a/webkit/appcache/appcache_storage_unittest.cc +++ b/webkit/appcache/appcache_storage_unittest.cc @@ -54,7 +54,8 @@ TEST_F(AppCacheStorageTest, AddRemoveResponseInfo) { MockAppCacheService service; scoped_refptr<AppCacheResponseInfo> info = new AppCacheResponseInfo(&service, GURL(), - 111, new net::HttpResponseInfo); + 111, new net::HttpResponseInfo, + kUnkownResponseDataSize); EXPECT_EQ(info.get(), service.storage()->working_set()->GetResponseInfo(111)); diff --git a/webkit/appcache/appcache_url_request_job.cc b/webkit/appcache/appcache_url_request_job.cc index bf52950..ea9ea9a 100644 --- a/webkit/appcache/appcache_url_request_job.cc +++ b/webkit/appcache/appcache_url_request_job.cc @@ -123,9 +123,9 @@ const net::HttpResponseInfo* AppCacheURLRequestJob::http_info() const { } void AppCacheURLRequestJob::SetupRangeResponse() { - DCHECK(is_range_request() && reader_.get() && + DCHECK(is_range_request() && info_.get() && reader_.get() && is_delivering_appcache_response()); - int resource_size = reader_->GetResourceSize(); + int resource_size = static_cast<int>(info_->response_data_size()); if (resource_size < 0 || !range_requested_.ComputeBounds(resource_size)) { range_requested_ = net::HttpByteRange(); return; diff --git a/webkit/appcache/mock_appcache_storage.h b/webkit/appcache/mock_appcache_storage.h index 10bd780..36a1c2e 100644 --- a/webkit/appcache/mock_appcache_storage.h +++ b/webkit/appcache/mock_appcache_storage.h @@ -12,8 +12,8 @@ #include "base/hash_tables.h" #include "base/scoped_ptr.h" #include "base/task.h" -#include "net/disk_cache/disk_cache.h" #include "webkit/appcache/appcache.h" +#include "webkit/appcache/appcache_disk_cache.h" #include "webkit/appcache/appcache_group.h" #include "webkit/appcache/appcache_storage.h" @@ -94,10 +94,11 @@ class MockAppCacheStorage : public AppCacheStorage { bool ShouldCacheLoadAppearAsync(const AppCache* cache); // Lazily constructed in-memory disk cache. - disk_cache::Backend* disk_cache() { + AppCacheDiskCache* disk_cache() { if (!disk_cache_.get()) { const int kMaxCacheSize = 10 * 1024 * 1024; - disk_cache_.reset(disk_cache::CreateInMemoryCacheBackend(kMaxCacheSize)); + disk_cache_.reset(new AppCacheDiskCache); + disk_cache_->InitWithMemBackend(kMaxCacheSize, NULL); } return disk_cache_.get(); } @@ -143,7 +144,7 @@ class MockAppCacheStorage : public AppCacheStorage { StoredCacheMap stored_caches_; StoredGroupMap stored_groups_; DoomedResponseIds doomed_response_ids_; - scoped_ptr<disk_cache::Backend> disk_cache_; + scoped_ptr<AppCacheDiskCache> disk_cache_; std::deque<Task*> pending_tasks_; ScopedRunnableMethodFactory<MockAppCacheStorage> method_factory_; diff --git a/webkit/appcache/webkit_appcache.gypi b/webkit/appcache/webkit_appcache.gypi index ebf8f65..e28d9d5 100644 --- a/webkit/appcache/webkit_appcache.gypi +++ b/webkit/appcache/webkit_appcache.gypi @@ -20,6 +20,8 @@ 'appcache_backend_impl.h', 'appcache_database.cc', 'appcache_database.h', + 'appcache_disk_cache.cc', + 'appcache_disk_cache.h', 'appcache_entry.h', 'appcache_frontend_impl.cc', 'appcache_frontend_impl.h', |