Supervised user import: Listen for profile creation/deletion
[chromium-blink-merge.git] / content / browser / cache_storage / cache_storage_cache.cc
blob072755522fb3fedd41492639a1a425c0a120e108
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/files/file_path.h"
10 #include "base/guid.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/strings/string_util.h"
14 #include "content/browser/cache_storage/cache_storage.pb.h"
15 #include "content/browser/cache_storage/cache_storage_scheduler.h"
16 #include "content/public/browser/browser_thread.h"
17 #include "content/public/common/referrer.h"
18 #include "net/base/completion_callback.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
21 #include "net/disk_cache/disk_cache.h"
22 #include "net/url_request/url_request_context.h"
23 #include "storage/browser/blob/blob_data_builder.h"
24 #include "storage/browser/blob/blob_data_handle.h"
25 #include "storage/browser/blob/blob_storage_context.h"
26 #include "storage/browser/blob/blob_url_request_job_factory.h"
27 #include "storage/browser/quota/quota_manager_proxy.h"
28 #include "third_party/WebKit/public/platform/WebServiceWorkerResponseType.h"
30 namespace content {
32 namespace {
34 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
35 EntryBoolCallback;
36 typedef base::Callback<void(scoped_ptr<CacheMetadata>)> MetadataCallback;
38 enum EntryIndex { INDEX_HEADERS = 0, INDEX_RESPONSE_BODY };
40 // The maximum size of an individual cache. Ultimately cache size is controlled
41 // per-origin.
42 const int kMaxCacheBytes = 512 * 1024 * 1024;
44 // Buffer size for cache and blob reading/writing.
45 const int kBufferSize = 1024 * 512;
47 void NotReachedCompletionCallback(int rv) {
48 NOTREACHED();
51 blink::WebServiceWorkerResponseType ProtoResponseTypeToWebResponseType(
52 CacheResponse::ResponseType response_type) {
53 switch (response_type) {
54 case CacheResponse::BASIC_TYPE:
55 return blink::WebServiceWorkerResponseTypeBasic;
56 case CacheResponse::CORS_TYPE:
57 return blink::WebServiceWorkerResponseTypeCORS;
58 case CacheResponse::DEFAULT_TYPE:
59 return blink::WebServiceWorkerResponseTypeDefault;
60 case CacheResponse::ERROR_TYPE:
61 return blink::WebServiceWorkerResponseTypeError;
62 case CacheResponse::OPAQUE_TYPE:
63 return blink::WebServiceWorkerResponseTypeOpaque;
65 NOTREACHED();
66 return blink::WebServiceWorkerResponseTypeOpaque;
69 CacheResponse::ResponseType WebResponseTypeToProtoResponseType(
70 blink::WebServiceWorkerResponseType response_type) {
71 switch (response_type) {
72 case blink::WebServiceWorkerResponseTypeBasic:
73 return CacheResponse::BASIC_TYPE;
74 case blink::WebServiceWorkerResponseTypeCORS:
75 return CacheResponse::CORS_TYPE;
76 case blink::WebServiceWorkerResponseTypeDefault:
77 return CacheResponse::DEFAULT_TYPE;
78 case blink::WebServiceWorkerResponseTypeError:
79 return CacheResponse::ERROR_TYPE;
80 case blink::WebServiceWorkerResponseTypeOpaque:
81 return CacheResponse::OPAQUE_TYPE;
83 NOTREACHED();
84 return CacheResponse::OPAQUE_TYPE;
87 // Copy headers out of a cache entry and into a protobuf. The callback is
88 // guaranteed to be run.
89 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback);
90 void ReadMetadataDidReadMetadata(
91 disk_cache::Entry* entry,
92 const MetadataCallback& callback,
93 const scoped_refptr<net::IOBufferWithSize>& buffer,
94 int rv);
96 bool VaryMatches(const ServiceWorkerHeaderMap& request,
97 const ServiceWorkerHeaderMap& cached_request,
98 const ServiceWorkerHeaderMap& response) {
99 ServiceWorkerHeaderMap::const_iterator vary_iter = response.find("vary");
100 if (vary_iter == response.end())
101 return true;
103 std::vector<std::string> vary_keys;
104 Tokenize(vary_iter->second, ",", &vary_keys);
105 for (std::vector<std::string>::const_iterator it = vary_keys.begin();
106 it != vary_keys.end(); ++it) {
107 std::string trimmed;
108 base::TrimWhitespaceASCII(*it, base::TRIM_ALL, &trimmed);
109 if (trimmed == "*")
110 return false;
112 ServiceWorkerHeaderMap::const_iterator request_iter = request.find(trimmed);
113 ServiceWorkerHeaderMap::const_iterator cached_request_iter =
114 cached_request.find(trimmed);
116 // If the header exists in one but not the other, no match.
117 if ((request_iter == request.end()) !=
118 (cached_request_iter == cached_request.end()))
119 return false;
121 // If the header exists in one, it exists in both. Verify that the values
122 // are equal.
123 if (request_iter != request.end() &&
124 request_iter->second != cached_request_iter->second)
125 return false;
128 return true;
131 void ReadMetadata(disk_cache::Entry* entry, const MetadataCallback& callback) {
132 DCHECK(entry);
134 scoped_refptr<net::IOBufferWithSize> buffer(
135 new net::IOBufferWithSize(entry->GetDataSize(INDEX_HEADERS)));
137 net::CompletionCallback read_header_callback =
138 base::Bind(ReadMetadataDidReadMetadata, entry, callback, buffer);
140 int read_rv = entry->ReadData(INDEX_HEADERS, 0, buffer.get(), buffer->size(),
141 read_header_callback);
143 if (read_rv != net::ERR_IO_PENDING)
144 read_header_callback.Run(read_rv);
147 void ReadMetadataDidReadMetadata(
148 disk_cache::Entry* entry,
149 const MetadataCallback& callback,
150 const scoped_refptr<net::IOBufferWithSize>& buffer,
151 int rv) {
152 if (rv != buffer->size()) {
153 callback.Run(scoped_ptr<CacheMetadata>());
154 return;
157 scoped_ptr<CacheMetadata> metadata(new CacheMetadata());
159 if (!metadata->ParseFromArray(buffer->data(), buffer->size())) {
160 callback.Run(scoped_ptr<CacheMetadata>());
161 return;
164 callback.Run(metadata.Pass());
167 } // namespace
169 // Streams data from a blob and writes it to a given disk_cache::Entry.
170 class CacheStorageCache::BlobReader : public net::URLRequest::Delegate {
171 public:
172 typedef base::Callback<void(disk_cache::ScopedEntryPtr, bool)>
173 EntryAndBoolCallback;
175 BlobReader()
176 : cache_entry_offset_(0),
177 buffer_(new net::IOBufferWithSize(kBufferSize)),
178 weak_ptr_factory_(this) {}
180 // |entry| is passed to the callback once complete.
181 void StreamBlobToCache(disk_cache::ScopedEntryPtr entry,
182 net::URLRequestContext* request_context,
183 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
184 const EntryAndBoolCallback& callback) {
185 DCHECK(entry);
186 entry_ = entry.Pass();
187 callback_ = callback;
188 blob_request_ = storage::BlobProtocolHandler::CreateBlobRequest(
189 blob_data_handle.Pass(), request_context, this);
190 blob_request_->Start();
193 // net::URLRequest::Delegate overrides for reading blobs.
194 void OnReceivedRedirect(net::URLRequest* request,
195 const net::RedirectInfo& redirect_info,
196 bool* defer_redirect) override {
197 NOTREACHED();
199 void OnAuthRequired(net::URLRequest* request,
200 net::AuthChallengeInfo* auth_info) override {
201 NOTREACHED();
203 void OnCertificateRequested(
204 net::URLRequest* request,
205 net::SSLCertRequestInfo* cert_request_info) override {
206 NOTREACHED();
208 void OnSSLCertificateError(net::URLRequest* request,
209 const net::SSLInfo& ssl_info,
210 bool fatal) override {
211 NOTREACHED();
213 void OnBeforeNetworkStart(net::URLRequest* request, bool* defer) override {
214 NOTREACHED();
217 void OnResponseStarted(net::URLRequest* request) override {
218 if (!request->status().is_success()) {
219 callback_.Run(entry_.Pass(), false);
220 return;
222 ReadFromBlob();
225 virtual void ReadFromBlob() {
226 int bytes_read = 0;
227 bool done =
228 blob_request_->Read(buffer_.get(), buffer_->size(), &bytes_read);
229 if (done)
230 OnReadCompleted(blob_request_.get(), bytes_read);
233 void OnReadCompleted(net::URLRequest* request, int bytes_read) override {
234 if (!request->status().is_success()) {
235 callback_.Run(entry_.Pass(), false);
236 return;
239 if (bytes_read == 0) {
240 callback_.Run(entry_.Pass(), true);
241 return;
244 net::CompletionCallback cache_write_callback =
245 base::Bind(&BlobReader::DidWriteDataToEntry,
246 weak_ptr_factory_.GetWeakPtr(), bytes_read);
248 int rv = entry_->WriteData(INDEX_RESPONSE_BODY, cache_entry_offset_,
249 buffer_.get(), bytes_read, cache_write_callback,
250 true /* truncate */);
251 if (rv != net::ERR_IO_PENDING)
252 cache_write_callback.Run(rv);
255 void DidWriteDataToEntry(int expected_bytes, int rv) {
256 if (rv != expected_bytes) {
257 callback_.Run(entry_.Pass(), false);
258 return;
261 cache_entry_offset_ += rv;
262 ReadFromBlob();
265 private:
266 int cache_entry_offset_;
267 disk_cache::ScopedEntryPtr entry_;
268 scoped_ptr<net::URLRequest> blob_request_;
269 EntryAndBoolCallback callback_;
270 scoped_refptr<net::IOBufferWithSize> buffer_;
271 base::WeakPtrFactory<BlobReader> weak_ptr_factory_;
274 // The state needed to pass between CacheStorageCache::Keys callbacks.
275 struct CacheStorageCache::KeysContext {
276 explicit KeysContext(const CacheStorageCache::RequestsCallback& callback)
277 : original_callback(callback),
278 out_keys(new CacheStorageCache::Requests()),
279 enumerated_entry(NULL) {}
281 ~KeysContext() {
282 for (size_t i = 0, max = entries.size(); i < max; ++i)
283 entries[i]->Close();
284 if (enumerated_entry)
285 enumerated_entry->Close();
288 // The callback passed to the Keys() function.
289 CacheStorageCache::RequestsCallback original_callback;
291 // The vector of open entries in the backend.
292 Entries entries;
294 // The output of the Keys function.
295 scoped_ptr<CacheStorageCache::Requests> out_keys;
297 // Used for enumerating cache entries.
298 scoped_ptr<disk_cache::Backend::Iterator> backend_iterator;
299 disk_cache::Entry* enumerated_entry;
301 DISALLOW_COPY_AND_ASSIGN(KeysContext);
304 struct CacheStorageCache::MatchContext {
305 MatchContext(scoped_ptr<ServiceWorkerFetchRequest> request,
306 const CacheStorageCache::ResponseCallback& callback,
307 base::WeakPtr<storage::BlobStorageContext> blob_storage_context)
308 : request(request.Pass()),
309 original_callback(callback),
310 blob_storage_context(blob_storage_context),
311 entry(nullptr),
312 total_bytes_read(0) {}
314 ~MatchContext() {
315 if (entry)
316 entry->Close();
319 // Input
320 scoped_ptr<ServiceWorkerFetchRequest> request;
321 CacheStorageCache::ResponseCallback original_callback;
322 base::WeakPtr<storage::BlobStorageContext> blob_storage_context;
323 disk_cache::Entry* entry;
325 // Output
326 scoped_ptr<ServiceWorkerResponse> response;
327 scoped_ptr<storage::BlobDataBuilder> blob_data;
329 // For reading the cache entry data into a blob.
330 scoped_refptr<net::IOBufferWithSize> response_body_buffer;
331 size_t total_bytes_read;
333 DISALLOW_COPY_AND_ASSIGN(MatchContext);
336 // The state needed to pass between CacheStorageCache::Put callbacks.
337 struct CacheStorageCache::PutContext {
338 PutContext(
339 const GURL& origin,
340 scoped_ptr<ServiceWorkerFetchRequest> request,
341 scoped_ptr<ServiceWorkerResponse> response,
342 scoped_ptr<storage::BlobDataHandle> blob_data_handle,
343 const CacheStorageCache::ResponseCallback& callback,
344 net::URLRequestContext* request_context,
345 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy)
346 : origin(origin),
347 request(request.Pass()),
348 response(response.Pass()),
349 blob_data_handle(blob_data_handle.Pass()),
350 callback(callback),
351 request_context(request_context),
352 quota_manager_proxy(quota_manager_proxy),
353 cache_entry(NULL) {}
354 ~PutContext() {
355 if (cache_entry)
356 cache_entry->Close();
359 // Input parameters to the Put function.
360 GURL origin;
361 scoped_ptr<ServiceWorkerFetchRequest> request;
362 scoped_ptr<ServiceWorkerResponse> response;
363 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
364 CacheStorageCache::ResponseCallback callback;
365 net::URLRequestContext* request_context;
366 scoped_refptr<storage::QuotaManagerProxy> quota_manager_proxy;
368 // This isn't a scoped_ptr because the disk_cache needs an Entry** as input to
369 // CreateEntry.
370 disk_cache::Entry* cache_entry;
372 // The BlobDataHandle for the output ServiceWorkerResponse.
373 scoped_ptr<storage::BlobDataHandle> out_blob_data_handle;
375 DISALLOW_COPY_AND_ASSIGN(PutContext);
378 // static
379 scoped_refptr<CacheStorageCache> CacheStorageCache::CreateMemoryCache(
380 const GURL& origin,
381 net::URLRequestContext* request_context,
382 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
383 base::WeakPtr<storage::BlobStorageContext> blob_context) {
384 return make_scoped_refptr(
385 new CacheStorageCache(origin, base::FilePath(), request_context,
386 quota_manager_proxy, blob_context));
389 // static
390 scoped_refptr<CacheStorageCache> CacheStorageCache::CreatePersistentCache(
391 const GURL& origin,
392 const base::FilePath& path,
393 net::URLRequestContext* request_context,
394 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
395 base::WeakPtr<storage::BlobStorageContext> blob_context) {
396 return make_scoped_refptr(new CacheStorageCache(
397 origin, path, request_context, quota_manager_proxy, blob_context));
400 CacheStorageCache::~CacheStorageCache() {
403 base::WeakPtr<CacheStorageCache> CacheStorageCache::AsWeakPtr() {
404 return weak_ptr_factory_.GetWeakPtr();
407 void CacheStorageCache::Put(scoped_ptr<ServiceWorkerFetchRequest> request,
408 scoped_ptr<ServiceWorkerResponse> response,
409 const ResponseCallback& callback) {
410 scoped_ptr<storage::BlobDataHandle> blob_data_handle;
412 if (!response->blob_uuid.empty()) {
413 if (!blob_storage_context_) {
414 callback.Run(ERROR_TYPE_STORAGE, scoped_ptr<ServiceWorkerResponse>(),
415 scoped_ptr<storage::BlobDataHandle>());
416 return;
418 blob_data_handle =
419 blob_storage_context_->GetBlobDataFromUUID(response->blob_uuid);
420 if (!blob_data_handle) {
421 callback.Run(ERROR_TYPE_STORAGE, scoped_ptr<ServiceWorkerResponse>(),
422 scoped_ptr<storage::BlobDataHandle>());
423 return;
427 ResponseCallback pending_callback =
428 base::Bind(&CacheStorageCache::PendingResponseCallback,
429 weak_ptr_factory_.GetWeakPtr(), callback);
431 scoped_ptr<PutContext> put_context(new PutContext(
432 origin_, request.Pass(), response.Pass(), blob_data_handle.Pass(),
433 pending_callback, request_context_, quota_manager_proxy_));
435 if (put_context->blob_data_handle) {
436 // Grab another handle to the blob for the callback response.
437 put_context->out_blob_data_handle =
438 blob_storage_context_->GetBlobDataFromUUID(
439 put_context->response->blob_uuid);
442 if (backend_state_ == BACKEND_UNINITIALIZED)
443 InitBackend();
445 scheduler_->ScheduleOperation(base::Bind(&CacheStorageCache::PutImpl,
446 weak_ptr_factory_.GetWeakPtr(),
447 base::Passed(put_context.Pass())));
450 void CacheStorageCache::Match(scoped_ptr<ServiceWorkerFetchRequest> request,
451 const ResponseCallback& callback) {
452 switch (backend_state_) {
453 case BACKEND_UNINITIALIZED:
454 InitBackend();
455 break;
456 case BACKEND_CLOSED:
457 callback.Run(ERROR_TYPE_STORAGE, scoped_ptr<ServiceWorkerResponse>(),
458 scoped_ptr<storage::BlobDataHandle>());
459 return;
460 case BACKEND_OPEN:
461 DCHECK(backend_);
462 break;
465 ResponseCallback pending_callback =
466 base::Bind(&CacheStorageCache::PendingResponseCallback,
467 weak_ptr_factory_.GetWeakPtr(), callback);
468 scheduler_->ScheduleOperation(
469 base::Bind(&CacheStorageCache::MatchImpl, weak_ptr_factory_.GetWeakPtr(),
470 base::Passed(request.Pass()), pending_callback));
473 void CacheStorageCache::Delete(scoped_ptr<ServiceWorkerFetchRequest> request,
474 const ErrorCallback& callback) {
475 switch (backend_state_) {
476 case BACKEND_UNINITIALIZED:
477 InitBackend();
478 break;
479 case BACKEND_CLOSED:
480 callback.Run(ERROR_TYPE_STORAGE);
481 return;
482 case BACKEND_OPEN:
483 DCHECK(backend_);
484 break;
486 ErrorCallback pending_callback =
487 base::Bind(&CacheStorageCache::PendingErrorCallback,
488 weak_ptr_factory_.GetWeakPtr(), callback);
489 scheduler_->ScheduleOperation(
490 base::Bind(&CacheStorageCache::DeleteImpl, weak_ptr_factory_.GetWeakPtr(),
491 base::Passed(request.Pass()), pending_callback));
494 void CacheStorageCache::Keys(const RequestsCallback& callback) {
495 switch (backend_state_) {
496 case BACKEND_UNINITIALIZED:
497 InitBackend();
498 break;
499 case BACKEND_CLOSED:
500 callback.Run(ERROR_TYPE_STORAGE, scoped_ptr<Requests>());
501 return;
502 case BACKEND_OPEN:
503 DCHECK(backend_);
504 break;
507 RequestsCallback pending_callback =
508 base::Bind(&CacheStorageCache::PendingRequestsCallback,
509 weak_ptr_factory_.GetWeakPtr(), callback);
510 scheduler_->ScheduleOperation(base::Bind(&CacheStorageCache::KeysImpl,
511 weak_ptr_factory_.GetWeakPtr(),
512 pending_callback));
515 void CacheStorageCache::Close(const base::Closure& callback) {
516 DCHECK(backend_state_ != BACKEND_CLOSED)
517 << "Don't call CacheStorageCache::Close() twice.";
519 base::Closure pending_callback =
520 base::Bind(&CacheStorageCache::PendingClosure,
521 weak_ptr_factory_.GetWeakPtr(), callback);
523 scheduler_->ScheduleOperation(base::Bind(&CacheStorageCache::CloseImpl,
524 weak_ptr_factory_.GetWeakPtr(),
525 pending_callback));
528 int64 CacheStorageCache::MemoryBackedSize() const {
529 if (backend_state_ != BACKEND_OPEN || !memory_only_)
530 return 0;
532 scoped_ptr<disk_cache::Backend::Iterator> backend_iter =
533 backend_->CreateIterator();
534 disk_cache::Entry* entry = nullptr;
536 int64 sum = 0;
538 std::vector<disk_cache::Entry*> entries;
539 int rv = net::OK;
540 while ((rv = backend_iter->OpenNextEntry(
541 &entry, base::Bind(NotReachedCompletionCallback))) == net::OK) {
542 entries.push_back(entry); // Open the entries without mutating them.
544 DCHECK(rv !=
545 net::ERR_IO_PENDING); // Expect all memory ops to be synchronous.
547 for (disk_cache::Entry* entry : entries) {
548 sum += entry->GetDataSize(INDEX_HEADERS) +
549 entry->GetDataSize(INDEX_RESPONSE_BODY);
550 entry->Close();
553 return sum;
556 CacheStorageCache::CacheStorageCache(
557 const GURL& origin,
558 const base::FilePath& path,
559 net::URLRequestContext* request_context,
560 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
561 base::WeakPtr<storage::BlobStorageContext> blob_context)
562 : origin_(origin),
563 path_(path),
564 request_context_(request_context),
565 quota_manager_proxy_(quota_manager_proxy),
566 blob_storage_context_(blob_context),
567 backend_state_(BACKEND_UNINITIALIZED),
568 scheduler_(new CacheStorageScheduler()),
569 initializing_(false),
570 memory_only_(path.empty()),
571 weak_ptr_factory_(this) {
574 void CacheStorageCache::MatchImpl(scoped_ptr<ServiceWorkerFetchRequest> request,
575 const ResponseCallback& callback) {
576 DCHECK(backend_state_ != BACKEND_UNINITIALIZED);
577 if (backend_state_ != BACKEND_OPEN) {
578 callback.Run(ERROR_TYPE_STORAGE, scoped_ptr<ServiceWorkerResponse>(),
579 scoped_ptr<storage::BlobDataHandle>());
580 return;
583 scoped_ptr<MatchContext> match_context(
584 new MatchContext(request.Pass(), callback, blob_storage_context_));
586 disk_cache::Entry** entry_ptr = &match_context->entry;
587 ServiceWorkerFetchRequest* request_ptr = match_context->request.get();
589 net::CompletionCallback open_entry_callback = base::Bind(
590 &CacheStorageCache::MatchDidOpenEntry, weak_ptr_factory_.GetWeakPtr(),
591 base::Passed(match_context.Pass()));
593 int rv = backend_->OpenEntry(request_ptr->url.spec(), entry_ptr,
594 open_entry_callback);
595 if (rv != net::ERR_IO_PENDING)
596 open_entry_callback.Run(rv);
599 void CacheStorageCache::MatchDidOpenEntry(
600 scoped_ptr<MatchContext> match_context,
601 int rv) {
602 if (rv != net::OK) {
603 match_context->original_callback.Run(
604 CacheStorageCache::ERROR_TYPE_NOT_FOUND,
605 scoped_ptr<ServiceWorkerResponse>(),
606 scoped_ptr<storage::BlobDataHandle>());
607 return;
610 // Copy the entry pointer before passing it in base::Bind.
611 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
612 DCHECK(tmp_entry_ptr);
614 MetadataCallback headers_callback = base::Bind(
615 &CacheStorageCache::MatchDidReadMetadata, weak_ptr_factory_.GetWeakPtr(),
616 base::Passed(match_context.Pass()));
618 ReadMetadata(tmp_entry_ptr, headers_callback);
621 void CacheStorageCache::MatchDidReadMetadata(
622 scoped_ptr<MatchContext> match_context,
623 scoped_ptr<CacheMetadata> metadata) {
624 if (!metadata) {
625 match_context->original_callback.Run(CacheStorageCache::ERROR_TYPE_STORAGE,
626 scoped_ptr<ServiceWorkerResponse>(),
627 scoped_ptr<storage::BlobDataHandle>());
628 return;
631 match_context->response.reset(new ServiceWorkerResponse(
632 match_context->request->url, metadata->response().status_code(),
633 metadata->response().status_text(),
634 ProtoResponseTypeToWebResponseType(metadata->response().response_type()),
635 ServiceWorkerHeaderMap(), "", 0, GURL()));
637 ServiceWorkerResponse* response = match_context->response.get();
639 if (metadata->response().has_url())
640 response->url = GURL(metadata->response().url());
642 for (int i = 0; i < metadata->response().headers_size(); ++i) {
643 const CacheHeaderMap header = metadata->response().headers(i);
644 DCHECK(header.name().find('\0') == std::string::npos);
645 DCHECK(header.value().find('\0') == std::string::npos);
646 response->headers.insert(std::make_pair(header.name(), header.value()));
649 ServiceWorkerHeaderMap cached_request_headers;
650 for (int i = 0; i < metadata->request().headers_size(); ++i) {
651 const CacheHeaderMap header = metadata->request().headers(i);
652 DCHECK(header.name().find('\0') == std::string::npos);
653 DCHECK(header.value().find('\0') == std::string::npos);
654 cached_request_headers[header.name()] = header.value();
657 if (!VaryMatches(match_context->request->headers, cached_request_headers,
658 response->headers)) {
659 match_context->original_callback.Run(
660 CacheStorageCache::ERROR_TYPE_NOT_FOUND,
661 scoped_ptr<ServiceWorkerResponse>(),
662 scoped_ptr<storage::BlobDataHandle>());
663 return;
666 if (match_context->entry->GetDataSize(INDEX_RESPONSE_BODY) == 0) {
667 match_context->original_callback.Run(CacheStorageCache::ERROR_TYPE_OK,
668 match_context->response.Pass(),
669 scoped_ptr<storage::BlobDataHandle>());
670 return;
673 // Stream the response body into a blob.
674 if (!match_context->blob_storage_context) {
675 match_context->original_callback.Run(CacheStorageCache::ERROR_TYPE_STORAGE,
676 scoped_ptr<ServiceWorkerResponse>(),
677 scoped_ptr<storage::BlobDataHandle>());
678 return;
681 response->blob_uuid = base::GenerateGUID();
683 match_context->blob_data.reset(
684 new storage::BlobDataBuilder(response->blob_uuid));
685 match_context->response_body_buffer = new net::IOBufferWithSize(kBufferSize);
687 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
688 net::IOBufferWithSize* response_body_buffer =
689 match_context->response_body_buffer.get();
691 net::CompletionCallback read_callback = base::Bind(
692 &CacheStorageCache::MatchDidReadResponseBodyData,
693 weak_ptr_factory_.GetWeakPtr(), base::Passed(match_context.Pass()));
695 int read_rv =
696 tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, 0, response_body_buffer,
697 response_body_buffer->size(), read_callback);
699 if (read_rv != net::ERR_IO_PENDING)
700 read_callback.Run(read_rv);
703 void CacheStorageCache::MatchDidReadResponseBodyData(
704 scoped_ptr<MatchContext> match_context,
705 int rv) {
706 if (rv < 0) {
707 match_context->original_callback.Run(CacheStorageCache::ERROR_TYPE_STORAGE,
708 scoped_ptr<ServiceWorkerResponse>(),
709 scoped_ptr<storage::BlobDataHandle>());
710 return;
713 if (rv == 0) {
714 match_context->response->blob_uuid = match_context->blob_data->uuid();
715 match_context->response->blob_size = match_context->total_bytes_read;
716 MatchDoneWithBody(match_context.Pass());
717 return;
720 // TODO(jkarlin): This copying of the the entire cache response into memory is
721 // awful. Create a new interface around SimpleCache that provides access the
722 // data directly from the file. See bug http://crbug.com/403493.
723 match_context->blob_data->AppendData(
724 match_context->response_body_buffer->data(), rv);
725 match_context->total_bytes_read += rv;
726 int total_bytes_read = match_context->total_bytes_read;
728 // Grab some pointers before passing match_context in bind.
729 net::IOBufferWithSize* buffer = match_context->response_body_buffer.get();
730 disk_cache::Entry* tmp_entry_ptr = match_context->entry;
732 net::CompletionCallback read_callback = base::Bind(
733 &CacheStorageCache::MatchDidReadResponseBodyData,
734 weak_ptr_factory_.GetWeakPtr(), base::Passed(match_context.Pass()));
736 int read_rv = tmp_entry_ptr->ReadData(INDEX_RESPONSE_BODY, total_bytes_read,
737 buffer, buffer->size(), read_callback);
739 if (read_rv != net::ERR_IO_PENDING)
740 read_callback.Run(read_rv);
743 void CacheStorageCache::MatchDoneWithBody(
744 scoped_ptr<MatchContext> match_context) {
745 if (!match_context->blob_storage_context) {
746 match_context->original_callback.Run(CacheStorageCache::ERROR_TYPE_STORAGE,
747 scoped_ptr<ServiceWorkerResponse>(),
748 scoped_ptr<storage::BlobDataHandle>());
749 return;
752 scoped_ptr<storage::BlobDataHandle> blob_data_handle(
753 match_context->blob_storage_context->AddFinishedBlob(
754 match_context->blob_data.get()));
756 match_context->original_callback.Run(CacheStorageCache::ERROR_TYPE_OK,
757 match_context->response.Pass(),
758 blob_data_handle.Pass());
761 void CacheStorageCache::PutImpl(scoped_ptr<PutContext> put_context) {
762 DCHECK(backend_state_ != BACKEND_UNINITIALIZED);
763 if (backend_state_ != BACKEND_OPEN) {
764 put_context->callback.Run(ERROR_TYPE_STORAGE,
765 scoped_ptr<ServiceWorkerResponse>(),
766 scoped_ptr<storage::BlobDataHandle>());
767 return;
770 scoped_ptr<ServiceWorkerFetchRequest> request_copy(
771 new ServiceWorkerFetchRequest(*put_context->request));
773 DeleteImpl(request_copy.Pass(), base::Bind(&CacheStorageCache::PutDidDelete,
774 weak_ptr_factory_.GetWeakPtr(),
775 base::Passed(put_context.Pass())));
778 void CacheStorageCache::PutDidDelete(scoped_ptr<PutContext> put_context,
779 ErrorType delete_error) {
780 if (backend_state_ != BACKEND_OPEN) {
781 put_context->callback.Run(ERROR_TYPE_STORAGE,
782 scoped_ptr<ServiceWorkerResponse>(),
783 scoped_ptr<storage::BlobDataHandle>());
784 return;
787 disk_cache::Entry** entry_ptr = &put_context->cache_entry;
788 ServiceWorkerFetchRequest* request_ptr = put_context->request.get();
789 disk_cache::Backend* backend_ptr = backend_.get();
791 net::CompletionCallback create_entry_callback = base::Bind(
792 &CacheStorageCache::PutDidCreateEntry, weak_ptr_factory_.GetWeakPtr(),
793 base::Passed(put_context.Pass()));
795 int create_rv = backend_ptr->CreateEntry(request_ptr->url.spec(), entry_ptr,
796 create_entry_callback);
798 if (create_rv != net::ERR_IO_PENDING)
799 create_entry_callback.Run(create_rv);
802 void CacheStorageCache::PutDidCreateEntry(scoped_ptr<PutContext> put_context,
803 int rv) {
804 if (rv != net::OK) {
805 put_context->callback.Run(CacheStorageCache::ERROR_TYPE_EXISTS,
806 scoped_ptr<ServiceWorkerResponse>(),
807 scoped_ptr<storage::BlobDataHandle>());
808 return;
811 DCHECK(put_context->cache_entry);
813 CacheMetadata metadata;
814 CacheRequest* request_metadata = metadata.mutable_request();
815 request_metadata->set_method(put_context->request->method);
816 for (ServiceWorkerHeaderMap::const_iterator it =
817 put_context->request->headers.begin();
818 it != put_context->request->headers.end(); ++it) {
819 DCHECK(it->first.find('\0') == std::string::npos);
820 DCHECK(it->second.find('\0') == std::string::npos);
821 CacheHeaderMap* header_map = request_metadata->add_headers();
822 header_map->set_name(it->first);
823 header_map->set_value(it->second);
826 CacheResponse* response_metadata = metadata.mutable_response();
827 response_metadata->set_status_code(put_context->response->status_code);
828 response_metadata->set_status_text(put_context->response->status_text);
829 response_metadata->set_response_type(
830 WebResponseTypeToProtoResponseType(put_context->response->response_type));
831 response_metadata->set_url(put_context->response->url.spec());
832 for (ServiceWorkerHeaderMap::const_iterator it =
833 put_context->response->headers.begin();
834 it != put_context->response->headers.end(); ++it) {
835 DCHECK(it->first.find('\0') == std::string::npos);
836 DCHECK(it->second.find('\0') == std::string::npos);
837 CacheHeaderMap* header_map = response_metadata->add_headers();
838 header_map->set_name(it->first);
839 header_map->set_value(it->second);
842 scoped_ptr<std::string> serialized(new std::string());
843 if (!metadata.SerializeToString(serialized.get())) {
844 put_context->callback.Run(CacheStorageCache::ERROR_TYPE_STORAGE,
845 scoped_ptr<ServiceWorkerResponse>(),
846 scoped_ptr<storage::BlobDataHandle>());
847 return;
850 scoped_refptr<net::StringIOBuffer> buffer(
851 new net::StringIOBuffer(serialized.Pass()));
853 // Get a temporary copy of the entry pointer before passing it in base::Bind.
854 disk_cache::Entry* tmp_entry_ptr = put_context->cache_entry;
856 net::CompletionCallback write_headers_callback = base::Bind(
857 &CacheStorageCache::PutDidWriteHeaders, weak_ptr_factory_.GetWeakPtr(),
858 base::Passed(put_context.Pass()), buffer->size());
860 rv = tmp_entry_ptr->WriteData(INDEX_HEADERS, 0 /* offset */, buffer.get(),
861 buffer->size(), write_headers_callback,
862 true /* truncate */);
864 if (rv != net::ERR_IO_PENDING)
865 write_headers_callback.Run(rv);
868 void CacheStorageCache::PutDidWriteHeaders(scoped_ptr<PutContext> put_context,
869 int expected_bytes,
870 int rv) {
871 if (rv != expected_bytes) {
872 put_context->cache_entry->Doom();
873 put_context->callback.Run(CacheStorageCache::ERROR_TYPE_STORAGE,
874 scoped_ptr<ServiceWorkerResponse>(),
875 scoped_ptr<storage::BlobDataHandle>());
876 return;
879 // The metadata is written, now for the response content. The data is streamed
880 // from the blob into the cache entry.
882 if (put_context->response->blob_uuid.empty()) {
883 if (put_context->quota_manager_proxy.get()) {
884 put_context->quota_manager_proxy->NotifyStorageModified(
885 storage::QuotaClient::kServiceWorkerCache, put_context->origin,
886 storage::kStorageTypeTemporary,
887 put_context->cache_entry->GetDataSize(INDEX_HEADERS));
890 put_context->callback.Run(CacheStorageCache::ERROR_TYPE_OK,
891 put_context->response.Pass(),
892 scoped_ptr<storage::BlobDataHandle>());
893 return;
896 DCHECK(put_context->blob_data_handle);
898 disk_cache::ScopedEntryPtr entry(put_context->cache_entry);
899 put_context->cache_entry = NULL;
900 scoped_ptr<BlobReader> reader(new BlobReader());
901 BlobReader* reader_ptr = reader.get();
903 // Grab some pointers before passing put_context in Bind.
904 net::URLRequestContext* request_context = put_context->request_context;
905 scoped_ptr<storage::BlobDataHandle> blob_data_handle =
906 put_context->blob_data_handle.Pass();
908 reader_ptr->StreamBlobToCache(
909 entry.Pass(), request_context, blob_data_handle.Pass(),
910 base::Bind(&CacheStorageCache::PutDidWriteBlobToCache,
911 weak_ptr_factory_.GetWeakPtr(),
912 base::Passed(put_context.Pass()),
913 base::Passed(reader.Pass())));
916 void CacheStorageCache::PutDidWriteBlobToCache(
917 scoped_ptr<PutContext> put_context,
918 scoped_ptr<BlobReader> blob_reader,
919 disk_cache::ScopedEntryPtr entry,
920 bool success) {
921 DCHECK(entry);
922 put_context->cache_entry = entry.release();
924 if (!success) {
925 put_context->cache_entry->Doom();
926 put_context->callback.Run(CacheStorageCache::ERROR_TYPE_STORAGE,
927 scoped_ptr<ServiceWorkerResponse>(),
928 scoped_ptr<storage::BlobDataHandle>());
929 return;
932 if (put_context->quota_manager_proxy.get()) {
933 put_context->quota_manager_proxy->NotifyStorageModified(
934 storage::QuotaClient::kServiceWorkerCache, put_context->origin,
935 storage::kStorageTypeTemporary,
936 put_context->cache_entry->GetDataSize(INDEX_HEADERS) +
937 put_context->cache_entry->GetDataSize(INDEX_RESPONSE_BODY));
940 put_context->callback.Run(CacheStorageCache::ERROR_TYPE_OK,
941 put_context->response.Pass(),
942 put_context->out_blob_data_handle.Pass());
945 void CacheStorageCache::DeleteImpl(
946 scoped_ptr<ServiceWorkerFetchRequest> request,
947 const ErrorCallback& callback) {
948 DCHECK(backend_state_ != BACKEND_UNINITIALIZED);
949 if (backend_state_ != BACKEND_OPEN) {
950 callback.Run(ERROR_TYPE_STORAGE);
951 return;
953 scoped_ptr<disk_cache::Entry*> entry(new disk_cache::Entry*);
955 disk_cache::Entry** entry_ptr = entry.get();
957 ServiceWorkerFetchRequest* request_ptr = request.get();
959 net::CompletionCallback open_entry_callback = base::Bind(
960 &CacheStorageCache::DeleteDidOpenEntry, weak_ptr_factory_.GetWeakPtr(),
961 origin_, base::Passed(request.Pass()), callback,
962 base::Passed(entry.Pass()), quota_manager_proxy_);
964 int rv = backend_->OpenEntry(request_ptr->url.spec(), entry_ptr,
965 open_entry_callback);
966 if (rv != net::ERR_IO_PENDING)
967 open_entry_callback.Run(rv);
970 void CacheStorageCache::DeleteDidOpenEntry(
971 const GURL& origin,
972 scoped_ptr<ServiceWorkerFetchRequest> request,
973 const CacheStorageCache::ErrorCallback& callback,
974 scoped_ptr<disk_cache::Entry*> entry_ptr,
975 const scoped_refptr<storage::QuotaManagerProxy>& quota_manager_proxy,
976 int rv) {
977 if (rv != net::OK) {
978 callback.Run(CacheStorageCache::ERROR_TYPE_NOT_FOUND);
979 return;
982 DCHECK(entry_ptr);
983 disk_cache::ScopedEntryPtr entry(*entry_ptr);
985 if (quota_manager_proxy.get()) {
986 quota_manager_proxy->NotifyStorageModified(
987 storage::QuotaClient::kServiceWorkerCache, origin,
988 storage::kStorageTypeTemporary,
989 -1 * (entry->GetDataSize(INDEX_HEADERS) +
990 entry->GetDataSize(INDEX_RESPONSE_BODY)));
993 entry->Doom();
994 callback.Run(CacheStorageCache::ERROR_TYPE_OK);
997 void CacheStorageCache::KeysImpl(const RequestsCallback& callback) {
998 DCHECK(backend_state_ != BACKEND_UNINITIALIZED);
999 if (backend_state_ != BACKEND_OPEN) {
1000 callback.Run(ERROR_TYPE_STORAGE, scoped_ptr<Requests>());
1001 return;
1004 // 1. Iterate through all of the entries, open them, and add them to a vector.
1005 // 2. For each open entry:
1006 // 2.1. Read the headers into a protobuf.
1007 // 2.2. Copy the protobuf into a ServiceWorkerFetchRequest (a "key").
1008 // 2.3. Push the response into a vector of requests to be returned.
1009 // 3. Return the vector of requests (keys).
1011 // The entries have to be loaded into a vector first because enumeration loops
1012 // forever if you read data from a cache entry while enumerating.
1014 scoped_ptr<KeysContext> keys_context(new KeysContext(callback));
1016 keys_context->backend_iterator = backend_->CreateIterator();
1017 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
1018 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
1020 net::CompletionCallback open_entry_callback = base::Bind(
1021 &CacheStorageCache::KeysDidOpenNextEntry, weak_ptr_factory_.GetWeakPtr(),
1022 base::Passed(keys_context.Pass()));
1024 int rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
1026 if (rv != net::ERR_IO_PENDING)
1027 open_entry_callback.Run(rv);
1030 void CacheStorageCache::KeysDidOpenNextEntry(
1031 scoped_ptr<KeysContext> keys_context,
1032 int rv) {
1033 if (rv == net::ERR_FAILED) {
1034 DCHECK(!keys_context->enumerated_entry);
1035 // Enumeration is complete, extract the requests from the entries.
1036 Entries::iterator iter = keys_context->entries.begin();
1037 KeysProcessNextEntry(keys_context.Pass(), iter);
1038 return;
1041 if (rv < 0) {
1042 keys_context->original_callback.Run(ERROR_TYPE_STORAGE,
1043 scoped_ptr<Requests>());
1044 return;
1047 if (backend_state_ != BACKEND_OPEN) {
1048 keys_context->original_callback.Run(ERROR_TYPE_NOT_FOUND,
1049 scoped_ptr<Requests>());
1050 return;
1053 // Store the entry.
1054 keys_context->entries.push_back(keys_context->enumerated_entry);
1055 keys_context->enumerated_entry = NULL;
1057 // Enumerate the next entry.
1058 disk_cache::Backend::Iterator& iterator = *keys_context->backend_iterator;
1059 disk_cache::Entry** enumerated_entry = &keys_context->enumerated_entry;
1060 net::CompletionCallback open_entry_callback = base::Bind(
1061 &CacheStorageCache::KeysDidOpenNextEntry, weak_ptr_factory_.GetWeakPtr(),
1062 base::Passed(keys_context.Pass()));
1064 rv = iterator.OpenNextEntry(enumerated_entry, open_entry_callback);
1066 if (rv != net::ERR_IO_PENDING)
1067 open_entry_callback.Run(rv);
1070 void CacheStorageCache::KeysProcessNextEntry(
1071 scoped_ptr<KeysContext> keys_context,
1072 const Entries::iterator& iter) {
1073 if (iter == keys_context->entries.end()) {
1074 // All done. Return all of the keys.
1075 keys_context->original_callback.Run(ERROR_TYPE_OK,
1076 keys_context->out_keys.Pass());
1077 return;
1080 ReadMetadata(*iter, base::Bind(&CacheStorageCache::KeysDidReadMetadata,
1081 weak_ptr_factory_.GetWeakPtr(),
1082 base::Passed(keys_context.Pass()), iter));
1085 void CacheStorageCache::KeysDidReadMetadata(
1086 scoped_ptr<KeysContext> keys_context,
1087 const Entries::iterator& iter,
1088 scoped_ptr<CacheMetadata> metadata) {
1089 disk_cache::Entry* entry = *iter;
1091 if (metadata) {
1092 keys_context->out_keys->push_back(ServiceWorkerFetchRequest(
1093 GURL(entry->GetKey()), metadata->request().method(),
1094 ServiceWorkerHeaderMap(), Referrer(), false));
1096 ServiceWorkerHeaderMap& req_headers =
1097 keys_context->out_keys->back().headers;
1099 for (int i = 0; i < metadata->request().headers_size(); ++i) {
1100 const CacheHeaderMap header = metadata->request().headers(i);
1101 DCHECK(header.name().find('\0') == std::string::npos);
1102 DCHECK(header.value().find('\0') == std::string::npos);
1103 req_headers.insert(std::make_pair(header.name(), header.value()));
1105 } else {
1106 entry->Doom();
1109 KeysProcessNextEntry(keys_context.Pass(), iter + 1);
1112 void CacheStorageCache::CloseImpl(const base::Closure& callback) {
1113 DCHECK(backend_state_ != BACKEND_CLOSED);
1115 backend_state_ = BACKEND_CLOSED;
1116 backend_.reset();
1117 callback.Run();
1120 void CacheStorageCache::CreateBackend(const ErrorCallback& callback) {
1121 DCHECK(!backend_);
1123 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
1124 net::CacheType cache_type = memory_only_ ? net::MEMORY_CACHE : net::APP_CACHE;
1126 scoped_ptr<ScopedBackendPtr> backend_ptr(new ScopedBackendPtr());
1128 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
1129 ScopedBackendPtr* backend = backend_ptr.get();
1131 net::CompletionCallback create_cache_callback =
1132 base::Bind(&CacheStorageCache::CreateBackendDidCreate,
1133 weak_ptr_factory_.GetWeakPtr(), callback,
1134 base::Passed(backend_ptr.Pass()));
1136 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
1137 // has for disk caches.
1138 int rv = disk_cache::CreateCacheBackend(
1139 cache_type, net::CACHE_BACKEND_SIMPLE, path_, kMaxCacheBytes,
1140 false, /* force */
1141 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE).get(),
1142 NULL, backend, create_cache_callback);
1143 if (rv != net::ERR_IO_PENDING)
1144 create_cache_callback.Run(rv);
1147 void CacheStorageCache::CreateBackendDidCreate(
1148 const CacheStorageCache::ErrorCallback& callback,
1149 scoped_ptr<ScopedBackendPtr> backend_ptr,
1150 int rv) {
1151 if (rv != net::OK) {
1152 callback.Run(CacheStorageCache::ERROR_TYPE_STORAGE);
1153 return;
1156 backend_ = backend_ptr->Pass();
1157 callback.Run(CacheStorageCache::ERROR_TYPE_OK);
1160 void CacheStorageCache::InitBackend() {
1161 DCHECK(backend_state_ == BACKEND_UNINITIALIZED);
1163 if (initializing_)
1164 return;
1166 DCHECK(!scheduler_->ScheduledOperations());
1167 initializing_ = true;
1169 scheduler_->ScheduleOperation(base::Bind(
1170 &CacheStorageCache::CreateBackend, weak_ptr_factory_.GetWeakPtr(),
1171 base::Bind(&CacheStorageCache::InitDone,
1172 weak_ptr_factory_.GetWeakPtr())));
1175 void CacheStorageCache::InitDone(ErrorType error) {
1176 initializing_ = false;
1177 backend_state_ = (error == ERROR_TYPE_OK && backend_ &&
1178 backend_state_ == BACKEND_UNINITIALIZED)
1179 ? BACKEND_OPEN
1180 : BACKEND_CLOSED;
1182 UMA_HISTOGRAM_ENUMERATION("ServiceWorkerCache.InitBackendResult", error,
1183 ErrorType::ERROR_TYPE_LAST + 1);
1185 scheduler_->CompleteOperationAndRunNext();
1188 void CacheStorageCache::PendingClosure(const base::Closure& callback) {
1189 base::WeakPtr<CacheStorageCache> cache = weak_ptr_factory_.GetWeakPtr();
1191 callback.Run();
1192 if (cache)
1193 scheduler_->CompleteOperationAndRunNext();
1196 void CacheStorageCache::PendingErrorCallback(const ErrorCallback& callback,
1197 ErrorType error) {
1198 base::WeakPtr<CacheStorageCache> cache = weak_ptr_factory_.GetWeakPtr();
1200 callback.Run(error);
1201 if (cache)
1202 scheduler_->CompleteOperationAndRunNext();
1205 void CacheStorageCache::PendingResponseCallback(
1206 const ResponseCallback& callback,
1207 ErrorType error,
1208 scoped_ptr<ServiceWorkerResponse> response,
1209 scoped_ptr<storage::BlobDataHandle> blob_data_handle) {
1210 base::WeakPtr<CacheStorageCache> cache = weak_ptr_factory_.GetWeakPtr();
1212 callback.Run(error, response.Pass(), blob_data_handle.Pass());
1213 if (cache)
1214 scheduler_->CompleteOperationAndRunNext();
1217 void CacheStorageCache::PendingRequestsCallback(
1218 const RequestsCallback& callback,
1219 ErrorType error,
1220 scoped_ptr<Requests> requests) {
1221 base::WeakPtr<CacheStorageCache> cache = weak_ptr_factory_.GetWeakPtr();
1223 callback.Run(error, requests.Pass());
1224 if (cache)
1225 scheduler_->CompleteOperationAndRunNext();
1228 } // namespace content