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(), 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
,
153 if (rv
!= buffer
->size()) {
154 callback
.Run(scoped_ptr
<ServiceWorkerCacheMetadata
>());
158 scoped_ptr
<ServiceWorkerCacheMetadata
> metadata(
159 new ServiceWorkerCacheMetadata());
161 if (!metadata
->ParseFromArray(buffer
->data(), buffer
->size())) {
162 callback
.Run(scoped_ptr
<ServiceWorkerCacheMetadata
>());
166 callback
.Run(metadata
.Pass());
171 // Streams data from a blob and writes it to a given disk_cache::Entry.
172 class ServiceWorkerCache::BlobReader
: public net::URLRequest::Delegate
{
174 typedef base::Callback
<void(disk_cache::ScopedEntryPtr
, bool)>
175 EntryAndBoolCallback
;
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
) {
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
{
201 void OnAuthRequired(net::URLRequest
* request
,
202 net::AuthChallengeInfo
* auth_info
) override
{
205 void OnCertificateRequested(
206 net::URLRequest
* request
,
207 net::SSLCertRequestInfo
* cert_request_info
) override
{
210 void OnSSLCertificateError(net::URLRequest
* request
,
211 const net::SSLInfo
& ssl_info
,
212 bool fatal
) override
{
215 void OnBeforeNetworkStart(net::URLRequest
* request
, bool* defer
) override
{
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);
232 virtual void ReadFromBlob() {
235 blob_request_
->Read(buffer_
.get(), buffer_
->size(), &bytes_read
);
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);
251 if (bytes_read
== 0) {
252 callback_
.Run(entry_
.Pass(), true);
256 net::CompletionCallback cache_write_callback
=
257 base::Bind(&BlobReader::DidWriteDataToEntry
,
258 weak_ptr_factory_
.GetWeakPtr(),
261 int rv
= entry_
->WriteData(INDEX_RESPONSE_BODY
,
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);
277 cache_entry_offset_
+= rv
;
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
) {}
298 for (size_t i
= 0, max
= entries
.size(); i
< max
; ++i
)
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.
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
),
328 total_bytes_read(0) {}
336 scoped_ptr
<ServiceWorkerFetchRequest
> request
;
337 ServiceWorkerCache::ResponseCallback original_callback
;
338 base::WeakPtr
<storage::BlobStorageContext
> blob_storage_context
;
339 disk_cache::Entry
* entry
;
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
{
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
)
363 request(request
.Pass()),
364 response(response
.Pass()),
365 blob_data_handle(blob_data_handle
.Pass()),
367 request_context(request_context
),
368 quota_manager_proxy(quota_manager_proxy
),
372 cache_entry
->Close();
375 // Input parameters to the Put function.
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
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
);
395 scoped_refptr
<ServiceWorkerCache
> ServiceWorkerCache::CreateMemoryCache(
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
,
408 scoped_refptr
<ServiceWorkerCache
> ServiceWorkerCache::CreatePersistentCache(
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
) {
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
>());
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
>());
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
);
474 void ServiceWorkerCache::Match(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
475 const ResponseCallback
& callback
) {
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
));
488 pending_callback
.Run(ErrorTypeStorage
,
489 scoped_ptr
<ServiceWorkerResponse
>(),
490 scoped_ptr
<storage::BlobDataHandle
>());
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
) {
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
));
527 pending_callback
.Run(ErrorTypeStorage
);
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
) {
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
));
563 pending_callback
.Run(ErrorTypeStorage
, scoped_ptr
<Requests
>());
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
);
612 int64
ServiceWorkerCache::MemoryBackedSize() const {
613 if (backend_state_
!= BACKEND_OPEN
|| !memory_only_
)
616 scoped_ptr
<disk_cache::Backend::Iterator
> backend_iter
=
617 backend_
->CreateIterator();
618 disk_cache::Entry
* entry
= nullptr;
622 std::vector
<disk_cache::Entry
*> entries
;
624 while ((rv
= backend_iter
->OpenNextEntry(
625 &entry
, base::Bind(NotReachedCompletionCallback
))) == net::OK
) {
626 entries
.push_back(entry
); // Open the entries without mutating them.
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
);
640 ServiceWorkerCache::ServiceWorkerCache(
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
)
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()),
654 weak_ptr_factory_(this) {
657 void ServiceWorkerCache::MatchDidOpenEntry(
658 scoped_ptr
<MatchContext
> match_context
,
661 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeNotFound
,
662 scoped_ptr
<ServiceWorkerResponse
>(),
663 scoped_ptr
<storage::BlobDataHandle
>());
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
) {
682 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
683 scoped_ptr
<ServiceWorkerResponse
>(),
684 scoped_ptr
<storage::BlobDataHandle
>());
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
>());
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
>());
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
>());
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()));
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
,
758 match_context
->original_callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
759 scoped_ptr
<ServiceWorkerResponse
>(),
760 scoped_ptr
<storage::BlobDataHandle
>());
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());
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
>());
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
>());
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
>());
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
,
855 put_context
->callback
.Run(ServiceWorkerCache::ErrorTypeExists
,
856 scoped_ptr
<ServiceWorkerResponse
>(),
857 scoped_ptr
<storage::BlobDataHandle
>());
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();
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();
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
>());
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
,
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
,
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
>());
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
,
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
>());
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
,
974 put_context
->cache_entry
= entry
.release();
977 put_context
->cache_entry
->Doom();
978 put_context
->callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
979 scoped_ptr
<ServiceWorkerResponse
>(),
980 scoped_ptr
<storage::BlobDataHandle
>());
984 if (put_context
->quota_manager_proxy
.get()) {
985 put_context
->quota_manager_proxy
->NotifyStorageModified(
986 storage::QuotaClient::kServiceWorkerCache
,
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(
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
,
1005 if (rv
!= net::OK
) {
1006 callback
.Run(ServiceWorkerCache::ErrorTypeNotFound
);
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
)));
1022 callback
.Run(ServiceWorkerCache::ErrorTypeOK
);
1025 void ServiceWorkerCache::KeysDidOpenNextEntry(
1026 scoped_ptr
<KeysContext
> keys_context
,
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
);
1037 keys_context
->original_callback
.Run(ErrorTypeStorage
,
1038 scoped_ptr
<Requests
>());
1042 if (backend_state_
!= BACKEND_OPEN
) {
1043 keys_context
->original_callback
.Run(ErrorTypeNotFound
,
1044 scoped_ptr
<Requests
>());
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());
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
;
1087 keys_context
->out_keys
->push_back(
1088 ServiceWorkerFetchRequest(GURL(entry
->GetKey()),
1089 metadata
->request().method(),
1090 ServiceWorkerHeaderMap(),
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()));
1105 KeysProcessNextEntry(keys_context
.Pass(), iter
+ 1);
1108 void ServiceWorkerCache::CloseImpl(const base::Closure
& callback
) {
1109 DCHECK(backend_state_
== BACKEND_CLOSED
);
1114 void ServiceWorkerCache::CreateBackend(const ErrorCallback
& callback
) {
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(
1134 net::CACHE_BACKEND_SIMPLE
,
1138 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE
).get(),
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
,
1150 if (rv
!= net::OK
) {
1151 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
);
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)
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
)
1177 for (std::vector
<base::Closure
>::iterator it
= init_callbacks_
.begin();
1178 it
!= init_callbacks_
.end(); ++it
) {
1181 init_callbacks_
.clear();
1184 void ServiceWorkerCache::DecPendingOps() {
1185 DCHECK(pending_ops_
> 0);
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
,
1195 callback
.Run(error
);
1199 void ServiceWorkerCache::PendingResponseCallback(
1200 const ResponseCallback
& callback
,
1202 scoped_ptr
<ServiceWorkerResponse
> response
,
1203 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
) {
1204 callback
.Run(error
, response
.Pass(), blob_data_handle
.Pass());
1208 void ServiceWorkerCache::PendingRequestsCallback(
1209 const RequestsCallback
& callback
,
1211 scoped_ptr
<Requests
> requests
) {
1212 callback
.Run(error
, requests
.Pass());
1216 } // namespace content