Add ICU message format support
[chromium-blink-merge.git] / content / browser / cache_storage / cache_storage_cache.cc
blobcf98e5007e766edb459f3638331454e433be7520
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"
7 #include <string>
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"
32 namespace content {
34 namespace {
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 {
40 public:
41 CacheStorageCacheDataHandle(const scoped_refptr<CacheStorageCache>& cache,
42 disk_cache::ScopedEntryPtr entry)
43 : cache_(cache), entry_(entry.Pass()) {}
45 private:
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
59 // per-origin.
60 const int kMaxCacheBytes = 512 * 1024 * 1024;
62 void NotReachedCompletionCallback(int rv) {
63 NOTREACHED();
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;
80 NOTREACHED();
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;
97 default:
98 // TODO(horo): Remove this when WebServiceWorkerResponseTypeOpaqueRedirect
99 // will be added in blink's WebServiceWorkerResponseType.h.
100 NOTREACHED();
102 NOTREACHED();
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,
113 int rv);
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())
120 return true;
122 for (const std::string& trimmed :
123 base::SplitString(vary_iter->second, ",",
124 base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY)) {
125 if (trimmed == "*")
126 return false;
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()))
135 return false;
137 // If the header exists in one, it exists in both. Verify that the values
138 // are equal.
139 if (request_iter != request.end() &&
140 request_iter->second != cached_request_iter->second)
141 return false;
144 return true;
147 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback) {
148 DCHECK(entry);
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,
167 int rv) {
168 if (rv != buffer->size()) {
169 callback.Run(scoped_ptr<CacheMetadata>());
170 return;
173 scoped_ptr<CacheMetadata> metadata(new CacheMetadata());
175 if (!metadata->ParseFromArray(buffer->data(), buffer->size())) {
176 callback.Run(scoped_ptr<CacheMetadata>());
177 return;
180 callback.Run(metadata.Pass());
183 } // namespace
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) {}
192 ~KeysContext() {
193 for (size_t i = 0, max = entries.size(); i < max; ++i)
194 entries[i]->Close();
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.
203 Entries entries;
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;
212 private:
213 DISALLOW_COPY_AND_ASSIGN(KeysContext);
216 // The state needed to pass between CacheStorageCache::Put callbacks.
217 struct CacheStorageCache::PutContext {
218 PutContext(
219 const GURL& origin,
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)
226 : origin(origin),
227 request(request.Pass()),
228 response(response.Pass()),
229 blob_data_handle(blob_data_handle.Pass()),
230 callback(callback),
231 request_context_getter(request_context_getter),
232 quota_manager_proxy(quota_manager_proxy) {}
234 // Input parameters to the Put function.
235 GURL origin;
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;
244 private:
245 DISALLOW_COPY_AND_ASSIGN(PutContext);
248 // static
249 scoped_refptr<CacheStorageCache> CacheStorageCache::CreateMemoryCache(
250 const GURL& origin,
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));
259 // static
260 scoped_refptr<CacheStorageCache> CacheStorageCache::CreatePersistentCache(
261 const GURL& origin,
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>());
283 return;
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);
299 return;
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);
315 break;
316 case CACHE_STORAGE_CACHE_OPERATION_TYPE_DELETE:
317 DCHECK_EQ(1u, operations.size());
318 Delete(operation, completion_callback);
319 break;
320 case CACHE_STORAGE_CACHE_OPERATION_TYPE_UNDEFINED:
321 NOTREACHED();
322 // TODO(nhiroki): This should return "TypeError".
323 // http://crbug.com/425505
324 completion_callback.Run(CACHE_STORAGE_ERROR_STORAGE);
325 break;
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();
336 return;
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())
347 return;
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>());
354 return;
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(),
362 pending_callback));
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(),
375 pending_callback));
378 int64 CacheStorageCache::MemoryBackedSize() const {
379 if (backend_state_ != BACKEND_OPEN || !memory_only_)
380 return 0;
382 scoped_ptr<disk_cache::Backend::Iterator> backend_iter =
383 backend_->CreateIterator();
384 disk_cache::Entry* entry = nullptr;
386 int64 sum = 0;
388 std::vector<disk_cache::Entry*> entries;
389 int rv = net::OK;
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);
400 entry->Close();
403 return sum;
406 CacheStorageCache::CacheStorageCache(
407 const GURL& origin,
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)
412 : origin_(origin),
413 path_(path),
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:
427 InitBackend();
428 return true;
429 case BACKEND_CLOSED:
430 return false;
431 case BACKEND_OPEN:
432 DCHECK(backend_);
433 return true;
435 NOTREACHED();
436 return false;
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>());
446 return;
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,
468 int rv) {
469 if (rv != net::OK) {
470 callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND,
471 scoped_ptr<ServiceWorkerResponse>(),
472 scoped_ptr<storage::BlobDataHandle>());
473 return;
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) {
489 if (!metadata) {
490 callback.Run(CACHE_STORAGE_ERROR_STORAGE,
491 scoped_ptr<ServiceWorkerResponse>(),
492 scoped_ptr<storage::BlobDataHandle>());
493 return;
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>());
526 return;
529 if (entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
530 callback.Run(CACHE_STORAGE_OK, response.Pass(),
531 scoped_ptr<storage::BlobDataHandle>());
532 return;
535 if (!blob_storage_context_) {
536 callback.Run(CACHE_STORAGE_ERROR_STORAGE,
537 scoped_ptr<ServiceWorkerResponse>(),
538 scoped_ptr<storage::BlobDataHandle>());
539 return;
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);
580 return;
582 blob_data_handle =
583 blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
584 if (!blob_data_handle) {
585 callback.Run(CACHE_STORAGE_ERROR_STORAGE);
586 return;
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);
607 return;
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);
622 return;
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,
644 int rv) {
645 if (rv != net::OK) {
646 put_context->callback.Run(CACHE_STORAGE_ERROR_EXISTS);
647 return;
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);
683 return;
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,
705 int expected_bytes,
706 int rv) {
707 if (rv != expected_bytes) {
708 put_context->cache_entry->Doom();
709 put_context->callback.Run(CACHE_STORAGE_ERROR_STORAGE);
710 return;
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);
725 return;
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,
756 bool success) {
757 DCHECK(entry);
758 put_context->cache_entry = entry.Pass();
760 active_blob_to_disk_cache_writers_.Remove(blob_to_cache_key);
762 if (!success) {
763 put_context->cache_entry->Doom();
764 put_context->callback.Run(CACHE_STORAGE_ERROR_STORAGE);
765 return;
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);
804 return;
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(
824 const GURL& origin,
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,
829 int rv) {
830 if (rv != net::OK) {
831 callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND);
832 return;
835 DCHECK(entry_ptr);
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)));
846 entry->Doom();
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>());
854 return;
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,
885 int rv) {
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);
891 return;
894 if (rv < 0) {
895 keys_context->original_callback.Run(CACHE_STORAGE_ERROR_STORAGE,
896 scoped_ptr<Requests>());
897 return;
900 if (backend_state_ != BACKEND_OPEN) {
901 keys_context->original_callback.Run(CACHE_STORAGE_ERROR_NOT_FOUND,
902 scoped_ptr<Requests>());
903 return;
906 // Store the entry.
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());
930 return;
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;
944 if (metadata) {
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()));
958 } else {
959 entry->Doom();
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;
969 backend_.reset();
970 callback.Run();
973 void CacheStorageCache::CreateBackend(const ErrorCallback& callback) {
974 DCHECK(!backend_);
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,
993 false, /* force */
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,
1003 int rv) {
1004 if (rv != net::OK) {
1005 callback.Run(CACHE_STORAGE_ERROR_STORAGE);
1006 return;
1009 backend_ = backend_ptr->Pass();
1010 callback.Run(CACHE_STORAGE_OK);
1013 void CacheStorageCache::InitBackend() {
1014 DCHECK_EQ(BACKEND_UNINITIALIZED, backend_state_);
1016 if (initializing_)
1017 return;
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)
1032 ? BACKEND_OPEN
1033 : BACKEND_CLOSED;
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();
1044 callback.Run();
1045 if (cache)
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);
1054 if (cache)
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());
1066 if (cache)
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());
1077 if (cache)
1078 scheduler_->CompleteOperationAndRunNext();
1081 } // namespace content