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"
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"
30 typedef base::Callback
<void(bool)> BoolCallback
;
31 typedef base::Callback
<void(disk_cache::ScopedEntryPtr
, bool)>
33 typedef base::Callback
<void(scoped_ptr
<ServiceWorkerCacheMetadata
>)>
36 enum EntryIndex
{ INDEX_HEADERS
= 0, INDEX_RESPONSE_BODY
};
38 // The maximum size of an individual cache. Ultimately cache size is controlled
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
) {
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
;
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
;
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
,
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())
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();
108 base::TrimWhitespaceASCII(*it
, base::TRIM_ALL
, &trimmed
);
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()))
121 // If the header exists in one, it exists in both. Verify that the values
123 if (request_iter
!= request
.end() &&
124 request_iter
->second
!= cached_request_iter
->second
)
132 void ReadMetadata(disk_cache::Entry
* entry
, const MetadataCallback
& callback
) {
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(),
143 tracked_objects::ScopedTracker::TrackCallback(
144 FROM_HERE_WITH_EXPLICIT_FUNCTION("422516 ReadMetadata"),
145 read_header_callback
));
147 if (read_rv
!= net::ERR_IO_PENDING
)
148 read_header_callback
.Run(read_rv
);
151 void ReadMetadataDidReadMetadata(
152 disk_cache::Entry
* entry
,
153 const MetadataCallback
& callback
,
154 const scoped_refptr
<net::IOBufferWithSize
>& buffer
,
156 if (rv
!= buffer
->size()) {
157 callback
.Run(scoped_ptr
<ServiceWorkerCacheMetadata
>());
161 scoped_ptr
<ServiceWorkerCacheMetadata
> metadata(
162 new ServiceWorkerCacheMetadata());
164 if (!metadata
->ParseFromArray(buffer
->data(), buffer
->size())) {
165 callback
.Run(scoped_ptr
<ServiceWorkerCacheMetadata
>());
169 callback
.Run(metadata
.Pass());
174 // Streams data from a blob and writes it to a given disk_cache::Entry.
175 class ServiceWorkerCache::BlobReader
: public net::URLRequest::Delegate
{
177 typedef base::Callback
<void(disk_cache::ScopedEntryPtr
, bool)>
178 EntryAndBoolCallback
;
181 : cache_entry_offset_(0),
182 buffer_(new net::IOBufferWithSize(kBufferSize
)),
183 weak_ptr_factory_(this) {}
185 // |entry| is passed to the callback once complete.
186 void StreamBlobToCache(disk_cache::ScopedEntryPtr entry
,
187 net::URLRequestContext
* request_context
,
188 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
189 const EntryAndBoolCallback
& callback
) {
191 entry_
= entry
.Pass();
192 callback_
= callback
;
193 blob_request_
= storage::BlobProtocolHandler::CreateBlobRequest(
194 blob_data_handle
.Pass(), request_context
, this);
195 blob_request_
->Start();
198 // net::URLRequest::Delegate overrides for reading blobs.
199 void OnReceivedRedirect(net::URLRequest
* request
,
200 const net::RedirectInfo
& redirect_info
,
201 bool* defer_redirect
) override
{
204 void OnAuthRequired(net::URLRequest
* request
,
205 net::AuthChallengeInfo
* auth_info
) override
{
208 void OnCertificateRequested(
209 net::URLRequest
* request
,
210 net::SSLCertRequestInfo
* cert_request_info
) override
{
213 void OnSSLCertificateError(net::URLRequest
* request
,
214 const net::SSLInfo
& ssl_info
,
215 bool fatal
) override
{
218 void OnBeforeNetworkStart(net::URLRequest
* request
, bool* defer
) override
{
222 void OnResponseStarted(net::URLRequest
* request
) override
{
223 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
224 tracked_objects::ScopedTracker
tracking_profile(
225 FROM_HERE_WITH_EXPLICIT_FUNCTION(
226 "423948 ServiceWorkerCache::BlobReader::OnResponseStarted"));
228 if (!request
->status().is_success()) {
229 callback_
.Run(entry_
.Pass(), false);
235 virtual void ReadFromBlob() {
238 blob_request_
->Read(buffer_
.get(), buffer_
->size(), &bytes_read
);
240 OnReadCompleted(blob_request_
.get(), bytes_read
);
243 void OnReadCompleted(net::URLRequest
* request
, int bytes_read
) override
{
244 // TODO(vadimt): Remove ScopedTracker below once crbug.com/423948 is fixed.
245 tracked_objects::ScopedTracker
tracking_profile(
246 FROM_HERE_WITH_EXPLICIT_FUNCTION(
247 "423948 ServiceWorkerCache::BlobReader::OnReadCompleted"));
249 if (!request
->status().is_success()) {
250 callback_
.Run(entry_
.Pass(), false);
254 if (bytes_read
== 0) {
255 callback_
.Run(entry_
.Pass(), true);
259 net::CompletionCallback cache_write_callback
=
260 base::Bind(&BlobReader::DidWriteDataToEntry
,
261 weak_ptr_factory_
.GetWeakPtr(),
264 int rv
= entry_
->WriteData(INDEX_RESPONSE_BODY
,
268 cache_write_callback
,
269 true /* truncate */);
270 if (rv
!= net::ERR_IO_PENDING
)
271 cache_write_callback
.Run(rv
);
274 void DidWriteDataToEntry(int expected_bytes
, int rv
) {
275 if (rv
!= expected_bytes
) {
276 callback_
.Run(entry_
.Pass(), false);
280 cache_entry_offset_
+= rv
;
285 int cache_entry_offset_
;
286 disk_cache::ScopedEntryPtr entry_
;
287 scoped_ptr
<net::URLRequest
> blob_request_
;
288 EntryAndBoolCallback callback_
;
289 scoped_refptr
<net::IOBufferWithSize
> buffer_
;
290 base::WeakPtrFactory
<BlobReader
> weak_ptr_factory_
;
293 // The state needed to pass between ServiceWorkerCache::Keys callbacks.
294 struct ServiceWorkerCache::KeysContext
{
295 KeysContext(const ServiceWorkerCache::RequestsCallback
& callback
)
296 : original_callback(callback
),
297 out_keys(new ServiceWorkerCache::Requests()),
298 enumerated_entry(NULL
) {}
301 for (size_t i
= 0, max
= entries
.size(); i
< max
; ++i
)
303 if (enumerated_entry
)
304 enumerated_entry
->Close();
307 // The callback passed to the Keys() function.
308 ServiceWorkerCache::RequestsCallback original_callback
;
310 // The vector of open entries in the backend.
313 // The output of the Keys function.
314 scoped_ptr
<ServiceWorkerCache::Requests
> out_keys
;
316 // Used for enumerating cache entries.
317 scoped_ptr
<disk_cache::Backend::Iterator
> backend_iterator
;
318 disk_cache::Entry
* enumerated_entry
;
320 DISALLOW_COPY_AND_ASSIGN(KeysContext
);
323 struct ServiceWorkerCache::MatchContext
{
324 MatchContext(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
325 const ServiceWorkerCache::ResponseCallback
& callback
,
326 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
)
327 : request(request
.Pass()),
328 original_callback(callback
),
329 blob_storage_context(blob_storage_context
),
331 total_bytes_read(0) {}
339 scoped_ptr
<ServiceWorkerFetchRequest
> request
;
340 ServiceWorkerCache::ResponseCallback original_callback
;
341 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
;
342 disk_cache::Entry
* entry
;
345 scoped_ptr
<ServiceWorkerResponse
> response
;
346 scoped_refptr
<storage::BlobData
> blob_data
;
348 // For reading the cache entry data into a blob.
349 scoped_refptr
<net::IOBufferWithSize
> response_body_buffer
;
350 size_t total_bytes_read
;
352 DISALLOW_COPY_AND_ASSIGN(MatchContext
);
355 // The state needed to pass between ServiceWorkerCache::Put callbacks.
356 struct ServiceWorkerCache::PutContext
{
359 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
360 scoped_ptr
<ServiceWorkerResponse
> response
,
361 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
362 const ServiceWorkerCache::ResponseCallback
& callback
,
363 net::URLRequestContext
* request_context
,
364 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
)
366 request(request
.Pass()),
367 response(response
.Pass()),
368 blob_data_handle(blob_data_handle
.Pass()),
370 request_context(request_context
),
371 quota_manager_proxy(quota_manager_proxy
),
375 cache_entry
->Close();
378 // Input parameters to the Put function.
380 scoped_ptr
<ServiceWorkerFetchRequest
> request
;
381 scoped_ptr
<ServiceWorkerResponse
> response
;
382 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
;
383 ServiceWorkerCache::ResponseCallback callback
;
384 net::URLRequestContext
* request_context
;
385 scoped_refptr
<storage::QuotaManagerProxy
> quota_manager_proxy
;
387 // This isn't a scoped_ptr because the disk_cache needs an Entry** as input to
389 disk_cache::Entry
* cache_entry
;
391 // The BlobDataHandle for the output ServiceWorkerResponse.
392 scoped_ptr
<storage::BlobDataHandle
> out_blob_data_handle
;
394 DISALLOW_COPY_AND_ASSIGN(PutContext
);
398 scoped_refptr
<ServiceWorkerCache
> ServiceWorkerCache::CreateMemoryCache(
400 net::URLRequestContext
* request_context
,
401 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
402 base::WeakPtr
<storage::BlobStorageContext
> blob_context
) {
403 return make_scoped_refptr(new ServiceWorkerCache(origin
,
411 scoped_refptr
<ServiceWorkerCache
> ServiceWorkerCache::CreatePersistentCache(
413 const base::FilePath
& path
,
414 net::URLRequestContext
* request_context
,
415 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
416 base::WeakPtr
<storage::BlobStorageContext
> blob_context
) {
417 return make_scoped_refptr(new ServiceWorkerCache(
418 origin
, path
, request_context
, quota_manager_proxy
, blob_context
));
421 ServiceWorkerCache::~ServiceWorkerCache() {
424 base::WeakPtr
<ServiceWorkerCache
> ServiceWorkerCache::AsWeakPtr() {
425 return weak_ptr_factory_
.GetWeakPtr();
428 void ServiceWorkerCache::Put(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
429 scoped_ptr
<ServiceWorkerResponse
> response
,
430 const ResponseCallback
& callback
) {
432 ResponseCallback pending_callback
=
433 base::Bind(&ServiceWorkerCache::PendingResponseCallback
,
434 weak_ptr_factory_
.GetWeakPtr(), callback
);
435 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
;
437 if (!response
->blob_uuid
.empty()) {
438 if (!blob_storage_context_
) {
439 pending_callback
.Run(ErrorTypeStorage
,
440 scoped_ptr
<ServiceWorkerResponse
>(),
441 scoped_ptr
<storage::BlobDataHandle
>());
445 blob_storage_context_
->GetBlobDataFromUUID(response
->blob_uuid
);
446 if (!blob_data_handle
) {
447 pending_callback
.Run(ErrorTypeStorage
,
448 scoped_ptr
<ServiceWorkerResponse
>(),
449 scoped_ptr
<storage::BlobDataHandle
>());
454 scoped_ptr
<PutContext
> put_context(new PutContext(
455 origin_
, request
.Pass(), response
.Pass(), blob_data_handle
.Pass(),
456 pending_callback
, request_context_
, quota_manager_proxy_
));
458 if (put_context
->blob_data_handle
) {
459 // Grab another handle to the blob for the callback response.
460 put_context
->out_blob_data_handle
=
461 blob_storage_context_
->GetBlobDataFromUUID(
462 put_context
->response
->blob_uuid
);
465 base::Closure continuation
=
466 base::Bind(&ServiceWorkerCache::PutImpl
, weak_ptr_factory_
.GetWeakPtr(),
467 base::Passed(put_context
.Pass()));
469 if (backend_state_
== BACKEND_UNINITIALIZED
) {
470 InitBackend(continuation
);
477 void ServiceWorkerCache::Match(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
478 const ResponseCallback
& callback
) {
480 ResponseCallback pending_callback
=
481 base::Bind(&ServiceWorkerCache::PendingResponseCallback
,
482 weak_ptr_factory_
.GetWeakPtr(), callback
);
484 switch (backend_state_
) {
485 case BACKEND_UNINITIALIZED
:
486 InitBackend(base::Bind(&ServiceWorkerCache::Match
,
487 weak_ptr_factory_
.GetWeakPtr(),
488 base::Passed(request
.Pass()), pending_callback
));
491 pending_callback
.Run(ErrorTypeStorage
,
492 scoped_ptr
<ServiceWorkerResponse
>(),
493 scoped_ptr
<storage::BlobDataHandle
>());
500 scoped_ptr
<MatchContext
> match_context(new MatchContext(
501 request
.Pass(), pending_callback
, blob_storage_context_
));
503 disk_cache::Entry
** entry_ptr
= &match_context
->entry
;
504 ServiceWorkerFetchRequest
* request_ptr
= match_context
->request
.get();
506 net::CompletionCallback open_entry_callback
= base::Bind(
507 &ServiceWorkerCache::MatchDidOpenEntry
, weak_ptr_factory_
.GetWeakPtr(),
508 base::Passed(match_context
.Pass()));
510 int rv
= backend_
->OpenEntry(
511 request_ptr
->url
.spec(), entry_ptr
, open_entry_callback
);
512 if (rv
!= net::ERR_IO_PENDING
)
513 open_entry_callback
.Run(rv
);
516 void ServiceWorkerCache::Delete(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
517 const ErrorCallback
& callback
) {
519 ErrorCallback pending_callback
=
520 base::Bind(&ServiceWorkerCache::PendingErrorCallback
,
521 weak_ptr_factory_
.GetWeakPtr(), callback
);
523 switch (backend_state_
) {
524 case BACKEND_UNINITIALIZED
:
525 InitBackend(base::Bind(&ServiceWorkerCache::Delete
,
526 weak_ptr_factory_
.GetWeakPtr(),
527 base::Passed(request
.Pass()), pending_callback
));
530 pending_callback
.Run(ErrorTypeStorage
);
537 scoped_ptr
<disk_cache::Entry
*> entry(new disk_cache::Entry
*);
539 disk_cache::Entry
** entry_ptr
= entry
.get();
541 ServiceWorkerFetchRequest
* request_ptr
= request
.get();
543 net::CompletionCallback open_entry_callback
= base::Bind(
544 &ServiceWorkerCache::DeleteDidOpenEntry
, weak_ptr_factory_
.GetWeakPtr(),
545 origin_
, base::Passed(request
.Pass()), pending_callback
,
546 base::Passed(entry
.Pass()), quota_manager_proxy_
);
548 int rv
= backend_
->OpenEntry(
549 request_ptr
->url
.spec(), entry_ptr
, open_entry_callback
);
550 if (rv
!= net::ERR_IO_PENDING
)
551 open_entry_callback
.Run(rv
);
554 void ServiceWorkerCache::Keys(const RequestsCallback
& callback
) {
556 RequestsCallback pending_callback
=
557 base::Bind(&ServiceWorkerCache::PendingRequestsCallback
,
558 weak_ptr_factory_
.GetWeakPtr(), callback
);
560 switch (backend_state_
) {
561 case BACKEND_UNINITIALIZED
:
562 InitBackend(base::Bind(&ServiceWorkerCache::Keys
,
563 weak_ptr_factory_
.GetWeakPtr(), pending_callback
));
566 pending_callback
.Run(ErrorTypeStorage
, scoped_ptr
<Requests
>());
573 // 1. Iterate through all of the entries, open them, and add them to a vector.
574 // 2. For each open entry:
575 // 2.1. Read the headers into a protobuf.
576 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
577 // 2.3. Push the response into a vector of requests to be returned.
578 // 3. Return the vector of requests (keys).
580 // The entries have to be loaded into a vector first because enumeration loops
581 // forever if you read data from a cache entry while enumerating.
583 scoped_ptr
<KeysContext
> keys_context(new KeysContext(pending_callback
));
585 keys_context
->backend_iterator
= backend_
->CreateIterator();
586 disk_cache::Backend::Iterator
& iterator
= *keys_context
->backend_iterator
;
587 disk_cache::Entry
** enumerated_entry
= &keys_context
->enumerated_entry
;
589 net::CompletionCallback open_entry_callback
= base::Bind(
590 &ServiceWorkerCache::KeysDidOpenNextEntry
, weak_ptr_factory_
.GetWeakPtr(),
591 base::Passed(keys_context
.Pass()));
593 int rv
= iterator
.OpenNextEntry(enumerated_entry
, open_entry_callback
);
595 if (rv
!= net::ERR_IO_PENDING
)
596 open_entry_callback
.Run(rv
);
599 void ServiceWorkerCache::Close(const base::Closure
& callback
) {
600 DCHECK(backend_state_
!= BACKEND_CLOSED
)
601 << "Don't call ServiceWorkerCache::Close() twice.";
603 backend_state_
= BACKEND_CLOSED
;
604 if (pending_ops_
> 0) {
605 DCHECK(ops_complete_callback_
.is_null());
606 ops_complete_callback_
=
607 base::Bind(&ServiceWorkerCache::CloseImpl
,
608 weak_ptr_factory_
.GetWeakPtr(), callback
);
615 int64
ServiceWorkerCache::MemoryBackedSize() const {
616 if (backend_state_
!= BACKEND_OPEN
|| !memory_only_
)
619 scoped_ptr
<disk_cache::Backend::Iterator
> backend_iter
=
620 backend_
->CreateIterator();
621 disk_cache::Entry
* entry
= nullptr;
625 std::vector
<disk_cache::Entry
*> entries
;
627 while ((rv
= backend_iter
->OpenNextEntry(
628 &entry
, base::Bind(NotReachedCompletionCallback
))) == net::OK
) {
629 entries
.push_back(entry
); // Open the entries without mutating them.
632 net::ERR_IO_PENDING
); // Expect all memory ops to be synchronous.
634 for (disk_cache::Entry
* entry
: entries
) {
635 sum
+= entry
->GetDataSize(INDEX_HEADERS
) +
636 entry
->GetDataSize(INDEX_RESPONSE_BODY
);
643 ServiceWorkerCache::ServiceWorkerCache(
645 const base::FilePath
& path
,
646 net::URLRequestContext
* request_context
,
647 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
648 base::WeakPtr
<storage::BlobStorageContext
> blob_context
)
651 request_context_(request_context
),
652 quota_manager_proxy_(quota_manager_proxy
),
653 blob_storage_context_(blob_context
),
654 backend_state_(BACKEND_UNINITIALIZED
),
655 memory_only_(path
.empty()),
657 weak_ptr_factory_(this) {
660 void ServiceWorkerCache::MatchDidOpenEntry(
661 scoped_ptr
<MatchContext
> match_context
,
664 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeNotFound
,
665 scoped_ptr
<ServiceWorkerResponse
>(),
666 scoped_ptr
<storage::BlobDataHandle
>());
670 // Copy the entry pointer before passing it in base::Bind.
671 disk_cache::Entry
* tmp_entry_ptr
= match_context
->entry
;
672 DCHECK(tmp_entry_ptr
);
674 MetadataCallback headers_callback
= base::Bind(
675 &ServiceWorkerCache::MatchDidReadMetadata
, weak_ptr_factory_
.GetWeakPtr(),
676 base::Passed(match_context
.Pass()));
678 ReadMetadata(tmp_entry_ptr
, headers_callback
);
681 void ServiceWorkerCache::MatchDidReadMetadata(
682 scoped_ptr
<MatchContext
> match_context
,
683 scoped_ptr
<ServiceWorkerCacheMetadata
> metadata
) {
685 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
686 scoped_ptr
<ServiceWorkerResponse
>(),
687 scoped_ptr
<storage::BlobDataHandle
>());
691 match_context
->response
.reset(new ServiceWorkerResponse(
692 match_context
->request
->url
, metadata
->response().status_code(),
693 metadata
->response().status_text(),
694 ProtoResponseTypeToWebResponseType(metadata
->response().response_type()),
695 ServiceWorkerHeaderMap(), "", 0, GURL()));
697 ServiceWorkerResponse
* response
= match_context
->response
.get();
699 if (metadata
->response().has_url())
700 response
->url
= GURL(metadata
->response().url());
702 for (int i
= 0; i
< metadata
->response().headers_size(); ++i
) {
703 const ServiceWorkerCacheHeaderMap header
= metadata
->response().headers(i
);
704 response
->headers
.insert(std::make_pair(header
.name(), header
.value()));
707 ServiceWorkerHeaderMap cached_request_headers
;
708 for (int i
= 0; i
< metadata
->request().headers_size(); ++i
) {
709 const ServiceWorkerCacheHeaderMap header
= metadata
->request().headers(i
);
710 cached_request_headers
[header
.name()] = header
.value();
713 if (!VaryMatches(match_context
->request
->headers
, cached_request_headers
,
714 response
->headers
)) {
715 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeNotFound
,
716 scoped_ptr
<ServiceWorkerResponse
>(),
717 scoped_ptr
<storage::BlobDataHandle
>());
721 if (match_context
->entry
->GetDataSize(INDEX_RESPONSE_BODY
) == 0) {
722 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeOK
,
723 match_context
->response
.Pass(),
724 scoped_ptr
<storage::BlobDataHandle
>());
728 // Stream the response body into a blob.
729 if (!match_context
->blob_storage_context
) {
730 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
731 scoped_ptr
<ServiceWorkerResponse
>(),
732 scoped_ptr
<storage::BlobDataHandle
>());
736 response
->blob_uuid
= base::GenerateGUID();
738 match_context
->blob_data
= new storage::BlobData(response
->blob_uuid
);
739 match_context
->response_body_buffer
= new net::IOBufferWithSize(kBufferSize
);
741 disk_cache::Entry
* tmp_entry_ptr
= match_context
->entry
;
742 net::IOBufferWithSize
* response_body_buffer
=
743 match_context
->response_body_buffer
.get();
745 net::CompletionCallback read_callback
= base::Bind(
746 &ServiceWorkerCache::MatchDidReadResponseBodyData
,
747 weak_ptr_factory_
.GetWeakPtr(), base::Passed(match_context
.Pass()));
750 tmp_entry_ptr
->ReadData(INDEX_RESPONSE_BODY
, 0, response_body_buffer
,
751 response_body_buffer
->size(), read_callback
);
753 if (read_rv
!= net::ERR_IO_PENDING
)
754 read_callback
.Run(read_rv
);
757 void ServiceWorkerCache::MatchDidReadResponseBodyData(
758 scoped_ptr
<MatchContext
> match_context
,
760 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
761 tracked_objects::ScopedTracker
tracking_profile(
762 FROM_HERE_WITH_EXPLICIT_FUNCTION(
763 "422516 ServiceWorkerCache::MatchDidReadResponseBodyData"));
766 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
767 scoped_ptr
<ServiceWorkerResponse
>(),
768 scoped_ptr
<storage::BlobDataHandle
>());
773 match_context
->response
->blob_uuid
= match_context
->blob_data
->uuid();
774 match_context
->response
->blob_size
= match_context
->total_bytes_read
;
775 MatchDoneWithBody(match_context
.Pass());
779 // TODO(jkarlin): This copying of the the entire cache response into memory is
780 // awful. Create a new interface around SimpleCache that provides access the
781 // data directly from the file. See bug http://crbug.com/403493.
782 match_context
->blob_data
->AppendData(
783 match_context
->response_body_buffer
->data(), rv
);
784 match_context
->total_bytes_read
+= rv
;
785 int total_bytes_read
= match_context
->total_bytes_read
;
787 // Grab some pointers before passing match_context in bind.
788 net::IOBufferWithSize
* buffer
= match_context
->response_body_buffer
.get();
789 disk_cache::Entry
* tmp_entry_ptr
= match_context
->entry
;
791 net::CompletionCallback read_callback
= base::Bind(
792 &ServiceWorkerCache::MatchDidReadResponseBodyData
,
793 weak_ptr_factory_
.GetWeakPtr(), base::Passed(match_context
.Pass()));
795 int read_rv
= tmp_entry_ptr
->ReadData(INDEX_RESPONSE_BODY
, total_bytes_read
,
796 buffer
, buffer
->size(), read_callback
);
798 if (read_rv
!= net::ERR_IO_PENDING
)
799 read_callback
.Run(read_rv
);
802 void ServiceWorkerCache::MatchDoneWithBody(
803 scoped_ptr
<MatchContext
> match_context
) {
804 if (!match_context
->blob_storage_context
) {
805 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
806 scoped_ptr
<ServiceWorkerResponse
>(),
807 scoped_ptr
<storage::BlobDataHandle
>());
811 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle(
812 match_context
->blob_storage_context
->AddFinishedBlob(
813 match_context
->blob_data
.get()));
815 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeOK
,
816 match_context
->response
.Pass(),
817 blob_data_handle
.Pass());
820 void ServiceWorkerCache::PutImpl(scoped_ptr
<PutContext
> put_context
) {
821 if (backend_state_
!= BACKEND_OPEN
) {
822 put_context
->callback
.Run(ErrorTypeStorage
,
823 scoped_ptr
<ServiceWorkerResponse
>(),
824 scoped_ptr
<storage::BlobDataHandle
>());
828 scoped_ptr
<ServiceWorkerFetchRequest
> request_copy(
829 new ServiceWorkerFetchRequest(*put_context
->request
));
831 Delete(request_copy
.Pass(), base::Bind(&ServiceWorkerCache::PutDidDelete
,
832 weak_ptr_factory_
.GetWeakPtr(),
833 base::Passed(put_context
.Pass())));
836 void ServiceWorkerCache::PutDidDelete(scoped_ptr
<PutContext
> put_context
,
837 ErrorType delete_error
) {
838 if (backend_state_
!= BACKEND_OPEN
) {
839 put_context
->callback
.Run(ErrorTypeStorage
,
840 scoped_ptr
<ServiceWorkerResponse
>(),
841 scoped_ptr
<storage::BlobDataHandle
>());
845 disk_cache::Entry
** entry_ptr
= &put_context
->cache_entry
;
846 ServiceWorkerFetchRequest
* request_ptr
= put_context
->request
.get();
847 disk_cache::Backend
* backend_ptr
= backend_
.get();
849 net::CompletionCallback create_entry_callback
= base::Bind(
850 &ServiceWorkerCache::PutDidCreateEntry
, weak_ptr_factory_
.GetWeakPtr(),
851 base::Passed(put_context
.Pass()));
853 int create_rv
= backend_ptr
->CreateEntry(
854 request_ptr
->url
.spec(), entry_ptr
, create_entry_callback
);
856 if (create_rv
!= net::ERR_IO_PENDING
)
857 create_entry_callback
.Run(create_rv
);
860 void ServiceWorkerCache::PutDidCreateEntry(scoped_ptr
<PutContext
> put_context
,
863 put_context
->callback
.Run(ServiceWorkerCache::ErrorTypeExists
,
864 scoped_ptr
<ServiceWorkerResponse
>(),
865 scoped_ptr
<storage::BlobDataHandle
>());
869 DCHECK(put_context
->cache_entry
);
871 ServiceWorkerCacheMetadata metadata
;
872 ServiceWorkerCacheRequest
* request_metadata
= metadata
.mutable_request();
873 request_metadata
->set_method(put_context
->request
->method
);
874 for (ServiceWorkerHeaderMap::const_iterator it
=
875 put_context
->request
->headers
.begin();
876 it
!= put_context
->request
->headers
.end();
878 ServiceWorkerCacheHeaderMap
* header_map
= request_metadata
->add_headers();
879 header_map
->set_name(it
->first
);
880 header_map
->set_value(it
->second
);
883 ServiceWorkerCacheResponse
* response_metadata
= metadata
.mutable_response();
884 response_metadata
->set_status_code(put_context
->response
->status_code
);
885 response_metadata
->set_status_text(put_context
->response
->status_text
);
886 response_metadata
->set_response_type(
887 WebResponseTypeToProtoResponseType(put_context
->response
->response_type
));
888 response_metadata
->set_url(put_context
->response
->url
.spec());
889 for (ServiceWorkerHeaderMap::const_iterator it
=
890 put_context
->response
->headers
.begin();
891 it
!= put_context
->response
->headers
.end();
893 ServiceWorkerCacheHeaderMap
* header_map
= response_metadata
->add_headers();
894 header_map
->set_name(it
->first
);
895 header_map
->set_value(it
->second
);
898 scoped_ptr
<std::string
> serialized(new std::string());
899 if (!metadata
.SerializeToString(serialized
.get())) {
900 put_context
->callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
901 scoped_ptr
<ServiceWorkerResponse
>(),
902 scoped_ptr
<storage::BlobDataHandle
>());
906 scoped_refptr
<net::StringIOBuffer
> buffer(
907 new net::StringIOBuffer(serialized
.Pass()));
909 // Get a temporary copy of the entry pointer before passing it in base::Bind.
910 disk_cache::Entry
* tmp_entry_ptr
= put_context
->cache_entry
;
912 net::CompletionCallback write_headers_callback
= base::Bind(
913 &ServiceWorkerCache::PutDidWriteHeaders
, weak_ptr_factory_
.GetWeakPtr(),
914 base::Passed(put_context
.Pass()), buffer
->size());
916 rv
= tmp_entry_ptr
->WriteData(INDEX_HEADERS
,
920 write_headers_callback
,
921 true /* truncate */);
923 if (rv
!= net::ERR_IO_PENDING
)
924 write_headers_callback
.Run(rv
);
927 void ServiceWorkerCache::PutDidWriteHeaders(scoped_ptr
<PutContext
> put_context
,
930 if (rv
!= expected_bytes
) {
931 put_context
->cache_entry
->Doom();
932 put_context
->callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
933 scoped_ptr
<ServiceWorkerResponse
>(),
934 scoped_ptr
<storage::BlobDataHandle
>());
938 // The metadata is written, now for the response content. The data is streamed
939 // from the blob into the cache entry.
941 if (put_context
->response
->blob_uuid
.empty()) {
942 if (put_context
->quota_manager_proxy
.get()) {
943 put_context
->quota_manager_proxy
->NotifyStorageModified(
944 storage::QuotaClient::kServiceWorkerCache
,
946 storage::kStorageTypeTemporary
,
947 put_context
->cache_entry
->GetDataSize(INDEX_HEADERS
));
950 put_context
->callback
.Run(ServiceWorkerCache::ErrorTypeOK
,
951 put_context
->response
.Pass(),
952 scoped_ptr
<storage::BlobDataHandle
>());
956 DCHECK(put_context
->blob_data_handle
);
958 disk_cache::ScopedEntryPtr
entry(put_context
->cache_entry
);
959 put_context
->cache_entry
= NULL
;
960 scoped_ptr
<BlobReader
> reader(new BlobReader());
961 BlobReader
* reader_ptr
= reader
.get();
963 // Grab some pointers before passing put_context in Bind.
964 net::URLRequestContext
* request_context
= put_context
->request_context
;
965 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
=
966 put_context
->blob_data_handle
.Pass();
968 reader_ptr
->StreamBlobToCache(
969 entry
.Pass(), request_context
, blob_data_handle
.Pass(),
970 base::Bind(&ServiceWorkerCache::PutDidWriteBlobToCache
,
971 weak_ptr_factory_
.GetWeakPtr(),
972 base::Passed(put_context
.Pass()),
973 base::Passed(reader
.Pass())));
976 void ServiceWorkerCache::PutDidWriteBlobToCache(
977 scoped_ptr
<PutContext
> put_context
,
978 scoped_ptr
<BlobReader
> blob_reader
,
979 disk_cache::ScopedEntryPtr entry
,
982 put_context
->cache_entry
= entry
.release();
985 put_context
->cache_entry
->Doom();
986 put_context
->callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
987 scoped_ptr
<ServiceWorkerResponse
>(),
988 scoped_ptr
<storage::BlobDataHandle
>());
992 if (put_context
->quota_manager_proxy
.get()) {
993 put_context
->quota_manager_proxy
->NotifyStorageModified(
994 storage::QuotaClient::kServiceWorkerCache
,
996 storage::kStorageTypeTemporary
,
997 put_context
->cache_entry
->GetDataSize(INDEX_HEADERS
) +
998 put_context
->cache_entry
->GetDataSize(INDEX_RESPONSE_BODY
));
1001 put_context
->callback
.Run(ServiceWorkerCache::ErrorTypeOK
,
1002 put_context
->response
.Pass(),
1003 put_context
->out_blob_data_handle
.Pass());
1006 void ServiceWorkerCache::DeleteDidOpenEntry(
1008 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
1009 const ServiceWorkerCache::ErrorCallback
& callback
,
1010 scoped_ptr
<disk_cache::Entry
*> entry_ptr
,
1011 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
1013 if (rv
!= net::OK
) {
1014 callback
.Run(ServiceWorkerCache::ErrorTypeNotFound
);
1019 disk_cache::ScopedEntryPtr
entry(*entry_ptr
);
1021 if (quota_manager_proxy
.get()) {
1022 quota_manager_proxy
->NotifyStorageModified(
1023 storage::QuotaClient::kServiceWorkerCache
, origin
,
1024 storage::kStorageTypeTemporary
,
1025 -1 * (entry
->GetDataSize(INDEX_HEADERS
) +
1026 entry
->GetDataSize(INDEX_RESPONSE_BODY
)));
1030 callback
.Run(ServiceWorkerCache::ErrorTypeOK
);
1033 void ServiceWorkerCache::KeysDidOpenNextEntry(
1034 scoped_ptr
<KeysContext
> keys_context
,
1036 if (rv
== net::ERR_FAILED
) {
1037 DCHECK(!keys_context
->enumerated_entry
);
1038 // Enumeration is complete, extract the requests from the entries.
1039 Entries::iterator iter
= keys_context
->entries
.begin();
1040 KeysProcessNextEntry(keys_context
.Pass(), iter
);
1045 keys_context
->original_callback
.Run(ErrorTypeStorage
,
1046 scoped_ptr
<Requests
>());
1050 if (backend_state_
!= BACKEND_OPEN
) {
1051 keys_context
->original_callback
.Run(ErrorTypeNotFound
,
1052 scoped_ptr
<Requests
>());
1057 keys_context
->entries
.push_back(keys_context
->enumerated_entry
);
1058 keys_context
->enumerated_entry
= NULL
;
1060 // Enumerate the next entry.
1061 disk_cache::Backend::Iterator
& iterator
= *keys_context
->backend_iterator
;
1062 disk_cache::Entry
** enumerated_entry
= &keys_context
->enumerated_entry
;
1063 net::CompletionCallback open_entry_callback
= base::Bind(
1064 &ServiceWorkerCache::KeysDidOpenNextEntry
, weak_ptr_factory_
.GetWeakPtr(),
1065 base::Passed(keys_context
.Pass()));
1067 rv
= iterator
.OpenNextEntry(enumerated_entry
, open_entry_callback
);
1069 if (rv
!= net::ERR_IO_PENDING
)
1070 open_entry_callback
.Run(rv
);
1073 void ServiceWorkerCache::KeysProcessNextEntry(
1074 scoped_ptr
<KeysContext
> keys_context
,
1075 const Entries::iterator
& iter
) {
1076 if (iter
== keys_context
->entries
.end()) {
1077 // All done. Return all of the keys.
1078 keys_context
->original_callback
.Run(ErrorTypeOK
,
1079 keys_context
->out_keys
.Pass());
1083 ReadMetadata(*iter
, base::Bind(&ServiceWorkerCache::KeysDidReadMetadata
,
1084 weak_ptr_factory_
.GetWeakPtr(),
1085 base::Passed(keys_context
.Pass()), iter
));
1088 void ServiceWorkerCache::KeysDidReadMetadata(
1089 scoped_ptr
<KeysContext
> keys_context
,
1090 const Entries::iterator
& iter
,
1091 scoped_ptr
<ServiceWorkerCacheMetadata
> metadata
) {
1092 disk_cache::Entry
* entry
= *iter
;
1095 keys_context
->out_keys
->push_back(
1096 ServiceWorkerFetchRequest(GURL(entry
->GetKey()),
1097 metadata
->request().method(),
1098 ServiceWorkerHeaderMap(),
1102 ServiceWorkerHeaderMap
& req_headers
=
1103 keys_context
->out_keys
->back().headers
;
1105 for (int i
= 0; i
< metadata
->request().headers_size(); ++i
) {
1106 const ServiceWorkerCacheHeaderMap header
= metadata
->request().headers(i
);
1107 req_headers
.insert(std::make_pair(header
.name(), header
.value()));
1113 KeysProcessNextEntry(keys_context
.Pass(), iter
+ 1);
1116 void ServiceWorkerCache::CloseImpl(const base::Closure
& callback
) {
1117 DCHECK(backend_state_
== BACKEND_CLOSED
);
1122 void ServiceWorkerCache::CreateBackend(const ErrorCallback
& callback
) {
1125 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
1126 net::CacheType cache_type
= memory_only_
? net::MEMORY_CACHE
: net::APP_CACHE
;
1128 scoped_ptr
<ScopedBackendPtr
> backend_ptr(new ScopedBackendPtr());
1130 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
1131 ScopedBackendPtr
* backend
= backend_ptr
.get();
1133 net::CompletionCallback create_cache_callback
=
1134 base::Bind(&ServiceWorkerCache::CreateBackendDidCreate
,
1135 weak_ptr_factory_
.GetWeakPtr(), callback
,
1136 base::Passed(backend_ptr
.Pass()));
1138 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
1139 // has for disk caches.
1140 int rv
= disk_cache::CreateCacheBackend(
1142 net::CACHE_BACKEND_SIMPLE
,
1146 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE
).get(),
1149 create_cache_callback
);
1150 if (rv
!= net::ERR_IO_PENDING
)
1151 create_cache_callback
.Run(rv
);
1154 void ServiceWorkerCache::CreateBackendDidCreate(
1155 const ServiceWorkerCache::ErrorCallback
& callback
,
1156 scoped_ptr
<ScopedBackendPtr
> backend_ptr
,
1158 if (rv
!= net::OK
) {
1159 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
);
1163 backend_
= backend_ptr
->Pass();
1164 callback
.Run(ServiceWorkerCache::ErrorTypeOK
);
1167 void ServiceWorkerCache::InitBackend(const base::Closure
& callback
) {
1168 DCHECK(backend_state_
== BACKEND_UNINITIALIZED
);
1169 init_callbacks_
.push_back(callback
);
1171 // If this isn't the first call to Init then return as the initialization
1172 // has already started.
1173 if (init_callbacks_
.size() > 1u)
1176 CreateBackend(base::Bind(&ServiceWorkerCache::InitDone
,
1177 weak_ptr_factory_
.GetWeakPtr()));
1180 void ServiceWorkerCache::InitDone(ErrorType error
) {
1181 backend_state_
= (error
== ErrorTypeOK
&& backend_
&&
1182 backend_state_
== BACKEND_UNINITIALIZED
)
1185 for (std::vector
<base::Closure
>::iterator it
= init_callbacks_
.begin();
1186 it
!= init_callbacks_
.end(); ++it
) {
1189 init_callbacks_
.clear();
1192 void ServiceWorkerCache::DecPendingOps() {
1193 DCHECK(pending_ops_
> 0);
1195 if (pending_ops_
== 0 && !ops_complete_callback_
.is_null()) {
1196 ops_complete_callback_
.Run();
1197 ops_complete_callback_
.Reset();
1201 void ServiceWorkerCache::PendingErrorCallback(const ErrorCallback
& callback
,
1203 callback
.Run(error
);
1207 void ServiceWorkerCache::PendingResponseCallback(
1208 const ResponseCallback
& callback
,
1210 scoped_ptr
<ServiceWorkerResponse
> response
,
1211 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
) {
1212 callback
.Run(error
, response
.Pass(), blob_data_handle
.Pass());
1216 void ServiceWorkerCache::PendingRequestsCallback(
1217 const RequestsCallback
& callback
,
1219 scoped_ptr
<Requests
> requests
) {
1220 callback
.Run(error
, requests
.Pass());
1224 } // namespace content