#include "storage/browser/blob/blob_data_handle.h"
#include "storage/browser/blob/blob_storage_context.h"
#include "storage/browser/blob/blob_url_request_job_factory.h"
+#include "storage/browser/quota/quota_manager_proxy.h"
+#include "third_party/WebKit/public/platform/WebServiceWorkerResponseType.h"
namespace content {
typedef base::Callback<void(bool)> BoolCallback;
typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
EntryBoolCallback;
-typedef base::Callback<void(scoped_ptr<ServiceWorkerRequestResponseHeaders>)>
- HeadersCallback;
+typedef base::Callback<void(scoped_ptr<ServiceWorkerCacheMetadata>)>
+ MetadataCallback;
enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
// Buffer size for cache and blob reading/writing.
const int kBufferSize = 1024 * 512;
+void NotReachedCompletionCallback(int rv) {
+ NOTREACHED();
+}
+
+blink::WebServiceWorkerResponseType ProtoResponseTypeToWebResponseType(
+ ServiceWorkerCacheResponse::ResponseType response_type) {
+ switch (response_type) {
+ case ServiceWorkerCacheResponse::BASIC_TYPE:
+ return blink::WebServiceWorkerResponseTypeBasic;
+ case ServiceWorkerCacheResponse::CORS_TYPE:
+ return blink::WebServiceWorkerResponseTypeCORS;
+ case ServiceWorkerCacheResponse::DEFAULT_TYPE:
+ return blink::WebServiceWorkerResponseTypeDefault;
+ case ServiceWorkerCacheResponse::ERROR_TYPE:
+ return blink::WebServiceWorkerResponseTypeError;
+ case ServiceWorkerCacheResponse::OPAQUE_TYPE:
+ return blink::WebServiceWorkerResponseTypeOpaque;
+ }
+ NOTREACHED();
+ return blink::WebServiceWorkerResponseTypeOpaque;
+}
+
+ServiceWorkerCacheResponse::ResponseType WebResponseTypeToProtoResponseType(
+ blink::WebServiceWorkerResponseType response_type) {
+ switch (response_type) {
+ case blink::WebServiceWorkerResponseTypeBasic:
+ return ServiceWorkerCacheResponse::BASIC_TYPE;
+ case blink::WebServiceWorkerResponseTypeCORS:
+ return ServiceWorkerCacheResponse::CORS_TYPE;
+ case blink::WebServiceWorkerResponseTypeDefault:
+ return ServiceWorkerCacheResponse::DEFAULT_TYPE;
+ case blink::WebServiceWorkerResponseTypeError:
+ return ServiceWorkerCacheResponse::ERROR_TYPE;
+ case blink::WebServiceWorkerResponseTypeOpaque:
+ return ServiceWorkerCacheResponse::OPAQUE_TYPE;
+ }
+ NOTREACHED();
+ return ServiceWorkerCacheResponse::OPAQUE_TYPE;
+}
+
struct ResponseReadContext {
ResponseReadContext(scoped_refptr<net::IOBufferWithSize> buff,
scoped_refptr<storage::BlobData> blob)
DISALLOW_COPY_AND_ASSIGN(ResponseReadContext);
};
-// Streams data from a blob and writes it to a given disk_cache::Entry.
-class BlobReader : public net::URLRequest::Delegate {
- public:
- typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
- EntryBoolCallback;
-
- BlobReader(disk_cache::ScopedEntryPtr entry)
- : cache_entry_offset_(0),
- buffer_(new net::IOBufferWithSize(kBufferSize)),
- weak_ptr_factory_(this) {
- DCHECK(entry);
- entry_ = entry.Pass();
- }
-
- void StreamBlobToCache(net::URLRequestContext* request_context,
- scoped_ptr<storage::BlobDataHandle> blob_data_handle,
- const EntryBoolCallback& callback) {
- callback_ = callback;
- blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
- blob_data_handle.Pass(), request_context, this);
- blob_request_->Start();
- }
-
- // net::URLRequest::Delegate overrides for reading blobs.
- virtual void OnReceivedRedirect(net::URLRequest* request,
- const net::RedirectInfo& redirect_info,
- bool* defer_redirect) OVERRIDE {
- NOTREACHED();
- }
- virtual void OnAuthRequired(net::URLRequest* request,
- net::AuthChallengeInfo* auth_info) OVERRIDE {
- NOTREACHED();
- }
- virtual void OnCertificateRequested(
- net::URLRequest* request,
- net::SSLCertRequestInfo* cert_request_info) OVERRIDE {
- NOTREACHED();
- }
- virtual void OnSSLCertificateError(net::URLRequest* request,
- const net::SSLInfo& ssl_info,
- bool fatal) OVERRIDE {
- NOTREACHED();
- }
- virtual void OnBeforeNetworkStart(net::URLRequest* request,
- bool* defer) OVERRIDE {
- NOTREACHED();
- }
-
- virtual void OnResponseStarted(net::URLRequest* request) OVERRIDE {
- if (!request->status().is_success()) {
- callback_.Run(entry_.Pass(), false);
- return;
- }
- ReadFromBlob();
- }
-
- virtual void ReadFromBlob() {
- int bytes_read = 0;
- bool done =
- blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
- if (done)
- OnReadCompleted(blob_request_.get(), bytes_read);
- }
-
- virtual void OnReadCompleted(net::URLRequest* request,
- int bytes_read) OVERRIDE {
- if (!request->status().is_success()) {
- callback_.Run(entry_.Pass(), false);
- return;
- }
-
- if (bytes_read == 0) {
- callback_.Run(entry_.Pass(), true);
- return;
- }
-
- net::CompletionCallback cache_write_callback =
- base::Bind(&BlobReader::DidWriteDataToEntry,
- weak_ptr_factory_.GetWeakPtr(),
- bytes_read);
-
- int rv = entry_->WriteData(INDEX_RESPONSE_BODY,
- cache_entry_offset_,
- buffer_.get(),
- bytes_read,
- cache_write_callback,
- true /* truncate */);
- if (rv != net::ERR_IO_PENDING)
- cache_write_callback.Run(rv);
- }
-
- void DidWriteDataToEntry(int expected_bytes, int rv) {
- if (rv != expected_bytes) {
- callback_.Run(entry_.Pass(), false);
- return;
- }
-
- cache_entry_offset_ += rv;
- ReadFromBlob();
- }
-
- private:
- int cache_entry_offset_;
- disk_cache::ScopedEntryPtr entry_;
- scoped_ptr<net::URLRequest> blob_request_;
- EntryBoolCallback callback_;
- scoped_refptr<net::IOBufferWithSize> buffer_;
- base::WeakPtrFactory<BlobReader> weak_ptr_factory_;
-};
-
-// Put callbacks
-void PutDidCreateEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
- scoped_ptr<ServiceWorkerResponse> response,
- const ServiceWorkerCache::ErrorCallback& callback,
- scoped_ptr<disk_cache::Entry*> entryptr,
- scoped_ptr<storage::BlobDataHandle> blob_data_handle,
- net::URLRequestContext* request_context,
- int rv);
-void PutDidWriteHeaders(scoped_ptr<ServiceWorkerResponse> response,
- const ServiceWorkerCache::ErrorCallback& callback,
- disk_cache::ScopedEntryPtr entry,
- scoped_ptr<storage::BlobDataHandle> blob_data_handle,
- net::URLRequestContext* request_context,
- int expected_bytes,
- int rv);
-void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback,
- scoped_ptr<BlobReader> blob_reader,
- disk_cache::ScopedEntryPtr entry,
- bool success);
-
// Match callbacks
void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
const ServiceWorkerCache::ResponseCallback& callback,
base::WeakPtr<storage::BlobStorageContext> blob_storage,
scoped_ptr<disk_cache::Entry*> entryptr,
int rv);
-void MatchDidReadHeaderData(
+void MatchDidReadMetadata(
scoped_ptr<ServiceWorkerFetchRequest> request,
const ServiceWorkerCache::ResponseCallback& callback,
base::WeakPtr<storage::BlobStorageContext> blob_storage,
disk_cache::ScopedEntryPtr entry,
- scoped_ptr<ServiceWorkerRequestResponseHeaders> headers);
+ scoped_ptr<ServiceWorkerCacheMetadata> headers);
void MatchDidReadResponseBodyData(
scoped_ptr<ServiceWorkerFetchRequest> request,
const ServiceWorkerCache::ResponseCallback& callback,
scoped_ptr<ResponseReadContext> response_context);
// Delete callbacks
-void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
- const ServiceWorkerCache::ErrorCallback& callback,
- scoped_ptr<disk_cache::Entry*> entryptr,
- int rv);
+void DeleteDidOpenEntry(
+ const GURL& origin,
+ scoped_ptr<ServiceWorkerFetchRequest> request,
+ const ServiceWorkerCache::ErrorCallback& callback,
+ scoped_ptr<disk_cache::Entry*> entryptr,
+ const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
+ int rv);
// Copy headers out of a cache entry and into a protobuf. The callback is
// guaranteed to be run.
-void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback);
-void ReadHeadersDidReadHeaderData(
+void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback);
+void ReadMetadataDidReadMetadata(
disk_cache::Entry* entry,
- const HeadersCallback& callback,
+ const MetadataCallback& callback,
const scoped_refptr<net::IOBufferWithSize>& buffer,
int rv);
base::WeakPtr<ServiceWorkerCache> cache,
int rv);
-void PutDidCreateEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
- scoped_ptr<ServiceWorkerResponse> response,
- const ServiceWorkerCache::ErrorCallback& callback,
- scoped_ptr<disk_cache::Entry*> entryptr,
- scoped_ptr<storage::BlobDataHandle> blob_data_handle,
- net::URLRequestContext* request_context,
- int rv) {
- if (rv != net::OK) {
- callback.Run(ServiceWorkerCache::ErrorTypeExists);
- return;
- }
-
- DCHECK(entryptr);
- disk_cache::ScopedEntryPtr entry(*entryptr);
-
- ServiceWorkerRequestResponseHeaders headers;
- headers.set_method(request->method);
-
- headers.set_status_code(response->status_code);
- headers.set_status_text(response->status_text);
- for (ServiceWorkerHeaderMap::const_iterator it = request->headers.begin();
- it != request->headers.end();
- ++it) {
- ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
- headers.add_request_headers();
- header_map->set_name(it->first);
- header_map->set_value(it->second);
- }
-
- for (ServiceWorkerHeaderMap::const_iterator it = response->headers.begin();
- it != response->headers.end();
- ++it) {
- ServiceWorkerRequestResponseHeaders::HeaderMap* header_map =
- headers.add_response_headers();
- header_map->set_name(it->first);
- header_map->set_value(it->second);
- }
-
- scoped_ptr<std::string> serialized(new std::string());
- if (!headers.SerializeToString(serialized.get())) {
- callback.Run(ServiceWorkerCache::ErrorTypeStorage);
- return;
- }
-
- scoped_refptr<net::StringIOBuffer> buffer(
- new net::StringIOBuffer(serialized.Pass()));
-
- // Get a temporary copy of the entry pointer before passing it in base::Bind.
- disk_cache::Entry* tmp_entry_ptr = entry.get();
-
- net::CompletionCallback write_headers_callback =
- base::Bind(PutDidWriteHeaders,
- base::Passed(response.Pass()),
- callback,
- base::Passed(entry.Pass()),
- base::Passed(blob_data_handle.Pass()),
- request_context,
- buffer->size());
-
- rv = tmp_entry_ptr->WriteData(INDEX_HEADERS,
- 0 /* offset */,
- buffer.get(),
- buffer->size(),
- write_headers_callback,
- true /* truncate */);
-
- if (rv != net::ERR_IO_PENDING)
- write_headers_callback.Run(rv);
-}
-
-void PutDidWriteHeaders(scoped_ptr<ServiceWorkerResponse> response,
- const ServiceWorkerCache::ErrorCallback& callback,
- disk_cache::ScopedEntryPtr entry,
- scoped_ptr<storage::BlobDataHandle> blob_data_handle,
- net::URLRequestContext* request_context,
- int expected_bytes,
- int rv) {
- if (rv != expected_bytes) {
- entry->Doom();
- callback.Run(ServiceWorkerCache::ErrorTypeStorage);
- return;
- }
-
- // The metadata is written, now for the response content. The data is streamed
- // from the blob into the cache entry.
-
- if (response->blob_uuid.empty()) {
- callback.Run(ServiceWorkerCache::ErrorTypeOK);
- return;
- }
-
- DCHECK(blob_data_handle);
-
- scoped_ptr<BlobReader> reader(new BlobReader(entry.Pass()));
- BlobReader* reader_ptr = reader.get();
-
- reader_ptr->StreamBlobToCache(
- request_context,
- blob_data_handle.Pass(),
- base::Bind(
- PutDidWriteBlobToCache, callback, base::Passed(reader.Pass())));
-}
-
-void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback& callback,
- scoped_ptr<BlobReader> blob_reader,
- disk_cache::ScopedEntryPtr entry,
- bool success) {
- if (!success) {
- entry->Doom();
- callback.Run(ServiceWorkerCache::ErrorTypeStorage);
- return;
- }
-
- callback.Run(ServiceWorkerCache::ErrorTypeOK);
-}
-
void MatchDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
const ServiceWorkerCache::ResponseCallback& callback,
base::WeakPtr<storage::BlobStorageContext> blob_storage,
// Copy the entry pointer before passing it in base::Bind.
disk_cache::Entry* tmp_entry_ptr = entry.get();
- HeadersCallback headers_callback = base::Bind(MatchDidReadHeaderData,
- base::Passed(request.Pass()),
- callback,
- blob_storage,
- base::Passed(entry.Pass()));
+ MetadataCallback headers_callback = base::Bind(MatchDidReadMetadata,
+ base::Passed(request.Pass()),
+ callback,
+ blob_storage,
+ base::Passed(entry.Pass()));
- ReadHeaders(tmp_entry_ptr, headers_callback);
+ ReadMetadata(tmp_entry_ptr, headers_callback);
}
bool VaryMatches(const ServiceWorkerHeaderMap& request,
return true;
}
-void MatchDidReadHeaderData(
+void MatchDidReadMetadata(
scoped_ptr<ServiceWorkerFetchRequest> request,
const ServiceWorkerCache::ResponseCallback& callback,
base::WeakPtr<storage::BlobStorageContext> blob_storage,
disk_cache::ScopedEntryPtr entry,
- scoped_ptr<ServiceWorkerRequestResponseHeaders> headers) {
- if (!headers) {
+ scoped_ptr<ServiceWorkerCacheMetadata> metadata) {
+ if (!metadata) {
callback.Run(ServiceWorkerCache::ErrorTypeStorage,
scoped_ptr<ServiceWorkerResponse>(),
scoped_ptr<storage::BlobDataHandle>());
return;
}
- scoped_ptr<ServiceWorkerResponse> response(
- new ServiceWorkerResponse(request->url,
- headers->status_code(),
- headers->status_text(),
- ServiceWorkerHeaderMap(),
- ""));
+ scoped_ptr<ServiceWorkerResponse> response(new ServiceWorkerResponse(
+ request->url,
+ metadata->response().status_code(),
+ metadata->response().status_text(),
+ ProtoResponseTypeToWebResponseType(metadata->response().response_type()),
+ ServiceWorkerHeaderMap(),
+ "",
+ 0));
- for (int i = 0; i < headers->response_headers_size(); ++i) {
- const ServiceWorkerRequestResponseHeaders::HeaderMap header =
- headers->response_headers(i);
+ if (metadata->response().has_url())
+ response->url = GURL(metadata->response().url());
+
+ for (int i = 0; i < metadata->response().headers_size(); ++i) {
+ const ServiceWorkerCacheHeaderMap header = metadata->response().headers(i);
response->headers.insert(std::make_pair(header.name(), header.value()));
}
ServiceWorkerHeaderMap cached_request_headers;
- for (int i = 0; i < headers->request_headers_size(); ++i) {
- const ServiceWorkerRequestResponseHeaders::HeaderMap header =
- headers->request_headers(i);
+ for (int i = 0; i < metadata->request().headers_size(); ++i) {
+ const ServiceWorkerCacheHeaderMap header = metadata->request().headers(i);
cached_request_headers[header.name()] = header.value();
}
}
if (rv == 0) {
+ response->blob_uuid = response_context->blob_data->uuid();
+ response->blob_size = response_context->total_bytes_read;
MatchDoneWithBody(request.Pass(),
callback,
blob_storage,
blob_data_handle.Pass());
}
-void DeleteDidOpenEntry(scoped_ptr<ServiceWorkerFetchRequest> request,
- const ServiceWorkerCache::ErrorCallback& callback,
- scoped_ptr<disk_cache::Entry*> entryptr,
- int rv) {
+void DeleteDidOpenEntry(
+ const GURL& origin,
+ scoped_ptr<ServiceWorkerFetchRequest> request,
+ const ServiceWorkerCache::ErrorCallback& callback,
+ scoped_ptr<disk_cache::Entry*> entryptr,
+ const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
+ int rv) {
if (rv != net::OK) {
callback.Run(ServiceWorkerCache::ErrorTypeNotFound);
return;
DCHECK(entryptr);
disk_cache::ScopedEntryPtr entry(*entryptr);
+ if (quota_manager_proxy.get()) {
+ quota_manager_proxy->NotifyStorageModified(
+ storage::QuotaClient::kServiceWorkerCache,
+ origin,
+ storage::kStorageTypeTemporary,
+ -1 * (entry->GetDataSize(INDEX_HEADERS) +
+ entry->GetDataSize(INDEX_RESPONSE_BODY)));
+ }
+
entry->Doom();
callback.Run(ServiceWorkerCache::ErrorTypeOK);
}
-void ReadHeaders(disk_cache::Entry* entry, const HeadersCallback& callback) {
+void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback) {
DCHECK(entry);
scoped_refptr<net::IOBufferWithSize> buffer(
new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
net::CompletionCallback read_header_callback =
- base::Bind(ReadHeadersDidReadHeaderData, entry, callback, buffer);
+ base::Bind(ReadMetadataDidReadMetadata, entry, callback, buffer);
int read_rv = entry->ReadData(
INDEX_HEADERS, 0, buffer.get(), buffer->size(), read_header_callback);
read_header_callback.Run(read_rv);
}
-void ReadHeadersDidReadHeaderData(
+void ReadMetadataDidReadMetadata(
disk_cache::Entry* entry,
- const HeadersCallback& callback,
+ const MetadataCallback& callback,
const scoped_refptr<net::IOBufferWithSize>& buffer,
int rv) {
if (rv != buffer->size()) {
- callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>());
+ callback.Run(scoped_ptr<ServiceWorkerCacheMetadata>());
return;
}
- scoped_ptr<ServiceWorkerRequestResponseHeaders> headers(
- new ServiceWorkerRequestResponseHeaders());
+ scoped_ptr<ServiceWorkerCacheMetadata> metadata(
+ new ServiceWorkerCacheMetadata());
- if (!headers->ParseFromArray(buffer->data(), buffer->size())) {
- callback.Run(scoped_ptr<ServiceWorkerRequestResponseHeaders>());
+ if (!metadata->ParseFromArray(buffer->data(), buffer->size())) {
+ callback.Run(scoped_ptr<ServiceWorkerCacheMetadata>());
return;
}
- callback.Run(headers.Pass());
+ callback.Run(metadata.Pass());
}
void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback& callback,
} // namespace
+// Streams data from a blob and writes it to a given disk_cache::Entry.
+class ServiceWorkerCache::BlobReader : public net::URLRequest::Delegate {
+ public:
+ typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
+ EntryAndBoolCallback;
+
+ BlobReader()
+ : cache_entry_offset_(0),
+ buffer_(new net::IOBufferWithSize(kBufferSize)),
+ weak_ptr_factory_(this) {}
+
+ // |entry| is passed to the callback once complete.
+ void StreamBlobToCache(disk_cache::ScopedEntryPtr entry,
+ net::URLRequestContext* request_context,
+ scoped_ptr<storage::BlobDataHandle> blob_data_handle,
+ const EntryAndBoolCallback& callback) {
+ DCHECK(entry);
+ entry_ = entry.Pass();
+ callback_ = callback;
+ blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
+ blob_data_handle.Pass(), request_context, this);
+ blob_request_->Start();
+ }
+
+ // net::URLRequest::Delegate overrides for reading blobs.
+ void OnReceivedRedirect(net::URLRequest* request,
+ const net::RedirectInfo& redirect_info,
+ bool* defer_redirect) override {
+ NOTREACHED();
+ }
+ void OnAuthRequired(net::URLRequest* request,
+ net::AuthChallengeInfo* auth_info) override {
+ NOTREACHED();
+ }
+ void OnCertificateRequested(
+ net::URLRequest* request,
+ net::SSLCertRequestInfo* cert_request_info) override {
+ NOTREACHED();
+ }
+ void OnSSLCertificateError(net::URLRequest* request,
+ const net::SSLInfo& ssl_info,
+ bool fatal) override {
+ NOTREACHED();
+ }
+ void OnBeforeNetworkStart(net::URLRequest* request, bool* defer) override {
+ NOTREACHED();
+ }
+
+ void OnResponseStarted(net::URLRequest* request) override {
+ if (!request->status().is_success()) {
+ callback_.Run(entry_.Pass(), false);
+ return;
+ }
+ ReadFromBlob();
+ }
+
+ virtual void ReadFromBlob() {
+ int bytes_read = 0;
+ bool done =
+ blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
+ if (done)
+ OnReadCompleted(blob_request_.get(), bytes_read);
+ }
+
+ void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
+ if (!request->status().is_success()) {
+ callback_.Run(entry_.Pass(), false);
+ return;
+ }
+
+ if (bytes_read == 0) {
+ callback_.Run(entry_.Pass(), true);
+ return;
+ }
+
+ net::CompletionCallback cache_write_callback =
+ base::Bind(&BlobReader::DidWriteDataToEntry,
+ weak_ptr_factory_.GetWeakPtr(),
+ bytes_read);
+
+ int rv = entry_->WriteData(INDEX_RESPONSE_BODY,
+ cache_entry_offset_,
+ buffer_.get(),
+ bytes_read,
+ cache_write_callback,
+ true /* truncate */);
+ if (rv != net::ERR_IO_PENDING)
+ cache_write_callback.Run(rv);
+ }
+
+ void DidWriteDataToEntry(int expected_bytes, int rv) {
+ if (rv != expected_bytes) {
+ callback_.Run(entry_.Pass(), false);
+ return;
+ }
+
+ cache_entry_offset_ += rv;
+ ReadFromBlob();
+ }
+
+ private:
+ int cache_entry_offset_;
+ disk_cache::ScopedEntryPtr entry_;
+ scoped_ptr<net::URLRequest> blob_request_;
+ EntryAndBoolCallback callback_;
+ scoped_refptr<net::IOBufferWithSize> buffer_;
+ base::WeakPtrFactory<BlobReader> weak_ptr_factory_;
+};
+
// The state needed to pass between ServiceWorkerCache::Keys callbacks.
struct ServiceWorkerCache::KeysContext {
KeysContext(const ServiceWorkerCache::RequestsCallback& callback,
// Used for enumerating cache entries.
scoped_ptr<disk_cache::Backend::Iterator> backend_iterator;
disk_cache::Entry* enumerated_entry;
+
+ DISALLOW_COPY_AND_ASSIGN(KeysContext);
+};
+
+// The state needed to pass between ServiceWorkerCache::Put callbacks.
+struct ServiceWorkerCache::PutContext {
+ PutContext(
+ const GURL& origin,
+ scoped_ptr<ServiceWorkerFetchRequest> request,
+ scoped_ptr<ServiceWorkerResponse> response,
+ scoped_ptr<storage::BlobDataHandle> blob_data_handle,
+ const ServiceWorkerCache::ResponseCallback& callback,
+ base::WeakPtr<ServiceWorkerCache> cache,
+ net::URLRequestContext* request_context,
+ const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy)
+ : origin(origin),
+ request(request.Pass()),
+ response(response.Pass()),
+ blob_data_handle(blob_data_handle.Pass()),
+ callback(callback),
+ cache(cache),
+ request_context(request_context),
+ quota_manager_proxy(quota_manager_proxy),
+ cache_entry(NULL) {}
+ ~PutContext() {
+ if (cache_entry)
+ cache_entry->Close();
+ }
+
+ // Input parameters to the Put function.
+ GURL origin;
+ scoped_ptr<ServiceWorkerFetchRequest> request;
+ scoped_ptr<ServiceWorkerResponse> response;
+ scoped_ptr<storage::BlobDataHandle> blob_data_handle;
+ ServiceWorkerCache::ResponseCallback callback;
+ base::WeakPtr<ServiceWorkerCache> cache;
+ net::URLRequestContext* request_context;
+ scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy;
+
+ // This isn't a scoped_ptr because the disk_cache needs an Entry** as input to
+ // CreateEntry.
+ disk_cache::Entry* cache_entry;
+
+ // The BlobDataHandle for the output ServiceWorkerResponse.
+ scoped_ptr<storage::BlobDataHandle> out_blob_data_handle;
+
+ DISALLOW_COPY_AND_ASSIGN(PutContext);
};
// static
scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreateMemoryCache(
+ const GURL& origin,
net::URLRequestContext* request_context,
+ const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
base::WeakPtr<storage::BlobStorageContext> blob_context) {
- return make_scoped_refptr(
- new ServiceWorkerCache(base::FilePath(), request_context, blob_context));
+ return make_scoped_refptr(new ServiceWorkerCache(origin,
+ base::FilePath(),
+ request_context,
+ quota_manager_proxy,
+ blob_context));
}
// static
scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreatePersistentCache(
+ const GURL& origin,
const base::FilePath& path,
net::URLRequestContext* request_context,
+ const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
base::WeakPtr<storage::BlobStorageContext> blob_context) {
- return make_scoped_refptr(
- new ServiceWorkerCache(path, request_context, blob_context));
+ return make_scoped_refptr(new ServiceWorkerCache(
+ origin, path, request_context, quota_manager_proxy, blob_context));
}
ServiceWorkerCache::~ServiceWorkerCache() {
void ServiceWorkerCache::Put(scoped_ptr<ServiceWorkerFetchRequest> request,
scoped_ptr<ServiceWorkerResponse> response,
- const ErrorCallback& callback) {
+ const ResponseCallback& callback) {
+ IncPendingOps();
+ ResponseCallback pending_callback =
+ base::Bind(&ServiceWorkerCache::PendingResponseCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback);
scoped_ptr<storage::BlobDataHandle> blob_data_handle;
if (!response->blob_uuid.empty()) {
if (!blob_storage_context_) {
- callback.Run(ErrorTypeStorage);
+ pending_callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
return;
}
blob_data_handle =
blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
if (!blob_data_handle) {
- callback.Run(ErrorTypeStorage);
+ pending_callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
return;
}
}
+ scoped_ptr<PutContext> put_context(new PutContext(
+ origin_, request.Pass(), response.Pass(), blob_data_handle.Pass(),
+ pending_callback, weak_ptr_factory_.GetWeakPtr(), request_context_,
+ quota_manager_proxy_));
+
+ if (put_context->blob_data_handle) {
+ // Grab another handle to the blob for the callback response.
+ put_context->out_blob_data_handle =
+ blob_storage_context_->GetBlobDataFromUUID(
+ put_context->response->blob_uuid);
+ }
+
base::Closure continuation = base::Bind(&ServiceWorkerCache::PutImpl,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(request.Pass()),
- base::Passed(response.Pass()),
- base::Passed(blob_data_handle.Pass()),
- callback);
+ base::Passed(put_context.Pass()));
if (!initialized_) {
Init(continuation);
void ServiceWorkerCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request,
const ResponseCallback& callback) {
+ IncPendingOps();
+ ResponseCallback pending_callback =
+ base::Bind(&ServiceWorkerCache::PendingResponseCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+
if (!initialized_) {
- Init(base::Bind(&ServiceWorkerCache::Match,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(request.Pass()),
- callback));
+ Init(base::Bind(&ServiceWorkerCache::Match, weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(request.Pass()), pending_callback));
return;
}
if (!backend_) {
- callback.Run(ErrorTypeStorage,
- scoped_ptr<ServiceWorkerResponse>(),
- scoped_ptr<storage::BlobDataHandle>());
+ pending_callback.Run(ErrorTypeStorage, scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
return;
}
ServiceWorkerFetchRequest* request_ptr = request.get();
- net::CompletionCallback open_entry_callback =
- base::Bind(MatchDidOpenEntry,
- base::Passed(request.Pass()),
- callback,
- blob_storage_context_,
- base::Passed(entry.Pass()));
+ net::CompletionCallback open_entry_callback = base::Bind(
+ MatchDidOpenEntry, base::Passed(request.Pass()), pending_callback,
+ blob_storage_context_, base::Passed(entry.Pass()));
int rv = backend_->OpenEntry(
request_ptr->url.spec(), entry_ptr, open_entry_callback);
void ServiceWorkerCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request,
const ErrorCallback& callback) {
+ IncPendingOps();
+ ErrorCallback pending_callback =
+ base::Bind(&ServiceWorkerCache::PendingErrorCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback);
+
if (!initialized_) {
- Init(base::Bind(&ServiceWorkerCache::Delete,
- weak_ptr_factory_.GetWeakPtr(),
- base::Passed(request.Pass()),
- callback));
+ Init(base::Bind(&ServiceWorkerCache::Delete, weak_ptr_factory_.GetWeakPtr(),
+ base::Passed(request.Pass()), pending_callback));
return;
}
if (!backend_) {
- callback.Run(ErrorTypeStorage);
+ pending_callback.Run(ErrorTypeStorage);
return;
}
ServiceWorkerFetchRequest* request_ptr = request.get();
- net::CompletionCallback open_entry_callback =
- base::Bind(DeleteDidOpenEntry,
- base::Passed(request.Pass()),
- callback,
- base::Passed(entry.Pass()));
+ net::CompletionCallback open_entry_callback = base::Bind(
+ DeleteDidOpenEntry, origin_, base::Passed(request.Pass()),
+ pending_callback, base::Passed(entry.Pass()), quota_manager_proxy_);
int rv = backend_->OpenEntry(
request_ptr->url.spec(), entry_ptr, open_entry_callback);
}
void ServiceWorkerCache::Keys(const RequestsCallback& callback) {
+ IncPendingOps();
+ RequestsCallback pending_callback =
+ base::Bind(&ServiceWorkerCache::PendingRequestsCallback,
+ weak_ptr_factory_.GetWeakPtr(), callback);
if (!initialized_) {
- Init(base::Bind(
- &ServiceWorkerCache::Keys, weak_ptr_factory_.GetWeakPtr(), callback));
+ Init(base::Bind(&ServiceWorkerCache::Keys, weak_ptr_factory_.GetWeakPtr(),
+ pending_callback));
return;
}
if (!backend_) {
- callback.Run(ErrorTypeStorage, scoped_ptr<Requests>());
+ pending_callback.Run(ErrorTypeStorage, scoped_ptr<Requests>());
return;
}
// forever if you read data from a cache entry while enumerating.
scoped_ptr<KeysContext> keys_context(
- new KeysContext(callback, weak_ptr_factory_.GetWeakPtr()));
+ new KeysContext(pending_callback, weak_ptr_factory_.GetWeakPtr()));
keys_context->backend_iterator = backend_->CreateIterator();
disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
open_entry_callback.Run(rv);
}
-void ServiceWorkerCache::Close() {
+void ServiceWorkerCache::Close(const base::Closure& callback) {
+ DCHECK(!initialized_ || backend_)
+ << "Don't call ServiceWorkerCache::Close() twice.";
+
+ if (pending_ops_ > 0) {
+ DCHECK(ops_complete_callback_.is_null());
+ initialized_ = true; // So that future operations halt.
+ ops_complete_callback_ = base::Bind(
+ &ServiceWorkerCache::Close, weak_ptr_factory_.GetWeakPtr(), callback);
+ return;
+ }
+
+ initialized_ = true;
backend_.reset();
+ callback.Run();
+}
+
+int64 ServiceWorkerCache::MemoryBackedSize() const {
+ if (!backend_ || !memory_only_)
+ return 0;
+
+ scoped_ptr<disk_cache::Backend::Iterator> backend_iter =
+ backend_->CreateIterator();
+ disk_cache::Entry* entry = nullptr;
+
+ int64 sum = 0;
+
+ std::vector<disk_cache::Entry*> entries;
+ int rv = net::OK;
+ while ((rv = backend_iter->OpenNextEntry(
+ &entry, base::Bind(NotReachedCompletionCallback))) == net::OK) {
+ entries.push_back(entry); // Open the entries without mutating them.
+ }
+ DCHECK(rv !=
+ net::ERR_IO_PENDING); // Expect all memory ops to be synchronous.
+
+ for (disk_cache::Entry* entry : entries) {
+ sum += entry->GetDataSize(INDEX_HEADERS) +
+ entry->GetDataSize(INDEX_RESPONSE_BODY);
+ entry->Close();
+ }
+
+ return sum;
}
ServiceWorkerCache::ServiceWorkerCache(
+ const GURL& origin,
const base::FilePath& path,
net::URLRequestContext* request_context,
+ const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
base::WeakPtr<storage::BlobStorageContext> blob_context)
- : path_(path),
+ : origin_(origin),
+ path_(path),
request_context_(request_context),
+ quota_manager_proxy_(quota_manager_proxy),
blob_storage_context_(blob_context),
initialized_(false),
+ memory_only_(path.empty()),
+ pending_ops_(0),
weak_ptr_factory_(this) {
}
-void ServiceWorkerCache::PutImpl(
- scoped_ptr<ServiceWorkerFetchRequest> request,
- scoped_ptr<ServiceWorkerResponse> response,
- scoped_ptr<storage::BlobDataHandle> blob_data_handle,
- const ErrorCallback& callback) {
- if (!backend_) {
- callback.Run(ErrorTypeStorage);
+// static
+void ServiceWorkerCache::PutImpl(scoped_ptr<PutContext> put_context) {
+ if (!put_context->cache || !put_context->cache->backend_) {
+ put_context->callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
return;
}
- scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
+ scoped_ptr<ServiceWorkerFetchRequest> request_copy(
+ new ServiceWorkerFetchRequest(*put_context->request));
+ ServiceWorkerCache* cache_ptr = put_context->cache.get();
- disk_cache::Entry** entry_ptr = entry.get();
+ cache_ptr->Delete(request_copy.Pass(),
+ base::Bind(PutDidDelete, base::Passed(put_context.Pass())));
+}
- ServiceWorkerFetchRequest* request_ptr = request.get();
+// static
+void ServiceWorkerCache::PutDidDelete(scoped_ptr<PutContext> put_context,
+ ErrorType delete_error) {
+ if (!put_context->cache || !put_context->cache->backend_) {
+ put_context->callback.Run(ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
+ return;
+ }
+
+ disk_cache::Entry** entry_ptr = &put_context->cache_entry;
+ ServiceWorkerFetchRequest* request_ptr = put_context->request.get();
+ disk_cache::Backend* backend_ptr = put_context->cache->backend_.get();
net::CompletionCallback create_entry_callback =
- base::Bind(PutDidCreateEntry,
- base::Passed(request.Pass()),
- base::Passed(response.Pass()),
- callback,
- base::Passed(entry.Pass()),
- base::Passed(blob_data_handle.Pass()),
- request_context_);
+ base::Bind(PutDidCreateEntry, base::Passed(put_context.Pass()));
- int rv = backend_->CreateEntry(
+ int create_rv = backend_ptr->CreateEntry(
request_ptr->url.spec(), entry_ptr, create_entry_callback);
+ if (create_rv != net::ERR_IO_PENDING)
+ create_entry_callback.Run(create_rv);
+}
+
+// static
+void ServiceWorkerCache::PutDidCreateEntry(scoped_ptr<PutContext> put_context,
+ int rv) {
+ if (rv != net::OK) {
+ put_context->callback.Run(ServiceWorkerCache::ErrorTypeExists,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
+ return;
+ }
+
+ DCHECK(put_context->cache_entry);
+
+ ServiceWorkerCacheMetadata metadata;
+ ServiceWorkerCacheRequest* request_metadata = metadata.mutable_request();
+ request_metadata->set_method(put_context->request->method);
+ for (ServiceWorkerHeaderMap::const_iterator it =
+ put_context->request->headers.begin();
+ it != put_context->request->headers.end();
+ ++it) {
+ ServiceWorkerCacheHeaderMap* header_map = request_metadata->add_headers();
+ header_map->set_name(it->first);
+ header_map->set_value(it->second);
+ }
+
+ ServiceWorkerCacheResponse* response_metadata = metadata.mutable_response();
+ response_metadata->set_status_code(put_context->response->status_code);
+ response_metadata->set_status_text(put_context->response->status_text);
+ response_metadata->set_response_type(
+ WebResponseTypeToProtoResponseType(put_context->response->response_type));
+ response_metadata->set_url(put_context->response->url.spec());
+ for (ServiceWorkerHeaderMap::const_iterator it =
+ put_context->response->headers.begin();
+ it != put_context->response->headers.end();
+ ++it) {
+ ServiceWorkerCacheHeaderMap* header_map = response_metadata->add_headers();
+ header_map->set_name(it->first);
+ header_map->set_value(it->second);
+ }
+
+ scoped_ptr<std::string> serialized(new std::string());
+ if (!metadata.SerializeToString(serialized.get())) {
+ put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
+ return;
+ }
+
+ scoped_refptr<net::StringIOBuffer> buffer(
+ new net::StringIOBuffer(serialized.Pass()));
+
+ // Get a temporary copy of the entry pointer before passing it in base::Bind.
+ disk_cache::Entry* tmp_entry_ptr = put_context->cache_entry;
+
+ net::CompletionCallback write_headers_callback = base::Bind(
+ PutDidWriteHeaders, base::Passed(put_context.Pass()), buffer->size());
+
+ rv = tmp_entry_ptr->WriteData(INDEX_HEADERS,
+ 0 /* offset */,
+ buffer.get(),
+ buffer->size(),
+ write_headers_callback,
+ true /* truncate */);
+
if (rv != net::ERR_IO_PENDING)
- create_entry_callback.Run(rv);
+ write_headers_callback.Run(rv);
+}
+
+// static
+void ServiceWorkerCache::PutDidWriteHeaders(scoped_ptr<PutContext> put_context,
+ int expected_bytes,
+ int rv) {
+ if (rv != expected_bytes) {
+ put_context->cache_entry->Doom();
+ put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
+ return;
+ }
+
+ // The metadata is written, now for the response content. The data is streamed
+ // from the blob into the cache entry.
+
+ if (put_context->response->blob_uuid.empty()) {
+ if (put_context->quota_manager_proxy.get()) {
+ put_context->quota_manager_proxy->NotifyStorageModified(
+ storage::QuotaClient::kServiceWorkerCache,
+ put_context->origin,
+ storage::kStorageTypeTemporary,
+ put_context->cache_entry->GetDataSize(INDEX_HEADERS));
+ }
+
+ put_context->callback.Run(ServiceWorkerCache::ErrorTypeOK,
+ put_context->response.Pass(),
+ scoped_ptr<storage::BlobDataHandle>());
+ return;
+ }
+
+ DCHECK(put_context->blob_data_handle);
+
+ disk_cache::ScopedEntryPtr entry(put_context->cache_entry);
+ put_context->cache_entry = NULL;
+ scoped_ptr<BlobReader> reader(new BlobReader());
+ BlobReader* reader_ptr = reader.get();
+
+ // Grab some pointers before passing put_context in Bind.
+ net::URLRequestContext* request_context = put_context->request_context;
+ scoped_ptr<storage::BlobDataHandle> blob_data_handle =
+ put_context->blob_data_handle.Pass();
+
+ reader_ptr->StreamBlobToCache(entry.Pass(),
+ request_context,
+ blob_data_handle.Pass(),
+ base::Bind(PutDidWriteBlobToCache,
+ base::Passed(put_context.Pass()),
+ base::Passed(reader.Pass())));
+}
+
+// static
+void ServiceWorkerCache::PutDidWriteBlobToCache(
+ scoped_ptr<PutContext> put_context,
+ scoped_ptr<BlobReader> blob_reader,
+ disk_cache::ScopedEntryPtr entry,
+ bool success) {
+ DCHECK(entry);
+ put_context->cache_entry = entry.release();
+
+ if (!success) {
+ put_context->cache_entry->Doom();
+ put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
+ scoped_ptr<ServiceWorkerResponse>(),
+ scoped_ptr<storage::BlobDataHandle>());
+ return;
+ }
+
+ if (put_context->quota_manager_proxy.get()) {
+ put_context->quota_manager_proxy->NotifyStorageModified(
+ storage::QuotaClient::kServiceWorkerCache,
+ put_context->origin,
+ storage::kStorageTypeTemporary,
+ put_context->cache_entry->GetDataSize(INDEX_HEADERS) +
+ put_context->cache_entry->GetDataSize(INDEX_RESPONSE_BODY));
+ }
+
+ put_context->callback.Run(ServiceWorkerCache::ErrorTypeOK,
+ put_context->response.Pass(),
+ put_context->out_blob_data_handle.Pass());
}
// static
return;
}
- ReadHeaders(
+ ReadMetadata(
*iter,
- base::Bind(KeysDidReadHeaders, base::Passed(keys_context.Pass()), iter));
+ base::Bind(KeysDidReadMetadata, base::Passed(keys_context.Pass()), iter));
}
// static
-void ServiceWorkerCache::KeysDidReadHeaders(
+void ServiceWorkerCache::KeysDidReadMetadata(
scoped_ptr<KeysContext> keys_context,
const Entries::iterator& iter,
- scoped_ptr<ServiceWorkerRequestResponseHeaders> headers) {
+ scoped_ptr<ServiceWorkerCacheMetadata> metadata) {
disk_cache::Entry* entry = *iter;
- if (headers) {
+ if (metadata) {
keys_context->out_keys->push_back(
ServiceWorkerFetchRequest(GURL(entry->GetKey()),
- headers->method(),
+ metadata->request().method(),
ServiceWorkerHeaderMap(),
GURL(),
false));
ServiceWorkerHeaderMap& req_headers =
keys_context->out_keys->back().headers;
- for (int i = 0; i < headers->request_headers_size(); ++i) {
- const ServiceWorkerRequestResponseHeaders::HeaderMap header =
- headers->request_headers(i);
+ for (int i = 0; i < metadata->request().headers_size(); ++i) {
+ const ServiceWorkerCacheHeaderMap header = metadata->request().headers(i);
req_headers.insert(std::make_pair(header.name(), header.value()));
}
} else {
DCHECK(!backend_);
// Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
- net::CacheType cache_type =
- path_.empty() ? net::MEMORY_CACHE : net::APP_CACHE;
+ net::CacheType cache_type = memory_only_ ? net::MEMORY_CACHE : net::APP_CACHE;
scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr());
}
void ServiceWorkerCache::Init(const base::Closure& callback) {
+ DCHECK(!initialized_);
init_callbacks_.push_back(callback);
// If this isn't the first call to Init then return as the initialization
init_callbacks_.clear();
}
+void ServiceWorkerCache::DecPendingOps() {
+ DCHECK(pending_ops_ > 0);
+ pending_ops_--;
+ if (pending_ops_ == 0 && !ops_complete_callback_.is_null()) {
+ ops_complete_callback_.Run();
+ ops_complete_callback_.Reset();
+ }
+}
+
+void ServiceWorkerCache::PendingErrorCallback(const ErrorCallback& callback,
+ ErrorType error) {
+ callback.Run(error);
+ DecPendingOps();
+}
+
+void ServiceWorkerCache::PendingResponseCallback(
+ const ResponseCallback& callback,
+ ErrorType error,
+ scoped_ptr<ServiceWorkerResponse> response,
+ scoped_ptr<storage::BlobDataHandle> blob_data_handle) {
+ callback.Run(error, response.Pass(), blob_data_handle.Pass());
+ DecPendingOps();
+}
+
+void ServiceWorkerCache::PendingRequestsCallback(
+ const RequestsCallback& callback,
+ ErrorType error,
+ scoped_ptr<Requests> requests) {
+ callback.Run(error, requests.Pass());
+ DecPendingOps();
+}
+
} // namespace content