summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--net/disk_cache/backend_impl.cc10
-rw-r--r--net/disk_cache/histogram_macros.h3
-rw-r--r--webkit/appcache/appcache_database.cc5
-rw-r--r--webkit/appcache/appcache_disk_cache.cc155
-rw-r--r--webkit/appcache/appcache_disk_cache.h99
-rw-r--r--webkit/appcache/appcache_response.cc191
-rw-r--r--webkit/appcache/appcache_response.h75
-rw-r--r--webkit/appcache/appcache_response_unittest.cc4
-rw-r--r--webkit/appcache/appcache_storage.cc3
-rw-r--r--webkit/appcache/appcache_storage_impl.cc54
-rw-r--r--webkit/appcache/appcache_storage_impl.h13
-rw-r--r--webkit/appcache/appcache_storage_unittest.cc3
-rw-r--r--webkit/appcache/appcache_url_request_job.cc4
-rw-r--r--webkit/appcache/mock_appcache_storage.h9
-rw-r--r--webkit/appcache/webkit_appcache.gypi2
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',