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/cache_storage/cache_storage_cache.h"
9 #include "base/barrier_closure.h"
10 #include "base/files/file_path.h"
11 #include "base/guid.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/string_split.h"
14 #include "base/strings/string_util.h"
15 #include "content/browser/cache_storage/cache_storage.pb.h"
16 #include "content/browser/cache_storage/cache_storage_blob_to_disk_cache.h"
17 #include "content/browser/cache_storage/cache_storage_scheduler.h"
18 #include "content/public/browser/browser_thread.h"
19 #include "content/public/common/referrer.h"
20 #include "net/base/completion_callback.h"
21 #include "net/base/io_buffer.h"
22 #include "net/base/net_errors.h"
23 #include "net/disk_cache/disk_cache.h"
24 #include "net/url_request/url_request_context_getter.h"
25 #include "storage/browser/blob/blob_data_builder.h"
26 #include "storage/browser/blob/blob_data_handle.h"
27 #include "storage/browser/blob/blob_storage_context.h"
28 #include "storage/browser/blob/blob_url_request_job_factory.h"
29 #include "storage/browser/quota/quota_manager_proxy.h"
30 #include "third_party/WebKit/public/platform/WebServiceWorkerResponseType.h"
36 // This class ensures that the cache and the entry have a lifetime as long as
37 // the blob that is created to contain them.
38 class CacheStorageCacheDataHandle
39 : public storage::BlobDataBuilder::DataHandle
{
41 CacheStorageCacheDataHandle(const scoped_refptr
<CacheStorageCache
>& cache
,
42 disk_cache::ScopedEntryPtr entry
)
43 : cache_(cache
), entry_(entry
.Pass()) {}
46 ~CacheStorageCacheDataHandle() override
{}
48 scoped_refptr
<CacheStorageCache
> cache_
;
49 disk_cache::ScopedEntryPtr entry_
;
51 DISALLOW_COPY_AND_ASSIGN(CacheStorageCacheDataHandle
);
54 typedef base::Callback
<void(scoped_ptr
<CacheMetadata
>)> MetadataCallback
;
56 enum EntryIndex
{ INDEX_HEADERS
= 0, INDEX_RESPONSE_BODY
};
58 // The maximum size of an individual cache. Ultimately cache size is controlled
60 const int kMaxCacheBytes
= 512 * 1024 * 1024;
62 void NotReachedCompletionCallback(int rv
) {
66 blink::WebServiceWorkerResponseType
ProtoResponseTypeToWebResponseType(
67 CacheResponse::ResponseType response_type
) {
68 switch (response_type
) {
69 case CacheResponse::BASIC_TYPE
:
70 return blink::WebServiceWorkerResponseTypeBasic
;
71 case CacheResponse::CORS_TYPE
:
72 return blink::WebServiceWorkerResponseTypeCORS
;
73 case CacheResponse::DEFAULT_TYPE
:
74 return blink::WebServiceWorkerResponseTypeDefault
;
75 case CacheResponse::ERROR_TYPE
:
76 return blink::WebServiceWorkerResponseTypeError
;
77 case CacheResponse::OPAQUE_TYPE
:
78 return blink::WebServiceWorkerResponseTypeOpaque
;
81 return blink::WebServiceWorkerResponseTypeOpaque
;
84 CacheResponse::ResponseType
WebResponseTypeToProtoResponseType(
85 blink::WebServiceWorkerResponseType response_type
) {
86 switch (response_type
) {
87 case blink::WebServiceWorkerResponseTypeBasic
:
88 return CacheResponse::BASIC_TYPE
;
89 case blink::WebServiceWorkerResponseTypeCORS
:
90 return CacheResponse::CORS_TYPE
;
91 case blink::WebServiceWorkerResponseTypeDefault
:
92 return CacheResponse::DEFAULT_TYPE
;
93 case blink::WebServiceWorkerResponseTypeError
:
94 return CacheResponse::ERROR_TYPE
;
95 case blink::WebServiceWorkerResponseTypeOpaque
:
96 return CacheResponse::OPAQUE_TYPE
;
98 // TODO(horo): Remove this when WebServiceWorkerResponseTypeOpaqueRedirect
99 // will be added in blink's WebServiceWorkerResponseType.h.
103 return CacheResponse::OPAQUE_TYPE
;
106 // Copy headers out of a cache entry and into a protobuf. The callback is
107 // guaranteed to be run.
108 void ReadMetadata(disk_cache::Entry
* entry
, const MetadataCallback
& callback
);
109 void ReadMetadataDidReadMetadata(
110 disk_cache::Entry
* entry
,
111 const MetadataCallback
& callback
,
112 const scoped_refptr
<net::IOBufferWithSize
>& buffer
,
115 bool VaryMatches(const ServiceWorkerHeaderMap
& request
,
116 const ServiceWorkerHeaderMap
& cached_request
,
117 const ServiceWorkerHeaderMap
& response
) {
118 ServiceWorkerHeaderMap::const_iterator vary_iter
= response
.find("vary");
119 if (vary_iter
== response
.end())
122 for (const std::string
& trimmed
:
123 base::SplitString(vary_iter
->second
, ",",
124 base::TRIM_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
)) {
128 ServiceWorkerHeaderMap::const_iterator request_iter
= request
.find(trimmed
);
129 ServiceWorkerHeaderMap::const_iterator cached_request_iter
=
130 cached_request
.find(trimmed
);
132 // If the header exists in one but not the other, no match.
133 if ((request_iter
== request
.end()) !=
134 (cached_request_iter
== cached_request
.end()))
137 // If the header exists in one, it exists in both. Verify that the values
139 if (request_iter
!= request
.end() &&
140 request_iter
->second
!= cached_request_iter
->second
)
147 void ReadMetadata(disk_cache::Entry
* entry
, const MetadataCallback
& callback
) {
150 scoped_refptr
<net::IOBufferWithSize
> buffer(
151 new net::IOBufferWithSize(entry
->GetDataSize(INDEX_HEADERS
)));
153 net::CompletionCallback read_header_callback
=
154 base::Bind(ReadMetadataDidReadMetadata
, entry
, callback
, buffer
);
156 int read_rv
= entry
->ReadData(INDEX_HEADERS
, 0, buffer
.get(), buffer
->size(),
157 read_header_callback
);
159 if (read_rv
!= net::ERR_IO_PENDING
)
160 read_header_callback
.Run(read_rv
);
163 void ReadMetadataDidReadMetadata(
164 disk_cache::Entry
* entry
,
165 const MetadataCallback
& callback
,
166 const scoped_refptr
<net::IOBufferWithSize
>& buffer
,
168 if (rv
!= buffer
->size()) {
169 callback
.Run(scoped_ptr
<CacheMetadata
>());
173 scoped_ptr
<CacheMetadata
> metadata(new CacheMetadata());
175 if (!metadata
->ParseFromArray(buffer
->data(), buffer
->size())) {
176 callback
.Run(scoped_ptr
<CacheMetadata
>());
180 callback
.Run(metadata
.Pass());
185 // The state needed to pass between CacheStorageCache::Keys callbacks.
186 struct CacheStorageCache::KeysContext
{
187 explicit KeysContext(const CacheStorageCache::RequestsCallback
& callback
)
188 : original_callback(callback
),
189 out_keys(new CacheStorageCache::Requests()),
190 enumerated_entry(NULL
) {}
193 for (size_t i
= 0, max
= entries
.size(); i
< max
; ++i
)
195 if (enumerated_entry
)
196 enumerated_entry
->Close();
199 // The callback passed to the Keys() function.
200 CacheStorageCache::RequestsCallback original_callback
;
202 // The vector of open entries in the backend.
205 // The output of the Keys function.
206 scoped_ptr
<CacheStorageCache::Requests
> out_keys
;
208 // Used for enumerating cache entries.
209 scoped_ptr
<disk_cache::Backend::Iterator
> backend_iterator
;
210 disk_cache::Entry
* enumerated_entry
;
213 DISALLOW_COPY_AND_ASSIGN(KeysContext
);
216 // The state needed to pass between CacheStorageCache::Put callbacks.
217 struct CacheStorageCache::PutContext
{
220 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
221 scoped_ptr
<ServiceWorkerResponse
> response
,
222 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
223 const CacheStorageCache::ErrorCallback
& callback
,
224 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
225 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
)
227 request(request
.Pass()),
228 response(response
.Pass()),
229 blob_data_handle(blob_data_handle
.Pass()),
231 request_context_getter(request_context_getter
),
232 quota_manager_proxy(quota_manager_proxy
) {}
234 // Input parameters to the Put function.
236 scoped_ptr
<ServiceWorkerFetchRequest
> request
;
237 scoped_ptr
<ServiceWorkerResponse
> response
;
238 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
;
239 CacheStorageCache::ErrorCallback callback
;
240 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter
;
241 scoped_refptr
<storage::QuotaManagerProxy
> quota_manager_proxy
;
242 disk_cache::ScopedEntryPtr cache_entry
;
245 DISALLOW_COPY_AND_ASSIGN(PutContext
);
249 scoped_refptr
<CacheStorageCache
> CacheStorageCache::CreateMemoryCache(
251 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
252 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
253 base::WeakPtr
<storage::BlobStorageContext
> blob_context
) {
254 return make_scoped_refptr(
255 new CacheStorageCache(origin
, base::FilePath(), request_context_getter
,
256 quota_manager_proxy
, blob_context
));
260 scoped_refptr
<CacheStorageCache
> CacheStorageCache::CreatePersistentCache(
262 const base::FilePath
& path
,
263 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
264 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
265 base::WeakPtr
<storage::BlobStorageContext
> blob_context
) {
266 return make_scoped_refptr(new CacheStorageCache(
267 origin
, path
, request_context_getter
, quota_manager_proxy
, blob_context
));
270 CacheStorageCache::~CacheStorageCache() {
273 base::WeakPtr
<CacheStorageCache
> CacheStorageCache::AsWeakPtr() {
274 return weak_ptr_factory_
.GetWeakPtr();
277 void CacheStorageCache::Match(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
278 const ResponseCallback
& callback
) {
279 if (!LazyInitialize()) {
280 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
,
281 scoped_ptr
<ServiceWorkerResponse
>(),
282 scoped_ptr
<storage::BlobDataHandle
>());
286 ResponseCallback pending_callback
=
287 base::Bind(&CacheStorageCache::PendingResponseCallback
,
288 weak_ptr_factory_
.GetWeakPtr(), callback
);
289 scheduler_
->ScheduleOperation(
290 base::Bind(&CacheStorageCache::MatchImpl
, weak_ptr_factory_
.GetWeakPtr(),
291 base::Passed(request
.Pass()), pending_callback
));
294 void CacheStorageCache::BatchOperation(
295 const std::vector
<CacheStorageBatchOperation
>& operations
,
296 const ErrorCallback
& callback
) {
297 if (!LazyInitialize()) {
298 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
302 scoped_ptr
<ErrorCallback
> callback_copy(new ErrorCallback(callback
));
303 ErrorCallback
* callback_ptr
= callback_copy
.get();
304 base::Closure barrier_closure
= base::BarrierClosure(
305 operations
.size(), base::Bind(&CacheStorageCache::BatchDidAllOperations
,
306 this, base::Passed(callback_copy
.Pass())));
307 ErrorCallback completion_callback
=
308 base::Bind(&CacheStorageCache::BatchDidOneOperation
, this,
309 barrier_closure
, callback_ptr
);
311 for (const auto& operation
: operations
) {
312 switch (operation
.operation_type
) {
313 case CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT
:
314 Put(operation
, completion_callback
);
316 case CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE
:
317 DCHECK_EQ(1u, operations
.size());
318 Delete(operation
, completion_callback
);
320 case CACHE_STORAGE_CACHE_OPERATION_TYPE_UNDEFINED
:
322 // TODO(nhiroki): This should return "TypeError".
323 // http://crbug.com/425505
324 completion_callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
330 void CacheStorageCache::BatchDidOneOperation(
331 const base::Closure
& barrier_closure
,
332 ErrorCallback
* callback
,
333 CacheStorageError error
) {
334 if (callback
->is_null() || error
== CACHE_STORAGE_OK
) {
335 barrier_closure
.Run();
338 callback
->Run(error
);
339 callback
->Reset(); // Only call the callback once.
341 barrier_closure
.Run();
344 void CacheStorageCache::BatchDidAllOperations(
345 scoped_ptr
<ErrorCallback
> callback
) {
346 if (callback
->is_null())
348 callback
->Run(CACHE_STORAGE_OK
);
351 void CacheStorageCache::Keys(const RequestsCallback
& callback
) {
352 if (!LazyInitialize()) {
353 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
, scoped_ptr
<Requests
>());
357 RequestsCallback pending_callback
=
358 base::Bind(&CacheStorageCache::PendingRequestsCallback
,
359 weak_ptr_factory_
.GetWeakPtr(), callback
);
360 scheduler_
->ScheduleOperation(base::Bind(&CacheStorageCache::KeysImpl
,
361 weak_ptr_factory_
.GetWeakPtr(),
365 void CacheStorageCache::Close(const base::Closure
& callback
) {
366 DCHECK_NE(BACKEND_CLOSED
, backend_state_
)
367 << "Was CacheStorageCache::Close() called twice?";
369 base::Closure pending_callback
=
370 base::Bind(&CacheStorageCache::PendingClosure
,
371 weak_ptr_factory_
.GetWeakPtr(), callback
);
373 scheduler_
->ScheduleOperation(base::Bind(&CacheStorageCache::CloseImpl
,
374 weak_ptr_factory_
.GetWeakPtr(),
378 int64
CacheStorageCache::MemoryBackedSize() const {
379 if (backend_state_
!= BACKEND_OPEN
|| !memory_only_
)
382 scoped_ptr
<disk_cache::Backend::Iterator
> backend_iter
=
383 backend_
->CreateIterator();
384 disk_cache::Entry
* entry
= nullptr;
388 std::vector
<disk_cache::Entry
*> entries
;
390 while ((rv
= backend_iter
->OpenNextEntry(
391 &entry
, base::Bind(NotReachedCompletionCallback
))) == net::OK
) {
392 entries
.push_back(entry
); // Open the entries without mutating them.
394 DCHECK_NE(net::ERR_IO_PENDING
, rv
)
395 << "Memory cache operations should be synchronous.";
397 for (disk_cache::Entry
* entry
: entries
) {
398 sum
+= entry
->GetDataSize(INDEX_HEADERS
) +
399 entry
->GetDataSize(INDEX_RESPONSE_BODY
);
406 CacheStorageCache::CacheStorageCache(
408 const base::FilePath
& path
,
409 const scoped_refptr
<net::URLRequestContextGetter
>& request_context_getter
,
410 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
411 base::WeakPtr
<storage::BlobStorageContext
> blob_context
)
414 request_context_getter_(request_context_getter
),
415 quota_manager_proxy_(quota_manager_proxy
),
416 blob_storage_context_(blob_context
),
417 backend_state_(BACKEND_UNINITIALIZED
),
418 scheduler_(new CacheStorageScheduler()),
419 initializing_(false),
420 memory_only_(path
.empty()),
421 weak_ptr_factory_(this) {
424 bool CacheStorageCache::LazyInitialize() {
425 switch (backend_state_
) {
426 case BACKEND_UNINITIALIZED
:
439 void CacheStorageCache::MatchImpl(scoped_ptr
<ServiceWorkerFetchRequest
> request
,
440 const ResponseCallback
& callback
) {
441 DCHECK_NE(BACKEND_UNINITIALIZED
, backend_state_
);
442 if (backend_state_
!= BACKEND_OPEN
) {
443 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
,
444 scoped_ptr
<ServiceWorkerResponse
>(),
445 scoped_ptr
<storage::BlobDataHandle
>());
449 scoped_ptr
<disk_cache::Entry
*> scoped_entry_ptr(new disk_cache::Entry
*());
450 disk_cache::Entry
** entry_ptr
= scoped_entry_ptr
.get();
451 ServiceWorkerFetchRequest
* request_ptr
= request
.get();
453 net::CompletionCallback open_entry_callback
=
454 base::Bind(&CacheStorageCache::MatchDidOpenEntry
,
455 weak_ptr_factory_
.GetWeakPtr(), base::Passed(request
.Pass()),
456 callback
, base::Passed(scoped_entry_ptr
.Pass()));
458 int rv
= backend_
->OpenEntry(request_ptr
->url
.spec(), entry_ptr
,
459 open_entry_callback
);
460 if (rv
!= net::ERR_IO_PENDING
)
461 open_entry_callback
.Run(rv
);
464 void CacheStorageCache::MatchDidOpenEntry(
465 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
466 const ResponseCallback
& callback
,
467 scoped_ptr
<disk_cache::Entry
*> entry_ptr
,
470 callback
.Run(CACHE_STORAGE_ERROR_NOT_FOUND
,
471 scoped_ptr
<ServiceWorkerResponse
>(),
472 scoped_ptr
<storage::BlobDataHandle
>());
475 disk_cache::ScopedEntryPtr
entry(*entry_ptr
);
477 MetadataCallback headers_callback
= base::Bind(
478 &CacheStorageCache::MatchDidReadMetadata
, weak_ptr_factory_
.GetWeakPtr(),
479 base::Passed(request
.Pass()), callback
, base::Passed(entry
.Pass()));
481 ReadMetadata(*entry_ptr
, headers_callback
);
484 void CacheStorageCache::MatchDidReadMetadata(
485 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
486 const ResponseCallback
& callback
,
487 disk_cache::ScopedEntryPtr entry
,
488 scoped_ptr
<CacheMetadata
> metadata
) {
490 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
,
491 scoped_ptr
<ServiceWorkerResponse
>(),
492 scoped_ptr
<storage::BlobDataHandle
>());
496 scoped_ptr
<ServiceWorkerResponse
> response(new ServiceWorkerResponse(
497 request
->url
, metadata
->response().status_code(),
498 metadata
->response().status_text(),
499 ProtoResponseTypeToWebResponseType(metadata
->response().response_type()),
500 ServiceWorkerHeaderMap(), "", 0, GURL(),
501 blink::WebServiceWorkerResponseErrorUnknown
));
503 if (metadata
->response().has_url())
504 response
->url
= GURL(metadata
->response().url());
506 for (int i
= 0; i
< metadata
->response().headers_size(); ++i
) {
507 const CacheHeaderMap header
= metadata
->response().headers(i
);
508 DCHECK_EQ(std::string::npos
, header
.name().find('\0'));
509 DCHECK_EQ(std::string::npos
, header
.value().find('\0'));
510 response
->headers
.insert(std::make_pair(header
.name(), header
.value()));
513 ServiceWorkerHeaderMap cached_request_headers
;
514 for (int i
= 0; i
< metadata
->request().headers_size(); ++i
) {
515 const CacheHeaderMap header
= metadata
->request().headers(i
);
516 DCHECK_EQ(std::string::npos
, header
.name().find('\0'));
517 DCHECK_EQ(std::string::npos
, header
.value().find('\0'));
518 cached_request_headers
[header
.name()] = header
.value();
521 if (!VaryMatches(request
->headers
, cached_request_headers
,
522 response
->headers
)) {
523 callback
.Run(CACHE_STORAGE_ERROR_NOT_FOUND
,
524 scoped_ptr
<ServiceWorkerResponse
>(),
525 scoped_ptr
<storage::BlobDataHandle
>());
529 if (entry
->GetDataSize(INDEX_RESPONSE_BODY
) == 0) {
530 callback
.Run(CACHE_STORAGE_OK
, response
.Pass(),
531 scoped_ptr
<storage::BlobDataHandle
>());
535 if (!blob_storage_context_
) {
536 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
,
537 scoped_ptr
<ServiceWorkerResponse
>(),
538 scoped_ptr
<storage::BlobDataHandle
>());
542 // Create a blob with the response body data.
543 response
->blob_size
= entry
->GetDataSize(INDEX_RESPONSE_BODY
);
544 response
->blob_uuid
= base::GenerateGUID();
545 storage::BlobDataBuilder
blob_data(response
->blob_uuid
);
547 disk_cache::Entry
* temp_entry
= entry
.get();
548 blob_data
.AppendDiskCacheEntry(
549 new CacheStorageCacheDataHandle(this, entry
.Pass()), temp_entry
,
550 INDEX_RESPONSE_BODY
);
551 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle(
552 blob_storage_context_
->AddFinishedBlob(&blob_data
));
553 callback
.Run(CACHE_STORAGE_OK
, response
.Pass(), blob_data_handle
.Pass());
556 void CacheStorageCache::Put(const CacheStorageBatchOperation
& operation
,
557 const ErrorCallback
& callback
) {
558 DCHECK(BACKEND_OPEN
== backend_state_
|| initializing_
);
559 DCHECK_EQ(CACHE_STORAGE_CACHE_OPERATION_TYPE_PUT
, operation
.operation_type
);
561 scoped_ptr
<ServiceWorkerFetchRequest
> request(new ServiceWorkerFetchRequest(
562 operation
.request
.url
, operation
.request
.method
,
563 operation
.request
.headers
, operation
.request
.referrer
,
564 operation
.request
.is_reload
));
566 // We don't support streaming for cache.
567 DCHECK(operation
.response
.stream_url
.is_empty());
568 scoped_ptr
<ServiceWorkerResponse
> response(new ServiceWorkerResponse(
569 operation
.response
.url
, operation
.response
.status_code
,
570 operation
.response
.status_text
, operation
.response
.response_type
,
571 operation
.response
.headers
, operation
.response
.blob_uuid
,
572 operation
.response
.blob_size
, operation
.response
.stream_url
,
573 operation
.response
.error
));
575 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
;
577 if (!response
->blob_uuid
.empty()) {
578 if (!blob_storage_context_
) {
579 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
583 blob_storage_context_
->GetBlobDataFromUUID(response
->blob_uuid
);
584 if (!blob_data_handle
) {
585 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
590 ErrorCallback pending_callback
=
591 base::Bind(&CacheStorageCache::PendingErrorCallback
,
592 weak_ptr_factory_
.GetWeakPtr(), callback
);
594 scoped_ptr
<PutContext
> put_context(new PutContext(
595 origin_
, request
.Pass(), response
.Pass(), blob_data_handle
.Pass(),
596 pending_callback
, request_context_getter_
, quota_manager_proxy_
));
598 scheduler_
->ScheduleOperation(base::Bind(&CacheStorageCache::PutImpl
,
599 weak_ptr_factory_
.GetWeakPtr(),
600 base::Passed(put_context
.Pass())));
603 void CacheStorageCache::PutImpl(scoped_ptr
<PutContext
> put_context
) {
604 DCHECK_NE(BACKEND_UNINITIALIZED
, backend_state_
);
605 if (backend_state_
!= BACKEND_OPEN
) {
606 put_context
->callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
610 scoped_ptr
<ServiceWorkerFetchRequest
> request_copy(
611 new ServiceWorkerFetchRequest(*put_context
->request
));
613 DeleteImpl(request_copy
.Pass(), base::Bind(&CacheStorageCache::PutDidDelete
,
614 weak_ptr_factory_
.GetWeakPtr(),
615 base::Passed(put_context
.Pass())));
618 void CacheStorageCache::PutDidDelete(scoped_ptr
<PutContext
> put_context
,
619 CacheStorageError delete_error
) {
620 if (backend_state_
!= BACKEND_OPEN
) {
621 put_context
->callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
625 scoped_ptr
<disk_cache::Entry
*> scoped_entry_ptr(new disk_cache::Entry
*());
626 disk_cache::Entry
** entry_ptr
= scoped_entry_ptr
.get();
627 ServiceWorkerFetchRequest
* request_ptr
= put_context
->request
.get();
628 disk_cache::Backend
* backend_ptr
= backend_
.get();
630 net::CompletionCallback create_entry_callback
= base::Bind(
631 &CacheStorageCache::PutDidCreateEntry
, weak_ptr_factory_
.GetWeakPtr(),
632 base::Passed(scoped_entry_ptr
.Pass()), base::Passed(put_context
.Pass()));
634 int create_rv
= backend_ptr
->CreateEntry(request_ptr
->url
.spec(), entry_ptr
,
635 create_entry_callback
);
637 if (create_rv
!= net::ERR_IO_PENDING
)
638 create_entry_callback
.Run(create_rv
);
641 void CacheStorageCache::PutDidCreateEntry(
642 scoped_ptr
<disk_cache::Entry
*> entry_ptr
,
643 scoped_ptr
<PutContext
> put_context
,
646 put_context
->callback
.Run(CACHE_STORAGE_ERROR_EXISTS
);
649 put_context
->cache_entry
.reset(*entry_ptr
);
651 CacheMetadata metadata
;
652 CacheRequest
* request_metadata
= metadata
.mutable_request();
653 request_metadata
->set_method(put_context
->request
->method
);
654 for (ServiceWorkerHeaderMap::const_iterator it
=
655 put_context
->request
->headers
.begin();
656 it
!= put_context
->request
->headers
.end(); ++it
) {
657 DCHECK_EQ(std::string::npos
, it
->first
.find('\0'));
658 DCHECK_EQ(std::string::npos
, it
->second
.find('\0'));
659 CacheHeaderMap
* header_map
= request_metadata
->add_headers();
660 header_map
->set_name(it
->first
);
661 header_map
->set_value(it
->second
);
664 CacheResponse
* response_metadata
= metadata
.mutable_response();
665 response_metadata
->set_status_code(put_context
->response
->status_code
);
666 response_metadata
->set_status_text(put_context
->response
->status_text
);
667 response_metadata
->set_response_type(
668 WebResponseTypeToProtoResponseType(put_context
->response
->response_type
));
669 response_metadata
->set_url(put_context
->response
->url
.spec());
670 for (ServiceWorkerHeaderMap::const_iterator it
=
671 put_context
->response
->headers
.begin();
672 it
!= put_context
->response
->headers
.end(); ++it
) {
673 DCHECK_EQ(std::string::npos
, it
->first
.find('\0'));
674 DCHECK_EQ(std::string::npos
, it
->second
.find('\0'));
675 CacheHeaderMap
* header_map
= response_metadata
->add_headers();
676 header_map
->set_name(it
->first
);
677 header_map
->set_value(it
->second
);
680 scoped_ptr
<std::string
> serialized(new std::string());
681 if (!metadata
.SerializeToString(serialized
.get())) {
682 put_context
->callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
686 scoped_refptr
<net::StringIOBuffer
> buffer(
687 new net::StringIOBuffer(serialized
.Pass()));
689 // Get a temporary copy of the entry pointer before passing it in base::Bind.
690 disk_cache::Entry
* temp_entry_ptr
= put_context
->cache_entry
.get();
692 net::CompletionCallback write_headers_callback
= base::Bind(
693 &CacheStorageCache::PutDidWriteHeaders
, weak_ptr_factory_
.GetWeakPtr(),
694 base::Passed(put_context
.Pass()), buffer
->size());
696 rv
= temp_entry_ptr
->WriteData(INDEX_HEADERS
, 0 /* offset */, buffer
.get(),
697 buffer
->size(), write_headers_callback
,
698 true /* truncate */);
700 if (rv
!= net::ERR_IO_PENDING
)
701 write_headers_callback
.Run(rv
);
704 void CacheStorageCache::PutDidWriteHeaders(scoped_ptr
<PutContext
> put_context
,
707 if (rv
!= expected_bytes
) {
708 put_context
->cache_entry
->Doom();
709 put_context
->callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
713 // The metadata is written, now for the response content. The data is streamed
714 // from the blob into the cache entry.
716 if (put_context
->response
->blob_uuid
.empty()) {
717 if (put_context
->quota_manager_proxy
.get()) {
718 put_context
->quota_manager_proxy
->NotifyStorageModified(
719 storage::QuotaClient::kServiceWorkerCache
, put_context
->origin
,
720 storage::kStorageTypeTemporary
,
721 put_context
->cache_entry
->GetDataSize(INDEX_HEADERS
));
724 put_context
->callback
.Run(CACHE_STORAGE_OK
);
728 DCHECK(put_context
->blob_data_handle
);
730 disk_cache::ScopedEntryPtr
entry(put_context
->cache_entry
.Pass());
731 put_context
->cache_entry
= NULL
;
733 CacheStorageBlobToDiskCache
* blob_to_cache
=
734 new CacheStorageBlobToDiskCache();
735 BlobToDiskCacheIDMap::KeyType blob_to_cache_key
=
736 active_blob_to_disk_cache_writers_
.Add(blob_to_cache
);
738 // Grab some pointers before passing put_context in Bind.
739 scoped_refptr
<net::URLRequestContextGetter
> request_context_getter
=
740 put_context
->request_context_getter
;
741 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
=
742 put_context
->blob_data_handle
.Pass();
744 blob_to_cache
->StreamBlobToCache(
745 entry
.Pass(), INDEX_RESPONSE_BODY
, request_context_getter
,
746 blob_data_handle
.Pass(),
747 base::Bind(&CacheStorageCache::PutDidWriteBlobToCache
,
748 weak_ptr_factory_
.GetWeakPtr(),
749 base::Passed(put_context
.Pass()), blob_to_cache_key
));
752 void CacheStorageCache::PutDidWriteBlobToCache(
753 scoped_ptr
<PutContext
> put_context
,
754 BlobToDiskCacheIDMap::KeyType blob_to_cache_key
,
755 disk_cache::ScopedEntryPtr entry
,
758 put_context
->cache_entry
= entry
.Pass();
760 active_blob_to_disk_cache_writers_
.Remove(blob_to_cache_key
);
763 put_context
->cache_entry
->Doom();
764 put_context
->callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
768 if (put_context
->quota_manager_proxy
.get()) {
769 put_context
->quota_manager_proxy
->NotifyStorageModified(
770 storage::QuotaClient::kServiceWorkerCache
, put_context
->origin
,
771 storage::kStorageTypeTemporary
,
772 put_context
->cache_entry
->GetDataSize(INDEX_HEADERS
) +
773 put_context
->cache_entry
->GetDataSize(INDEX_RESPONSE_BODY
));
776 put_context
->callback
.Run(CACHE_STORAGE_OK
);
779 void CacheStorageCache::Delete(const CacheStorageBatchOperation
& operation
,
780 const ErrorCallback
& callback
) {
781 DCHECK(BACKEND_OPEN
== backend_state_
|| initializing_
);
782 DCHECK_EQ(CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE
,
783 operation
.operation_type
);
785 scoped_ptr
<ServiceWorkerFetchRequest
> request(new ServiceWorkerFetchRequest(
786 operation
.request
.url
, operation
.request
.method
,
787 operation
.request
.headers
, operation
.request
.referrer
,
788 operation
.request
.is_reload
));
790 ErrorCallback pending_callback
=
791 base::Bind(&CacheStorageCache::PendingErrorCallback
,
792 weak_ptr_factory_
.GetWeakPtr(), callback
);
793 scheduler_
->ScheduleOperation(
794 base::Bind(&CacheStorageCache::DeleteImpl
, weak_ptr_factory_
.GetWeakPtr(),
795 base::Passed(request
.Pass()), pending_callback
));
798 void CacheStorageCache::DeleteImpl(
799 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
800 const ErrorCallback
& callback
) {
801 DCHECK_NE(BACKEND_UNINITIALIZED
, backend_state_
);
802 if (backend_state_
!= BACKEND_OPEN
) {
803 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
806 scoped_ptr
<disk_cache::Entry
*> entry(new disk_cache::Entry
*);
808 disk_cache::Entry
** entry_ptr
= entry
.get();
810 ServiceWorkerFetchRequest
* request_ptr
= request
.get();
812 net::CompletionCallback open_entry_callback
= base::Bind(
813 &CacheStorageCache::DeleteDidOpenEntry
, weak_ptr_factory_
.GetWeakPtr(),
814 origin_
, base::Passed(request
.Pass()), callback
,
815 base::Passed(entry
.Pass()), quota_manager_proxy_
);
817 int rv
= backend_
->OpenEntry(request_ptr
->url
.spec(), entry_ptr
,
818 open_entry_callback
);
819 if (rv
!= net::ERR_IO_PENDING
)
820 open_entry_callback
.Run(rv
);
823 void CacheStorageCache::DeleteDidOpenEntry(
825 scoped_ptr
<ServiceWorkerFetchRequest
> request
,
826 const CacheStorageCache::ErrorCallback
& callback
,
827 scoped_ptr
<disk_cache::Entry
*> entry_ptr
,
828 const scoped_refptr
<storage::QuotaManagerProxy
>& quota_manager_proxy
,
831 callback
.Run(CACHE_STORAGE_ERROR_NOT_FOUND
);
836 disk_cache::ScopedEntryPtr
entry(*entry_ptr
);
838 if (quota_manager_proxy
.get()) {
839 quota_manager_proxy
->NotifyStorageModified(
840 storage::QuotaClient::kServiceWorkerCache
, origin
,
841 storage::kStorageTypeTemporary
,
842 -1 * (entry
->GetDataSize(INDEX_HEADERS
) +
843 entry
->GetDataSize(INDEX_RESPONSE_BODY
)));
847 callback
.Run(CACHE_STORAGE_OK
);
850 void CacheStorageCache::KeysImpl(const RequestsCallback
& callback
) {
851 DCHECK_NE(BACKEND_UNINITIALIZED
, backend_state_
);
852 if (backend_state_
!= BACKEND_OPEN
) {
853 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
, scoped_ptr
<Requests
>());
857 // 1. Iterate through all of the entries, open them, and add them to a vector.
858 // 2. For each open entry:
859 // 2.1. Read the headers into a protobuf.
860 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
861 // 2.3. Push the response into a vector of requests to be returned.
862 // 3. Return the vector of requests (keys).
864 // The entries have to be loaded into a vector first because enumeration loops
865 // forever if you read data from a cache entry while enumerating.
867 scoped_ptr
<KeysContext
> keys_context(new KeysContext(callback
));
869 keys_context
->backend_iterator
= backend_
->CreateIterator();
870 disk_cache::Backend::Iterator
& iterator
= *keys_context
->backend_iterator
;
871 disk_cache::Entry
** enumerated_entry
= &keys_context
->enumerated_entry
;
873 net::CompletionCallback open_entry_callback
= base::Bind(
874 &CacheStorageCache::KeysDidOpenNextEntry
, weak_ptr_factory_
.GetWeakPtr(),
875 base::Passed(keys_context
.Pass()));
877 int rv
= iterator
.OpenNextEntry(enumerated_entry
, open_entry_callback
);
879 if (rv
!= net::ERR_IO_PENDING
)
880 open_entry_callback
.Run(rv
);
883 void CacheStorageCache::KeysDidOpenNextEntry(
884 scoped_ptr
<KeysContext
> keys_context
,
886 if (rv
== net::ERR_FAILED
) {
887 DCHECK(!keys_context
->enumerated_entry
);
888 // Enumeration is complete, extract the requests from the entries.
889 Entries::iterator iter
= keys_context
->entries
.begin();
890 KeysProcessNextEntry(keys_context
.Pass(), iter
);
895 keys_context
->original_callback
.Run(CACHE_STORAGE_ERROR_STORAGE
,
896 scoped_ptr
<Requests
>());
900 if (backend_state_
!= BACKEND_OPEN
) {
901 keys_context
->original_callback
.Run(CACHE_STORAGE_ERROR_NOT_FOUND
,
902 scoped_ptr
<Requests
>());
907 keys_context
->entries
.push_back(keys_context
->enumerated_entry
);
908 keys_context
->enumerated_entry
= NULL
;
910 // Enumerate the next entry.
911 disk_cache::Backend::Iterator
& iterator
= *keys_context
->backend_iterator
;
912 disk_cache::Entry
** enumerated_entry
= &keys_context
->enumerated_entry
;
913 net::CompletionCallback open_entry_callback
= base::Bind(
914 &CacheStorageCache::KeysDidOpenNextEntry
, weak_ptr_factory_
.GetWeakPtr(),
915 base::Passed(keys_context
.Pass()));
917 rv
= iterator
.OpenNextEntry(enumerated_entry
, open_entry_callback
);
919 if (rv
!= net::ERR_IO_PENDING
)
920 open_entry_callback
.Run(rv
);
923 void CacheStorageCache::KeysProcessNextEntry(
924 scoped_ptr
<KeysContext
> keys_context
,
925 const Entries::iterator
& iter
) {
926 if (iter
== keys_context
->entries
.end()) {
927 // All done. Return all of the keys.
928 keys_context
->original_callback
.Run(CACHE_STORAGE_OK
,
929 keys_context
->out_keys
.Pass());
933 ReadMetadata(*iter
, base::Bind(&CacheStorageCache::KeysDidReadMetadata
,
934 weak_ptr_factory_
.GetWeakPtr(),
935 base::Passed(keys_context
.Pass()), iter
));
938 void CacheStorageCache::KeysDidReadMetadata(
939 scoped_ptr
<KeysContext
> keys_context
,
940 const Entries::iterator
& iter
,
941 scoped_ptr
<CacheMetadata
> metadata
) {
942 disk_cache::Entry
* entry
= *iter
;
945 keys_context
->out_keys
->push_back(ServiceWorkerFetchRequest(
946 GURL(entry
->GetKey()), metadata
->request().method(),
947 ServiceWorkerHeaderMap(), Referrer(), false));
949 ServiceWorkerHeaderMap
& req_headers
=
950 keys_context
->out_keys
->back().headers
;
952 for (int i
= 0; i
< metadata
->request().headers_size(); ++i
) {
953 const CacheHeaderMap header
= metadata
->request().headers(i
);
954 DCHECK_EQ(std::string::npos
, header
.name().find('\0'));
955 DCHECK_EQ(std::string::npos
, header
.value().find('\0'));
956 req_headers
.insert(std::make_pair(header
.name(), header
.value()));
962 KeysProcessNextEntry(keys_context
.Pass(), iter
+ 1);
965 void CacheStorageCache::CloseImpl(const base::Closure
& callback
) {
966 DCHECK_NE(BACKEND_CLOSED
, backend_state_
);
968 backend_state_
= BACKEND_CLOSED
;
973 void CacheStorageCache::CreateBackend(const ErrorCallback
& callback
) {
976 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
977 net::CacheType cache_type
= memory_only_
? net::MEMORY_CACHE
: net::APP_CACHE
;
979 scoped_ptr
<ScopedBackendPtr
> backend_ptr(new ScopedBackendPtr());
981 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
982 ScopedBackendPtr
* backend
= backend_ptr
.get();
984 net::CompletionCallback create_cache_callback
=
985 base::Bind(&CacheStorageCache::CreateBackendDidCreate
,
986 weak_ptr_factory_
.GetWeakPtr(), callback
,
987 base::Passed(backend_ptr
.Pass()));
989 // TODO(jkarlin): Use the cache task runner that ServiceWorkerCacheCore
990 // has for disk caches.
991 int rv
= disk_cache::CreateCacheBackend(
992 cache_type
, net::CACHE_BACKEND_SIMPLE
, path_
, kMaxCacheBytes
,
994 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE
).get(),
995 NULL
, backend
, create_cache_callback
);
996 if (rv
!= net::ERR_IO_PENDING
)
997 create_cache_callback
.Run(rv
);
1000 void CacheStorageCache::CreateBackendDidCreate(
1001 const CacheStorageCache::ErrorCallback
& callback
,
1002 scoped_ptr
<ScopedBackendPtr
> backend_ptr
,
1004 if (rv
!= net::OK
) {
1005 callback
.Run(CACHE_STORAGE_ERROR_STORAGE
);
1009 backend_
= backend_ptr
->Pass();
1010 callback
.Run(CACHE_STORAGE_OK
);
1013 void CacheStorageCache::InitBackend() {
1014 DCHECK_EQ(BACKEND_UNINITIALIZED
, backend_state_
);
1019 DCHECK(!scheduler_
->ScheduledOperations());
1020 initializing_
= true;
1022 scheduler_
->ScheduleOperation(base::Bind(
1023 &CacheStorageCache::CreateBackend
, weak_ptr_factory_
.GetWeakPtr(),
1024 base::Bind(&CacheStorageCache::InitDone
,
1025 weak_ptr_factory_
.GetWeakPtr())));
1028 void CacheStorageCache::InitDone(CacheStorageError error
) {
1029 initializing_
= false;
1030 backend_state_
= (error
== CACHE_STORAGE_OK
&& backend_
&&
1031 backend_state_
== BACKEND_UNINITIALIZED
)
1035 UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.InitBackendResult", error
,
1036 CACHE_STORAGE_ERROR_LAST
+ 1);
1038 scheduler_
->CompleteOperationAndRunNext();
1041 void CacheStorageCache::PendingClosure(const base::Closure
& callback
) {
1042 base::WeakPtr
<CacheStorageCache
> cache
= weak_ptr_factory_
.GetWeakPtr();
1046 scheduler_
->CompleteOperationAndRunNext();
1049 void CacheStorageCache::PendingErrorCallback(const ErrorCallback
& callback
,
1050 CacheStorageError error
) {
1051 base::WeakPtr
<CacheStorageCache
> cache
= weak_ptr_factory_
.GetWeakPtr();
1053 callback
.Run(error
);
1055 scheduler_
->CompleteOperationAndRunNext();
1058 void CacheStorageCache::PendingResponseCallback(
1059 const ResponseCallback
& callback
,
1060 CacheStorageError error
,
1061 scoped_ptr
<ServiceWorkerResponse
> response
,
1062 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
) {
1063 base::WeakPtr
<CacheStorageCache
> cache
= weak_ptr_factory_
.GetWeakPtr();
1065 callback
.Run(error
, response
.Pass(), blob_data_handle
.Pass());
1067 scheduler_
->CompleteOperationAndRunNext();
1070 void CacheStorageCache::PendingRequestsCallback(
1071 const RequestsCallback
& callback
,
1072 CacheStorageError error
,
1073 scoped_ptr
<Requests
> requests
) {
1074 base::WeakPtr
<CacheStorageCache
> cache
= weak_ptr_factory_
.GetWeakPtr();
1076 callback
.Run(error
, requests
.Pass());
1078 scheduler_
->CompleteOperationAndRunNext();
1081 } // namespace content