Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / content / browser / service_worker / service_worker_cache.cc
blob5283a08b776ea81e1700c6d2ee552c8360f9ed4f
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_cache.h"
7 #include <string>
9 #include "base/files/file_path.h"
10 #include "base/guid.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/profiler/scoped_tracker.h"
13 #include "base/strings/string_util.h"
14 #include "content/browser/service_worker/service_worker_cache.pb.h"
15 #include "content/public/browser/browser_thread.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/disk_cache/disk_cache.h"
19 #include "net/url_request/url_request_context.h"
20 #include "storage/browser/blob/blob_data_handle.h"
21 #include "storage/browser/blob/blob_storage_context.h"
22 #include "storage/browser/blob/blob_url_request_job_factory.h"
23 #include "storage/browser/quota/quota_manager_proxy.h"
24 #include "third_party/WebKit/public/platform/WebServiceWorkerResponseType.h"
26 namespace content {
28 namespace {
30 typedef base::Callback<void(bool)> BoolCallback;
31 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
32 EntryBoolCallback;
33 typedef base::Callback<void(scoped_ptr<ServiceWorkerCacheMetadata>)>
34 MetadataCallback;
36 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
38 // The maximum size of an individual cache. Ultimately cache size is controlled
39 // per-origin.
40 const int kMaxCacheBytes = 512 * 1024 * 1024;
42 // Buffer size for cache and blob reading/writing.
43 const int kBufferSize = 1024 * 512;
45 void NotReachedCompletionCallback(int rv) {
46 NOTREACHED();
49 blink::WebServiceWorkerResponseType ProtoResponseTypeToWebResponseType(
50 ServiceWorkerCacheResponse::ResponseType response_type) {
51 switch (response_type) {
52 case ServiceWorkerCacheResponse::BASIC_TYPE:
53 return blink::WebServiceWorkerResponseTypeBasic;
54 case ServiceWorkerCacheResponse::CORS_TYPE:
55 return blink::WebServiceWorkerResponseTypeCORS;
56 case ServiceWorkerCacheResponse::DEFAULT_TYPE:
57 return blink::WebServiceWorkerResponseTypeDefault;
58 case ServiceWorkerCacheResponse::ERROR_TYPE:
59 return blink::WebServiceWorkerResponseTypeError;
60 case ServiceWorkerCacheResponse::OPAQUE_TYPE:
61 return blink::WebServiceWorkerResponseTypeOpaque;
63 NOTREACHED();
64 return blink::WebServiceWorkerResponseTypeOpaque;
67 ServiceWorkerCacheResponse::ResponseType WebResponseTypeToProtoResponseType(
68 blink::WebServiceWorkerResponseType response_type) {
69 switch (response_type) {
70 case blink::WebServiceWorkerResponseTypeBasic:
71 return ServiceWorkerCacheResponse::BASIC_TYPE;
72 case blink::WebServiceWorkerResponseTypeCORS:
73 return ServiceWorkerCacheResponse::CORS_TYPE;
74 case blink::WebServiceWorkerResponseTypeDefault:
75 return ServiceWorkerCacheResponse::DEFAULT_TYPE;
76 case blink::WebServiceWorkerResponseTypeError:
77 return ServiceWorkerCacheResponse::ERROR_TYPE;
78 case blink::WebServiceWorkerResponseTypeOpaque:
79 return ServiceWorkerCacheResponse::OPAQUE_TYPE;
81 NOTREACHED();
82 return ServiceWorkerCacheResponse::OPAQUE_TYPE;
85 // Copy headers out of a cache entry and into a protobuf. The callback is
86 // guaranteed to be run.
87 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback);
88 void ReadMetadataDidReadMetadata(
89 disk_cache::Entry* entry,
90 const MetadataCallback& callback,
91 const scoped_refptr<net::IOBufferWithSize>& buffer,
92 int rv);
95 bool VaryMatches(const ServiceWorkerHeaderMap& request,
96 const ServiceWorkerHeaderMap& cached_request,
97 const ServiceWorkerHeaderMap& response) {
98 ServiceWorkerHeaderMap::const_iterator vary_iter = response.find("vary");
99 if (vary_iter == response.end())
100 return true;
102 std::vector<std::string> vary_keys;
103 Tokenize(vary_iter->second, ",", &vary_keys);
104 for (std::vector<std::string>::const_iterator it = vary_keys.begin();
105 it != vary_keys.end();
106 ++it) {
107 std::string trimmed;
108 base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &trimmed);
109 if (trimmed == "*")
110 return false;
112 ServiceWorkerHeaderMap::const_iterator request_iter = request.find(trimmed);
113 ServiceWorkerHeaderMap::const_iterator cached_request_iter =
114 cached_request.find(trimmed);
116 // If the header exists in one but not the other, no match.
117 if ((request_iter == request.end()) !=
118 (cached_request_iter == cached_request.end()))
119 return false;
121 // If the header exists in one, it exists in both. Verify that the values
122 // are equal.
123 if (request_iter != request.end() &&
124 request_iter->second != cached_request_iter->second)
125 return false;
128 return true;
132 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback) {
133 DCHECK(entry);
135 scoped_refptr<net::IOBufferWithSize> buffer(
136 new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
138 net::CompletionCallback read_header_callback =
139 base::Bind(ReadMetadataDidReadMetadata, entry, callback, buffer);
141 int read_rv = entry->ReadData(
142 INDEX_HEADERS, 0, buffer.get(), buffer->size(), read_header_callback);
144 if (read_rv != net::ERR_IO_PENDING)
145 read_header_callback.Run(read_rv);
148 void ReadMetadataDidReadMetadata(
149 disk_cache::Entry* entry,
150 const MetadataCallback& callback,
151 const scoped_refptr<net::IOBufferWithSize>& buffer,
152 int rv) {
153 if (rv != buffer->size()) {
154 callback.Run(scoped_ptr<ServiceWorkerCacheMetadata>());
155 return;
158 scoped_ptr<ServiceWorkerCacheMetadata> metadata(
159 new ServiceWorkerCacheMetadata());
161 if (!metadata->ParseFromArray(buffer->data(), buffer->size())) {
162 callback.Run(scoped_ptr<ServiceWorkerCacheMetadata>());
163 return;
166 callback.Run(metadata.Pass());
169 } // namespace
171 // Streams data from a blob and writes it to a given disk_cache::Entry.
172 class ServiceWorkerCache::BlobReader : public net::URLRequest::Delegate {
173 public:
174 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
175 EntryAndBoolCallback;
177 BlobReader()
178 : cache_entry_offset_(0),
179 buffer_(new net::IOBufferWithSize(kBufferSize)),
180 weak_ptr_factory_(this) {}
182 // |entry| is passed to the callback once complete.
183 void StreamBlobToCache(disk_cache::ScopedEntryPtr entry,
184 net::URLRequestContext* request_context,
185 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
186 const EntryAndBoolCallback& callback) {
187 DCHECK(entry);
188 entry_ = entry.Pass();
189 callback_ = callback;
190 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
191 blob_data_handle.Pass(), request_context, this);
192 blob_request_->Start();
195 // net::URLRequest::Delegate overrides for reading blobs.
196 void OnReceivedRedirect(net::URLRequest* request,
197 const net::RedirectInfo& redirect_info,
198 bool* defer_redirect) override {
199 NOTREACHED();
201 void OnAuthRequired(net::URLRequest* request,
202 net::AuthChallengeInfo* auth_info) override {
203 NOTREACHED();
205 void OnCertificateRequested(
206 net::URLRequest* request,
207 net::SSLCertRequestInfo* cert_request_info) override {
208 NOTREACHED();
210 void OnSSLCertificateError(net::URLRequest* request,
211 const net::SSLInfo& ssl_info,
212 bool fatal) override {
213 NOTREACHED();
215 void OnBeforeNetworkStart(net::URLRequest* request, bool* defer) override {
216 NOTREACHED();
219 void OnResponseStarted(net::URLRequest* request) override {
220 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
221 tracked_objects::ScopedTracker tracking_profile(
222 FROM_HERE_WITH_EXPLICIT_FUNCTION(
223 "423948 ServiceWorkerCache::BlobReader::OnResponseStarted"));
225 if (!request->status().is_success()) {
226 callback_.Run(entry_.Pass(), false);
227 return;
229 ReadFromBlob();
232 virtual void ReadFromBlob() {
233 int bytes_read = 0;
234 bool done =
235 blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
236 if (done)
237 OnReadCompleted(blob_request_.get(), bytes_read);
240 void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
241 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
242 tracked_objects::ScopedTracker tracking_profile(
243 FROM_HERE_WITH_EXPLICIT_FUNCTION(
244 "423948 ServiceWorkerCache::BlobReader::OnReadCompleted"));
246 if (!request->status().is_success()) {
247 callback_.Run(entry_.Pass(), false);
248 return;
251 if (bytes_read == 0) {
252 callback_.Run(entry_.Pass(), true);
253 return;
256 net::CompletionCallback cache_write_callback =
257 base::Bind(&BlobReader::DidWriteDataToEntry,
258 weak_ptr_factory_.GetWeakPtr(),
259 bytes_read);
261 int rv = entry_->WriteData(INDEX_RESPONSE_BODY,
262 cache_entry_offset_,
263 buffer_.get(),
264 bytes_read,
265 cache_write_callback,
266 true /* truncate */);
267 if (rv != net::ERR_IO_PENDING)
268 cache_write_callback.Run(rv);
271 void DidWriteDataToEntry(int expected_bytes, int rv) {
272 if (rv != expected_bytes) {
273 callback_.Run(entry_.Pass(), false);
274 return;
277 cache_entry_offset_ += rv;
278 ReadFromBlob();
281 private:
282 int cache_entry_offset_;
283 disk_cache::ScopedEntryPtr entry_;
284 scoped_ptr<net::URLRequest> blob_request_;
285 EntryAndBoolCallback callback_;
286 scoped_refptr<net::IOBufferWithSize> buffer_;
287 base::WeakPtrFactory<BlobReader> weak_ptr_factory_;
290 // The state needed to pass between ServiceWorkerCache::Keys callbacks.
291 struct ServiceWorkerCache::KeysContext {
292 KeysContext(const ServiceWorkerCache::RequestsCallback& callback)
293 : original_callback(callback),
294 out_keys(new ServiceWorkerCache::Requests()),
295 enumerated_entry(NULL) {}
297 ~KeysContext() {
298 for (size_t i = 0, max = entries.size(); i < max; ++i)
299 entries[i]->Close();
300 if (enumerated_entry)
301 enumerated_entry->Close();
304 // The callback passed to the Keys() function.
305 ServiceWorkerCache::RequestsCallback original_callback;
307 // The vector of open entries in the backend.
308 Entries entries;
310 // The output of the Keys function.
311 scoped_ptr<ServiceWorkerCache::Requests> out_keys;
313 // Used for enumerating cache entries.
314 scoped_ptr<disk_cache::Backend::Iterator> backend_iterator;
315 disk_cache::Entry* enumerated_entry;
317 DISALLOW_COPY_AND_ASSIGN(KeysContext);
320 struct ServiceWorkerCache::MatchContext {
321 MatchContext(scoped_ptr<ServiceWorkerFetchRequest> request,
322 const ServiceWorkerCache::ResponseCallback& callback,
323 base::WeakPtr<storage::BlobStorageContext> blob_storage_context)
324 : request(request.Pass()),
325 original_callback(callback),
326 blob_storage_context(blob_storage_context),
327 entry(nullptr),
328 total_bytes_read(0) {}
330 ~MatchContext() {
331 if (entry)
332 entry->Close();
335 // Input
336 scoped_ptr<ServiceWorkerFetchRequest> request;
337 ServiceWorkerCache::ResponseCallback original_callback;
338 base::WeakPtr<storage::BlobStorageContext> blob_storage_context;
339 disk_cache::Entry* entry;
341 // Output
342 scoped_ptr<ServiceWorkerResponse> response;
343 scoped_refptr<storage::BlobData> blob_data;
345 // For reading the cache entry data into a blob.
346 scoped_refptr<net::IOBufferWithSize> response_body_buffer;
347 size_t total_bytes_read;
349 DISALLOW_COPY_AND_ASSIGN(MatchContext);
352 // The state needed to pass between ServiceWorkerCache::Put callbacks.
353 struct ServiceWorkerCache::PutContext {
354 PutContext(
355 const GURL& origin,
356 scoped_ptr<ServiceWorkerFetchRequest> request,
357 scoped_ptr<ServiceWorkerResponse> response,
358 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
359 const ServiceWorkerCache::ResponseCallback& callback,
360 net::URLRequestContext* request_context,
361 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy)
362 : origin(origin),
363 request(request.Pass()),
364 response(response.Pass()),
365 blob_data_handle(blob_data_handle.Pass()),
366 callback(callback),
367 request_context(request_context),
368 quota_manager_proxy(quota_manager_proxy),
369 cache_entry(NULL) {}
370 ~PutContext() {
371 if (cache_entry)
372 cache_entry->Close();
375 // Input parameters to the Put function.
376 GURL origin;
377 scoped_ptr<ServiceWorkerFetchRequest> request;
378 scoped_ptr<ServiceWorkerResponse> response;
379 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
380 ServiceWorkerCache::ResponseCallback callback;
381 net::URLRequestContext* request_context;
382 scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy;
384 // This isn't a scoped_ptr because the disk_cache needs an Entry** as input to
385 // CreateEntry.
386 disk_cache::Entry* cache_entry;
388 // The BlobDataHandle for the output ServiceWorkerResponse.
389 scoped_ptr<storage::BlobDataHandle> out_blob_data_handle;
391 DISALLOW_COPY_AND_ASSIGN(PutContext);
394 // static
395 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreateMemoryCache(
396 const GURL& origin,
397 net::URLRequestContext* request_context,
398 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
399 base::WeakPtr<storage::BlobStorageContext> blob_context) {
400 return make_scoped_refptr(new ServiceWorkerCache(origin,
401 base::FilePath(),
402 request_context,
403 quota_manager_proxy,
404 blob_context));
407 // static
408 scoped_refptr<ServiceWorkerCache> ServiceWorkerCache::CreatePersistentCache(
409 const GURL& origin,
410 const base::FilePath& path,
411 net::URLRequestContext* request_context,
412 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
413 base::WeakPtr<storage::BlobStorageContext> blob_context) {
414 return make_scoped_refptr(new ServiceWorkerCache(
415 origin, path, request_context, quota_manager_proxy, blob_context));
418 ServiceWorkerCache::~ServiceWorkerCache() {
421 base::WeakPtr<ServiceWorkerCache> ServiceWorkerCache::AsWeakPtr() {
422 return weak_ptr_factory_.GetWeakPtr();
425 void ServiceWorkerCache::Put(scoped_ptr<ServiceWorkerFetchRequest> request,
426 scoped_ptr<ServiceWorkerResponse> response,
427 const ResponseCallback& callback) {
428 IncPendingOps();
429 ResponseCallback pending_callback =
430 base::Bind(&ServiceWorkerCache::PendingResponseCallback,
431 weak_ptr_factory_.GetWeakPtr(), callback);
432 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
434 if (!response->blob_uuid.empty()) {
435 if (!blob_storage_context_) {
436 pending_callback.Run(ErrorTypeStorage,
437 scoped_ptr<ServiceWorkerResponse>(),
438 scoped_ptr<storage::BlobDataHandle>());
439 return;
441 blob_data_handle =
442 blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
443 if (!blob_data_handle) {
444 pending_callback.Run(ErrorTypeStorage,
445 scoped_ptr<ServiceWorkerResponse>(),
446 scoped_ptr<storage::BlobDataHandle>());
447 return;
451 scoped_ptr<PutContext> put_context(new PutContext(
452 origin_, request.Pass(), response.Pass(), blob_data_handle.Pass(),
453 pending_callback, request_context_, quota_manager_proxy_));
455 if (put_context->blob_data_handle) {
456 // Grab another handle to the blob for the callback response.
457 put_context->out_blob_data_handle =
458 blob_storage_context_->GetBlobDataFromUUID(
459 put_context->response->blob_uuid);
462 base::Closure continuation =
463 base::Bind(&ServiceWorkerCache::PutImpl, weak_ptr_factory_.GetWeakPtr(),
464 base::Passed(put_context.Pass()));
466 if (backend_state_ == BACKEND_UNINITIALIZED) {
467 InitBackend(continuation);
468 return;
471 continuation.Run();
474 void ServiceWorkerCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request,
475 const ResponseCallback& callback) {
476 IncPendingOps();
477 ResponseCallback pending_callback =
478 base::Bind(&ServiceWorkerCache::PendingResponseCallback,
479 weak_ptr_factory_.GetWeakPtr(), callback);
481 switch (backend_state_) {
482 case BACKEND_UNINITIALIZED:
483 InitBackend(base::Bind(&ServiceWorkerCache::Match,
484 weak_ptr_factory_.GetWeakPtr(),
485 base::Passed(request.Pass()), pending_callback));
486 return;
487 case BACKEND_CLOSED:
488 pending_callback.Run(ErrorTypeStorage,
489 scoped_ptr<ServiceWorkerResponse>(),
490 scoped_ptr<storage::BlobDataHandle>());
491 return;
492 case BACKEND_OPEN:
493 DCHECK(backend_);
494 break;
497 scoped_ptr<MatchContext> match_context(new MatchContext(
498 request.Pass(), pending_callback, blob_storage_context_));
500 disk_cache::Entry** entry_ptr = &match_context->entry;
501 ServiceWorkerFetchRequest* request_ptr = match_context->request.get();
503 net::CompletionCallback open_entry_callback = base::Bind(
504 &ServiceWorkerCache::MatchDidOpenEntry, weak_ptr_factory_.GetWeakPtr(),
505 base::Passed(match_context.Pass()));
507 int rv = backend_->OpenEntry(
508 request_ptr->url.spec(), entry_ptr, open_entry_callback);
509 if (rv != net::ERR_IO_PENDING)
510 open_entry_callback.Run(rv);
513 void ServiceWorkerCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request,
514 const ErrorCallback& callback) {
515 IncPendingOps();
516 ErrorCallback pending_callback =
517 base::Bind(&ServiceWorkerCache::PendingErrorCallback,
518 weak_ptr_factory_.GetWeakPtr(), callback);
520 switch (backend_state_) {
521 case BACKEND_UNINITIALIZED:
522 InitBackend(base::Bind(&ServiceWorkerCache::Delete,
523 weak_ptr_factory_.GetWeakPtr(),
524 base::Passed(request.Pass()), pending_callback));
525 return;
526 case BACKEND_CLOSED:
527 pending_callback.Run(ErrorTypeStorage);
528 return;
529 case BACKEND_OPEN:
530 DCHECK(backend_);
531 break;
534 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
536 disk_cache::Entry** entry_ptr = entry.get();
538 ServiceWorkerFetchRequest* request_ptr = request.get();
540 net::CompletionCallback open_entry_callback = base::Bind(
541 &ServiceWorkerCache::DeleteDidOpenEntry, weak_ptr_factory_.GetWeakPtr(),
542 origin_, base::Passed(request.Pass()), pending_callback,
543 base::Passed(entry.Pass()), quota_manager_proxy_);
545 int rv = backend_->OpenEntry(
546 request_ptr->url.spec(), entry_ptr, open_entry_callback);
547 if (rv != net::ERR_IO_PENDING)
548 open_entry_callback.Run(rv);
551 void ServiceWorkerCache::Keys(const RequestsCallback& callback) {
552 IncPendingOps();
553 RequestsCallback pending_callback =
554 base::Bind(&ServiceWorkerCache::PendingRequestsCallback,
555 weak_ptr_factory_.GetWeakPtr(), callback);
557 switch (backend_state_) {
558 case BACKEND_UNINITIALIZED:
559 InitBackend(base::Bind(&ServiceWorkerCache::Keys,
560 weak_ptr_factory_.GetWeakPtr(), pending_callback));
561 return;
562 case BACKEND_CLOSED:
563 pending_callback.Run(ErrorTypeStorage, scoped_ptr<Requests>());
564 return;
565 case BACKEND_OPEN:
566 DCHECK(backend_);
567 break;
570 // 1. Iterate through all of the entries, open them, and add them to a vector.
571 // 2. For each open entry:
572 // 2.1. Read the headers into a protobuf.
573 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
574 // 2.3. Push the response into a vector of requests to be returned.
575 // 3. Return the vector of requests (keys).
577 // The entries have to be loaded into a vector first because enumeration loops
578 // forever if you read data from a cache entry while enumerating.
580 scoped_ptr<KeysContext> keys_context(new KeysContext(pending_callback));
582 keys_context->backend_iterator = backend_->CreateIterator();
583 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
584 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
586 net::CompletionCallback open_entry_callback = base::Bind(
587 &ServiceWorkerCache::KeysDidOpenNextEntry, weak_ptr_factory_.GetWeakPtr(),
588 base::Passed(keys_context.Pass()));
590 int rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
592 if (rv != net::ERR_IO_PENDING)
593 open_entry_callback.Run(rv);
596 void ServiceWorkerCache::Close(const base::Closure& callback) {
597 DCHECK(backend_state_ != BACKEND_CLOSED)
598 << "Don't call ServiceWorkerCache::Close() twice.";
600 backend_state_ = BACKEND_CLOSED;
601 if (pending_ops_ > 0) {
602 DCHECK(ops_complete_callback_.is_null());
603 ops_complete_callback_ =
604 base::Bind(&ServiceWorkerCache::CloseImpl,
605 weak_ptr_factory_.GetWeakPtr(), callback);
606 return;
609 CloseImpl(callback);
612 int64 ServiceWorkerCache::MemoryBackedSize() const {
613 if (backend_state_ != BACKEND_OPEN || !memory_only_)
614 return 0;
616 scoped_ptr<disk_cache::Backend::Iterator> backend_iter =
617 backend_->CreateIterator();
618 disk_cache::Entry* entry = nullptr;
620 int64 sum = 0;
622 std::vector<disk_cache::Entry*> entries;
623 int rv = net::OK;
624 while ((rv = backend_iter->OpenNextEntry(
625 &entry, base::Bind(NotReachedCompletionCallback))) == net::OK) {
626 entries.push_back(entry); // Open the entries without mutating them.
628 DCHECK(rv !=
629 net::ERR_IO_PENDING); // Expect all memory ops to be synchronous.
631 for (disk_cache::Entry* entry : entries) {
632 sum += entry->GetDataSize(INDEX_HEADERS) +
633 entry->GetDataSize(INDEX_RESPONSE_BODY);
634 entry->Close();
637 return sum;
640 ServiceWorkerCache::ServiceWorkerCache(
641 const GURL& origin,
642 const base::FilePath& path,
643 net::URLRequestContext* request_context,
644 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
645 base::WeakPtr<storage::BlobStorageContext> blob_context)
646 : origin_(origin),
647 path_(path),
648 request_context_(request_context),
649 quota_manager_proxy_(quota_manager_proxy),
650 blob_storage_context_(blob_context),
651 backend_state_(BACKEND_UNINITIALIZED),
652 memory_only_(path.empty()),
653 pending_ops_(0),
654 weak_ptr_factory_(this) {
657 void ServiceWorkerCache::MatchDidOpenEntry(
658 scoped_ptr<MatchContext> match_context,
659 int rv) {
660 if (rv != net::OK) {
661 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
662 scoped_ptr<ServiceWorkerResponse>(),
663 scoped_ptr<storage::BlobDataHandle>());
664 return;
667 // Copy the entry pointer before passing it in base::Bind.
668 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
669 DCHECK(tmp_entry_ptr);
671 MetadataCallback headers_callback = base::Bind(
672 &ServiceWorkerCache::MatchDidReadMetadata, weak_ptr_factory_.GetWeakPtr(),
673 base::Passed(match_context.Pass()));
675 ReadMetadata(tmp_entry_ptr, headers_callback);
678 void ServiceWorkerCache::MatchDidReadMetadata(
679 scoped_ptr<MatchContext> match_context,
680 scoped_ptr<ServiceWorkerCacheMetadata> metadata) {
681 if (!metadata) {
682 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeStorage,
683 scoped_ptr<ServiceWorkerResponse>(),
684 scoped_ptr<storage::BlobDataHandle>());
685 return;
688 match_context->response.reset(new ServiceWorkerResponse(
689 match_context->request->url, metadata->response().status_code(),
690 metadata->response().status_text(),
691 ProtoResponseTypeToWebResponseType(metadata->response().response_type()),
692 ServiceWorkerHeaderMap(), "", 0));
694 ServiceWorkerResponse* response = match_context->response.get();
696 if (metadata->response().has_url())
697 response->url = GURL(metadata->response().url());
699 for (int i = 0; i < metadata->response().headers_size(); ++i) {
700 const ServiceWorkerCacheHeaderMap header = metadata->response().headers(i);
701 response->headers.insert(std::make_pair(header.name(), header.value()));
704 ServiceWorkerHeaderMap cached_request_headers;
705 for (int i = 0; i < metadata->request().headers_size(); ++i) {
706 const ServiceWorkerCacheHeaderMap header = metadata->request().headers(i);
707 cached_request_headers[header.name()] = header.value();
710 if (!VaryMatches(match_context->request->headers, cached_request_headers,
711 response->headers)) {
712 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeNotFound,
713 scoped_ptr<ServiceWorkerResponse>(),
714 scoped_ptr<storage::BlobDataHandle>());
715 return;
718 if (match_context->entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
719 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeOK,
720 match_context->response.Pass(),
721 scoped_ptr<storage::BlobDataHandle>());
722 return;
725 // Stream the response body into a blob.
726 if (!match_context->blob_storage_context) {
727 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeStorage,
728 scoped_ptr<ServiceWorkerResponse>(),
729 scoped_ptr<storage::BlobDataHandle>());
730 return;
733 response->blob_uuid = base::GenerateGUID();
735 match_context->blob_data = new storage::BlobData(response->blob_uuid);
736 match_context->response_body_buffer = new net::IOBufferWithSize(kBufferSize);
738 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
739 net::IOBufferWithSize* response_body_buffer =
740 match_context->response_body_buffer.get();
742 net::CompletionCallback read_callback = base::Bind(
743 &ServiceWorkerCache::MatchDidReadResponseBodyData,
744 weak_ptr_factory_.GetWeakPtr(), base::Passed(match_context.Pass()));
746 int read_rv =
747 tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, 0, response_body_buffer,
748 response_body_buffer->size(), read_callback);
750 if (read_rv != net::ERR_IO_PENDING)
751 read_callback.Run(read_rv);
754 void ServiceWorkerCache::MatchDidReadResponseBodyData(
755 scoped_ptr<MatchContext> match_context,
756 int rv) {
757 if (rv < 0) {
758 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeStorage,
759 scoped_ptr<ServiceWorkerResponse>(),
760 scoped_ptr<storage::BlobDataHandle>());
761 return;
764 if (rv == 0) {
765 match_context->response->blob_uuid = match_context->blob_data->uuid();
766 match_context->response->blob_size = match_context->total_bytes_read;
767 MatchDoneWithBody(match_context.Pass());
768 return;
771 // TODO(jkarlin): This copying of the the entire cache response into memory is
772 // awful. Create a new interface around SimpleCache that provides access the
773 // data directly from the file. See bug http://crbug.com/403493.
774 match_context->blob_data->AppendData(
775 match_context->response_body_buffer->data(), rv);
776 match_context->total_bytes_read += rv;
777 int total_bytes_read = match_context->total_bytes_read;
779 // Grab some pointers before passing match_context in bind.
780 net::IOBufferWithSize* buffer = match_context->response_body_buffer.get();
781 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
783 net::CompletionCallback read_callback = base::Bind(
784 &ServiceWorkerCache::MatchDidReadResponseBodyData,
785 weak_ptr_factory_.GetWeakPtr(), base::Passed(match_context.Pass()));
787 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, total_bytes_read,
788 buffer, buffer->size(), read_callback);
790 if (read_rv != net::ERR_IO_PENDING)
791 read_callback.Run(read_rv);
794 void ServiceWorkerCache::MatchDoneWithBody(
795 scoped_ptr<MatchContext> match_context) {
796 if (!match_context->blob_storage_context) {
797 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeStorage,
798 scoped_ptr<ServiceWorkerResponse>(),
799 scoped_ptr<storage::BlobDataHandle>());
800 return;
803 scoped_ptr<storage::BlobDataHandle> blob_data_handle(
804 match_context->blob_storage_context->AddFinishedBlob(
805 match_context->blob_data.get()));
807 match_context->original_callback.Run(ServiceWorkerCache::ErrorTypeOK,
808 match_context->response.Pass(),
809 blob_data_handle.Pass());
812 void ServiceWorkerCache::PutImpl(scoped_ptr<PutContext> put_context) {
813 if (backend_state_ != BACKEND_OPEN) {
814 put_context->callback.Run(ErrorTypeStorage,
815 scoped_ptr<ServiceWorkerResponse>(),
816 scoped_ptr<storage::BlobDataHandle>());
817 return;
820 scoped_ptr<ServiceWorkerFetchRequest> request_copy(
821 new ServiceWorkerFetchRequest(*put_context->request));
823 Delete(request_copy.Pass(), base::Bind(&ServiceWorkerCache::PutDidDelete,
824 weak_ptr_factory_.GetWeakPtr(),
825 base::Passed(put_context.Pass())));
828 void ServiceWorkerCache::PutDidDelete(scoped_ptr<PutContext> put_context,
829 ErrorType delete_error) {
830 if (backend_state_ != BACKEND_OPEN) {
831 put_context->callback.Run(ErrorTypeStorage,
832 scoped_ptr<ServiceWorkerResponse>(),
833 scoped_ptr<storage::BlobDataHandle>());
834 return;
837 disk_cache::Entry** entry_ptr = &put_context->cache_entry;
838 ServiceWorkerFetchRequest* request_ptr = put_context->request.get();
839 disk_cache::Backend* backend_ptr = backend_.get();
841 net::CompletionCallback create_entry_callback = base::Bind(
842 &ServiceWorkerCache::PutDidCreateEntry, weak_ptr_factory_.GetWeakPtr(),
843 base::Passed(put_context.Pass()));
845 int create_rv = backend_ptr->CreateEntry(
846 request_ptr->url.spec(), entry_ptr, create_entry_callback);
848 if (create_rv != net::ERR_IO_PENDING)
849 create_entry_callback.Run(create_rv);
852 void ServiceWorkerCache::PutDidCreateEntry(scoped_ptr<PutContext> put_context,
853 int rv) {
854 if (rv != net::OK) {
855 put_context->callback.Run(ServiceWorkerCache::ErrorTypeExists,
856 scoped_ptr<ServiceWorkerResponse>(),
857 scoped_ptr<storage::BlobDataHandle>());
858 return;
861 DCHECK(put_context->cache_entry);
863 ServiceWorkerCacheMetadata metadata;
864 ServiceWorkerCacheRequest* request_metadata = metadata.mutable_request();
865 request_metadata->set_method(put_context->request->method);
866 for (ServiceWorkerHeaderMap::const_iterator it =
867 put_context->request->headers.begin();
868 it != put_context->request->headers.end();
869 ++it) {
870 ServiceWorkerCacheHeaderMap* header_map = request_metadata->add_headers();
871 header_map->set_name(it->first);
872 header_map->set_value(it->second);
875 ServiceWorkerCacheResponse* response_metadata = metadata.mutable_response();
876 response_metadata->set_status_code(put_context->response->status_code);
877 response_metadata->set_status_text(put_context->response->status_text);
878 response_metadata->set_response_type(
879 WebResponseTypeToProtoResponseType(put_context->response->response_type));
880 response_metadata->set_url(put_context->response->url.spec());
881 for (ServiceWorkerHeaderMap::const_iterator it =
882 put_context->response->headers.begin();
883 it != put_context->response->headers.end();
884 ++it) {
885 ServiceWorkerCacheHeaderMap* header_map = response_metadata->add_headers();
886 header_map->set_name(it->first);
887 header_map->set_value(it->second);
890 scoped_ptr<std::string> serialized(new std::string());
891 if (!metadata.SerializeToString(serialized.get())) {
892 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
893 scoped_ptr<ServiceWorkerResponse>(),
894 scoped_ptr<storage::BlobDataHandle>());
895 return;
898 scoped_refptr<net::StringIOBuffer> buffer(
899 new net::StringIOBuffer(serialized.Pass()));
901 // Get a temporary copy of the entry pointer before passing it in base::Bind.
902 disk_cache::Entry* tmp_entry_ptr = put_context->cache_entry;
904 net::CompletionCallback write_headers_callback = base::Bind(
905 &ServiceWorkerCache::PutDidWriteHeaders, weak_ptr_factory_.GetWeakPtr(),
906 base::Passed(put_context.Pass()), buffer->size());
908 rv = tmp_entry_ptr->WriteData(INDEX_HEADERS,
909 0 /* offset */,
910 buffer.get(),
911 buffer->size(),
912 write_headers_callback,
913 true /* truncate */);
915 if (rv != net::ERR_IO_PENDING)
916 write_headers_callback.Run(rv);
919 void ServiceWorkerCache::PutDidWriteHeaders(scoped_ptr<PutContext> put_context,
920 int expected_bytes,
921 int rv) {
922 if (rv != expected_bytes) {
923 put_context->cache_entry->Doom();
924 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
925 scoped_ptr<ServiceWorkerResponse>(),
926 scoped_ptr<storage::BlobDataHandle>());
927 return;
930 // The metadata is written, now for the response content. The data is streamed
931 // from the blob into the cache entry.
933 if (put_context->response->blob_uuid.empty()) {
934 if (put_context->quota_manager_proxy.get()) {
935 put_context->quota_manager_proxy->NotifyStorageModified(
936 storage::QuotaClient::kServiceWorkerCache,
937 put_context->origin,
938 storage::kStorageTypeTemporary,
939 put_context->cache_entry->GetDataSize(INDEX_HEADERS));
942 put_context->callback.Run(ServiceWorkerCache::ErrorTypeOK,
943 put_context->response.Pass(),
944 scoped_ptr<storage::BlobDataHandle>());
945 return;
948 DCHECK(put_context->blob_data_handle);
950 disk_cache::ScopedEntryPtr entry(put_context->cache_entry);
951 put_context->cache_entry = NULL;
952 scoped_ptr<BlobReader> reader(new BlobReader());
953 BlobReader* reader_ptr = reader.get();
955 // Grab some pointers before passing put_context in Bind.
956 net::URLRequestContext* request_context = put_context->request_context;
957 scoped_ptr<storage::BlobDataHandle> blob_data_handle =
958 put_context->blob_data_handle.Pass();
960 reader_ptr->StreamBlobToCache(
961 entry.Pass(), request_context, blob_data_handle.Pass(),
962 base::Bind(&ServiceWorkerCache::PutDidWriteBlobToCache,
963 weak_ptr_factory_.GetWeakPtr(),
964 base::Passed(put_context.Pass()),
965 base::Passed(reader.Pass())));
968 void ServiceWorkerCache::PutDidWriteBlobToCache(
969 scoped_ptr<PutContext> put_context,
970 scoped_ptr<BlobReader> blob_reader,
971 disk_cache::ScopedEntryPtr entry,
972 bool success) {
973 DCHECK(entry);
974 put_context->cache_entry = entry.release();
976 if (!success) {
977 put_context->cache_entry->Doom();
978 put_context->callback.Run(ServiceWorkerCache::ErrorTypeStorage,
979 scoped_ptr<ServiceWorkerResponse>(),
980 scoped_ptr<storage::BlobDataHandle>());
981 return;
984 if (put_context->quota_manager_proxy.get()) {
985 put_context->quota_manager_proxy->NotifyStorageModified(
986 storage::QuotaClient::kServiceWorkerCache,
987 put_context->origin,
988 storage::kStorageTypeTemporary,
989 put_context->cache_entry->GetDataSize(INDEX_HEADERS) +
990 put_context->cache_entry->GetDataSize(INDEX_RESPONSE_BODY));
993 put_context->callback.Run(ServiceWorkerCache::ErrorTypeOK,
994 put_context->response.Pass(),
995 put_context->out_blob_data_handle.Pass());
998 void ServiceWorkerCache::DeleteDidOpenEntry(
999 const GURL& origin,
1000 scoped_ptr<ServiceWorkerFetchRequest> request,
1001 const ServiceWorkerCache::ErrorCallback& callback,
1002 scoped_ptr<disk_cache::Entry*> entry_ptr,
1003 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
1004 int rv) {
1005 if (rv != net::OK) {
1006 callback.Run(ServiceWorkerCache::ErrorTypeNotFound);
1007 return;
1010 DCHECK(entry_ptr);
1011 disk_cache::ScopedEntryPtr entry(*entry_ptr);
1013 if (quota_manager_proxy.get()) {
1014 quota_manager_proxy->NotifyStorageModified(
1015 storage::QuotaClient::kServiceWorkerCache, origin,
1016 storage::kStorageTypeTemporary,
1017 -1 * (entry->GetDataSize(INDEX_HEADERS) +
1018 entry->GetDataSize(INDEX_RESPONSE_BODY)));
1021 entry->Doom();
1022 callback.Run(ServiceWorkerCache::ErrorTypeOK);
1025 void ServiceWorkerCache::KeysDidOpenNextEntry(
1026 scoped_ptr<KeysContext> keys_context,
1027 int rv) {
1028 if (rv == net::ERR_FAILED) {
1029 DCHECK(!keys_context->enumerated_entry);
1030 // Enumeration is complete, extract the requests from the entries.
1031 Entries::iterator iter = keys_context->entries.begin();
1032 KeysProcessNextEntry(keys_context.Pass(), iter);
1033 return;
1036 if (rv < 0) {
1037 keys_context->original_callback.Run(ErrorTypeStorage,
1038 scoped_ptr<Requests>());
1039 return;
1042 if (backend_state_ != BACKEND_OPEN) {
1043 keys_context->original_callback.Run(ErrorTypeNotFound,
1044 scoped_ptr<Requests>());
1045 return;
1048 // Store the entry.
1049 keys_context->entries.push_back(keys_context->enumerated_entry);
1050 keys_context->enumerated_entry = NULL;
1052 // Enumerate the next entry.
1053 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
1054 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
1055 net::CompletionCallback open_entry_callback = base::Bind(
1056 &ServiceWorkerCache::KeysDidOpenNextEntry, weak_ptr_factory_.GetWeakPtr(),
1057 base::Passed(keys_context.Pass()));
1059 rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
1061 if (rv != net::ERR_IO_PENDING)
1062 open_entry_callback.Run(rv);
1065 void ServiceWorkerCache::KeysProcessNextEntry(
1066 scoped_ptr<KeysContext> keys_context,
1067 const Entries::iterator& iter) {
1068 if (iter == keys_context->entries.end()) {
1069 // All done. Return all of the keys.
1070 keys_context->original_callback.Run(ErrorTypeOK,
1071 keys_context->out_keys.Pass());
1072 return;
1075 ReadMetadata(*iter, base::Bind(&ServiceWorkerCache::KeysDidReadMetadata,
1076 weak_ptr_factory_.GetWeakPtr(),
1077 base::Passed(keys_context.Pass()), iter));
1080 void ServiceWorkerCache::KeysDidReadMetadata(
1081 scoped_ptr<KeysContext> keys_context,
1082 const Entries::iterator& iter,
1083 scoped_ptr<ServiceWorkerCacheMetadata> metadata) {
1084 disk_cache::Entry* entry = *iter;
1086 if (metadata) {
1087 keys_context->out_keys->push_back(
1088 ServiceWorkerFetchRequest(GURL(entry->GetKey()),
1089 metadata->request().method(),
1090 ServiceWorkerHeaderMap(),
1091 GURL(),
1092 false));
1094 ServiceWorkerHeaderMap& req_headers =
1095 keys_context->out_keys->back().headers;
1097 for (int i = 0; i < metadata->request().headers_size(); ++i) {
1098 const ServiceWorkerCacheHeaderMap header = metadata->request().headers(i);
1099 req_headers.insert(std::make_pair(header.name(), header.value()));
1101 } else {
1102 entry->Doom();
1105 KeysProcessNextEntry(keys_context.Pass(), iter + 1);
1108 void ServiceWorkerCache::CloseImpl(const base::Closure& callback) {
1109 DCHECK(backend_state_ == BACKEND_CLOSED);
1110 backend_.reset();
1111 callback.Run();
1114 void ServiceWorkerCache::CreateBackend(const ErrorCallback& callback) {
1115 DCHECK(!backend_);
1117 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
1118 net::CacheType cache_type = memory_only_ ? net::MEMORY_CACHE : net::APP_CACHE;
1120 scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr());
1122 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
1123 ScopedBackendPtr* backend = backend_ptr.get();
1125 net::CompletionCallback create_cache_callback =
1126 base::Bind(&ServiceWorkerCache::CreateBackendDidCreate,
1127 weak_ptr_factory_.GetWeakPtr(), callback,
1128 base::Passed(backend_ptr.Pass()));
1130 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
1131 // has for disk caches.
1132 int rv = disk_cache::CreateCacheBackend(
1133 cache_type,
1134 net::CACHE_BACKEND_SIMPLE,
1135 path_,
1136 kMaxCacheBytes,
1137 false, /* force */
1138 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
1139 NULL,
1140 backend,
1141 create_cache_callback);
1142 if (rv != net::ERR_IO_PENDING)
1143 create_cache_callback.Run(rv);
1146 void ServiceWorkerCache::CreateBackendDidCreate(
1147 const ServiceWorkerCache::ErrorCallback& callback,
1148 scoped_ptr<ScopedBackendPtr> backend_ptr,
1149 int rv) {
1150 if (rv != net::OK) {
1151 callback.Run(ServiceWorkerCache::ErrorTypeStorage);
1152 return;
1155 backend_ = backend_ptr->Pass();
1156 callback.Run(ServiceWorkerCache::ErrorTypeOK);
1159 void ServiceWorkerCache::InitBackend(const base::Closure& callback) {
1160 DCHECK(backend_state_ == BACKEND_UNINITIALIZED);
1161 init_callbacks_.push_back(callback);
1163 // If this isn't the first call to Init then return as the initialization
1164 // has already started.
1165 if (init_callbacks_.size() > 1u)
1166 return;
1168 CreateBackend(base::Bind(&ServiceWorkerCache::InitDone,
1169 weak_ptr_factory_.GetWeakPtr()));
1172 void ServiceWorkerCache::InitDone(ErrorType error) {
1173 backend_state_ = (error == ErrorTypeOK && backend_ &&
1174 backend_state_ == BACKEND_UNINITIALIZED)
1175 ? BACKEND_OPEN
1176 : BACKEND_CLOSED;
1177 for (std::vector<base::Closure>::iterator it = init_callbacks_.begin();
1178 it != init_callbacks_.end(); ++it) {
1179 it->Run();
1181 init_callbacks_.clear();
1184 void ServiceWorkerCache::DecPendingOps() {
1185 DCHECK(pending_ops_ > 0);
1186 pending_ops_--;
1187 if (pending_ops_ == 0 && !ops_complete_callback_.is_null()) {
1188 ops_complete_callback_.Run();
1189 ops_complete_callback_.Reset();
1193 void ServiceWorkerCache::PendingErrorCallback(const ErrorCallback& callback,
1194 ErrorType error) {
1195 callback.Run(error);
1196 DecPendingOps();
1199 void ServiceWorkerCache::PendingResponseCallback(
1200 const ResponseCallback& callback,
1201 ErrorType error,
1202 scoped_ptr<ServiceWorkerResponse> response,
1203 scoped_ptr<storage::BlobDataHandle> blob_data_handle) {
1204 callback.Run(error, response.Pass(), blob_data_handle.Pass());
1205 DecPendingOps();
1208 void ServiceWorkerCache::PendingRequestsCallback(
1209 const RequestsCallback& callback,
1210 ErrorType error,
1211 scoped_ptr<Requests> requests) {
1212 callback.Run(error, requests.Pass());
1213 DecPendingOps();
1216 } // namespace content