1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/service_worker/service_worker_cache.h"
9 #include "base/files/file_path.h"
10 #include "base/guid.h"
11 #include "base/message_loop/message_loop_proxy.h"
12 #include "content/browser/service_worker/service_worker_cache.pb.h"
13 #include "content/public/browser/browser_thread.h"
14 #include "net/base/io_buffer.h"
15 #include "net/base/net_errors.h"
16 #include "net/disk_cache/disk_cache.h"
17 #include "net/url_request/url_request_context.h"
18 #include "webkit/browser/blob/blob_data_handle.h"
19 #include "webkit/browser/blob/blob_storage_context.h"
20 #include "webkit/browser/blob/blob_url_request_job_factory.h"
26 typedef scoped_ptr
<disk_cache::Backend
> ScopedBackendPtr
;
27 typedef base::Callback
<void(bool)> BoolCallback
;
28 typedef base::Callback
<void(disk_cache::ScopedEntryPtr
, bool)>
30 enum EntryIndex
{ INDEX_HEADERS
= 0, INDEX_RESPONSE_BODY
};
32 // The maximum size of an individual cache. Ultimately cache size is controlled
34 const int kMaxCacheBytes
= 512 * 1024 * 1024;
36 // Buffer size for cache and blob reading/writing.
37 const int kBufferSize
= 1024 * 512;
39 struct ResponseReadContext
{
40 ResponseReadContext(scoped_refptr
<net::IOBufferWithSize
> buff
,
41 scoped_refptr
<storage::BlobData
> blob
)
42 : buffer(buff
), blob_data(blob
), total_bytes_read(0) {}
44 scoped_refptr
<net::IOBufferWithSize
> buffer
;
45 scoped_refptr
<storage::BlobData
> blob_data
;
48 DISALLOW_COPY_AND_ASSIGN(ResponseReadContext
);
51 // Streams data from a blob and writes it to a given disk_cache::Entry.
52 class BlobReader
: public net::URLRequest::Delegate
{
54 typedef base::Callback
<void(disk_cache::ScopedEntryPtr
, bool)>
57 BlobReader(disk_cache::ScopedEntryPtr entry
)
58 : cache_entry_offset_(0),
59 buffer_(new net::IOBufferWithSize(kBufferSize
)),
60 weak_ptr_factory_(this) {
62 entry_
= entry
.Pass();
65 void StreamBlobToCache(net::URLRequestContext
* request_context
,
66 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
67 const EntryBoolCallback
& callback
) {
69 blob_request_
= storage::BlobProtocolHandler::CreateBlobRequest(
70 blob_data_handle
.Pass(), request_context
, this);
71 blob_request_
->Start();
74 // net::URLRequest::Delegate overrides for reading blobs.
75 virtual void OnReceivedRedirect(net::URLRequest
* request
,
76 const net::RedirectInfo
& redirect_info
,
77 bool* defer_redirect
) OVERRIDE
{
80 virtual void OnAuthRequired(net::URLRequest
* request
,
81 net::AuthChallengeInfo
* auth_info
) OVERRIDE
{
84 virtual void OnCertificateRequested(
85 net::URLRequest
* request
,
86 net::SSLCertRequestInfo
* cert_request_info
) OVERRIDE
{
89 virtual void OnSSLCertificateError(net::URLRequest
* request
,
90 const net::SSLInfo
& ssl_info
,
91 bool fatal
) OVERRIDE
{
94 virtual void OnBeforeNetworkStart(net::URLRequest
* request
,
95 bool* defer
) OVERRIDE
{
99 virtual void OnResponseStarted(net::URLRequest
* request
) OVERRIDE
{
100 if (!request
->status().is_success()) {
101 callback_
.Run(entry_
.Pass(), false);
107 virtual void ReadFromBlob() {
110 blob_request_
->Read(buffer_
.get(), buffer_
->size(), &bytes_read
);
112 OnReadCompleted(blob_request_
.get(), bytes_read
);
115 virtual void OnReadCompleted(net::URLRequest
* request
,
116 int bytes_read
) OVERRIDE
{
117 if (!request
->status().is_success()) {
118 callback_
.Run(entry_
.Pass(), false);
122 if (bytes_read
== 0) {
123 callback_
.Run(entry_
.Pass(), true);
127 net::CompletionCallback cache_write_callback
=
128 base::Bind(&BlobReader::DidWriteDataToEntry
,
129 weak_ptr_factory_
.GetWeakPtr(),
132 int rv
= entry_
->WriteData(INDEX_RESPONSE_BODY
,
136 cache_write_callback
,
137 true /* truncate */);
138 if (rv
!= net::ERR_IO_PENDING
)
139 cache_write_callback
.Run(rv
);
142 void DidWriteDataToEntry(int expected_bytes
, int rv
) {
143 if (rv
!= expected_bytes
) {
144 callback_
.Run(entry_
.Pass(), false);
148 cache_entry_offset_
+= rv
;
153 int cache_entry_offset_
;
154 disk_cache::ScopedEntryPtr entry_
;
155 scoped_ptr
<net::URLRequest
> blob_request_
;
156 EntryBoolCallback callback_
;
157 scoped_refptr
<net::IOBufferWithSize
> buffer_
;
158 base::WeakPtrFactory
<BlobReader
> weak_ptr_factory_
;
162 void PutDidCreateEntry(ServiceWorkerFetchRequest
* request
,
163 ServiceWorkerResponse
* response
,
164 const ServiceWorkerCache::ErrorCallback
& callback
,
165 scoped_ptr
<disk_cache::Entry
*> entryptr
,
166 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
167 net::URLRequestContext
* request_context
,
169 void PutDidWriteHeaders(ServiceWorkerResponse
* response
,
170 const ServiceWorkerCache::ErrorCallback
& callback
,
171 disk_cache::ScopedEntryPtr entry
,
172 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
173 net::URLRequestContext
* request_context
,
176 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback
& callback
,
177 scoped_ptr
<BlobReader
> blob_reader
,
178 disk_cache::ScopedEntryPtr entry
,
182 void MatchDidOpenEntry(ServiceWorkerFetchRequest
* request
,
183 const ServiceWorkerCache::ResponseCallback
& callback
,
184 base::WeakPtr
<storage::BlobStorageContext
> blob_storage
,
185 scoped_ptr
<disk_cache::Entry
*> entryptr
,
187 void MatchDidReadHeaderData(
188 ServiceWorkerFetchRequest
* request
,
189 const ServiceWorkerCache::ResponseCallback
& callback
,
190 base::WeakPtr
<storage::BlobStorageContext
> blob_storage
,
191 disk_cache::ScopedEntryPtr entry
,
192 const scoped_refptr
<net::IOBufferWithSize
>& buffer
,
194 void MatchDidReadResponseBodyData(
195 ServiceWorkerFetchRequest
* request
,
196 const ServiceWorkerCache::ResponseCallback
& callback
,
197 base::WeakPtr
<storage::BlobStorageContext
> blob_storage
,
198 disk_cache::ScopedEntryPtr entry
,
199 scoped_ptr
<ServiceWorkerResponse
> response
,
200 scoped_ptr
<ResponseReadContext
> response_context
,
202 void MatchDoneWithBody(ServiceWorkerFetchRequest
* request
,
203 const ServiceWorkerCache::ResponseCallback
& callback
,
204 base::WeakPtr
<storage::BlobStorageContext
> blob_storage
,
205 scoped_ptr
<ServiceWorkerResponse
> response
,
206 scoped_ptr
<ResponseReadContext
> response_context
);
209 void DeleteDidOpenEntry(ServiceWorkerFetchRequest
* request
,
210 const ServiceWorkerCache::ErrorCallback
& callback
,
211 scoped_ptr
<disk_cache::Entry
*> entryptr
,
214 // CreateBackend callbacks
215 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback
& callback
,
216 scoped_ptr
<ScopedBackendPtr
> backend_ptr
,
217 base::WeakPtr
<ServiceWorkerCache
> cache
,
220 void PutDidCreateEntry(ServiceWorkerFetchRequest
* request
,
221 ServiceWorkerResponse
* response
,
222 const ServiceWorkerCache::ErrorCallback
& callback
,
223 scoped_ptr
<disk_cache::Entry
*> entryptr
,
224 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
225 net::URLRequestContext
* request_context
,
228 callback
.Run(ServiceWorkerCache::ErrorTypeExists
);
233 disk_cache::ScopedEntryPtr
entry(*entryptr
);
235 ServiceWorkerRequestResponseHeaders headers
;
236 headers
.set_method(request
->method
);
238 headers
.set_status_code(response
->status_code
);
239 headers
.set_status_text(response
->status_text
);
240 for (std::map
<std::string
, std::string
>::const_iterator it
=
241 request
->headers
.begin();
242 it
!= request
->headers
.end();
244 ServiceWorkerRequestResponseHeaders::HeaderMap
* header_map
=
245 headers
.add_request_headers();
246 header_map
->set_name(it
->first
);
247 header_map
->set_value(it
->second
);
250 for (std::map
<std::string
, std::string
>::const_iterator it
=
251 response
->headers
.begin();
252 it
!= response
->headers
.end();
254 ServiceWorkerRequestResponseHeaders::HeaderMap
* header_map
=
255 headers
.add_response_headers();
256 header_map
->set_name(it
->first
);
257 header_map
->set_value(it
->second
);
260 scoped_ptr
<std::string
> serialized(new std::string());
261 if (!headers
.SerializeToString(serialized
.get())) {
262 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
);
266 scoped_refptr
<net::StringIOBuffer
> buffer(
267 new net::StringIOBuffer(serialized
.Pass()));
269 // Get a temporary copy of the entry pointer before passing it in base::Bind.
270 disk_cache::Entry
* tmp_entry_ptr
= entry
.get();
272 net::CompletionCallback write_headers_callback
=
273 base::Bind(PutDidWriteHeaders
,
276 base::Passed(entry
.Pass()),
277 base::Passed(blob_data_handle
.Pass()),
281 rv
= tmp_entry_ptr
->WriteData(INDEX_HEADERS
,
285 write_headers_callback
,
286 true /* truncate */);
288 if (rv
!= net::ERR_IO_PENDING
)
289 write_headers_callback
.Run(rv
);
292 void PutDidWriteHeaders(ServiceWorkerResponse
* response
,
293 const ServiceWorkerCache::ErrorCallback
& callback
,
294 disk_cache::ScopedEntryPtr entry
,
295 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
,
296 net::URLRequestContext
* request_context
,
299 if (rv
!= expected_bytes
) {
301 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
);
305 // The metadata is written, now for the response content. The data is streamed
306 // from the blob into the cache entry.
308 if (response
->blob_uuid
.empty()) {
309 callback
.Run(ServiceWorkerCache::ErrorTypeOK
);
313 DCHECK(blob_data_handle
);
315 scoped_ptr
<BlobReader
> reader(new BlobReader(entry
.Pass()));
316 BlobReader
* reader_ptr
= reader
.get();
318 reader_ptr
->StreamBlobToCache(
320 blob_data_handle
.Pass(),
322 PutDidWriteBlobToCache
, callback
, base::Passed(reader
.Pass())));
325 void PutDidWriteBlobToCache(const ServiceWorkerCache::ErrorCallback
& callback
,
326 scoped_ptr
<BlobReader
> blob_reader
,
327 disk_cache::ScopedEntryPtr entry
,
331 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
);
335 callback
.Run(ServiceWorkerCache::ErrorTypeOK
);
338 void MatchDidOpenEntry(ServiceWorkerFetchRequest
* request
,
339 const ServiceWorkerCache::ResponseCallback
& callback
,
340 base::WeakPtr
<storage::BlobStorageContext
> blob_storage
,
341 scoped_ptr
<disk_cache::Entry
*> entryptr
,
344 callback
.Run(ServiceWorkerCache::ErrorTypeNotFound
,
345 scoped_ptr
<ServiceWorkerResponse
>(),
346 scoped_ptr
<storage::BlobDataHandle
>());
351 disk_cache::ScopedEntryPtr
entry(*entryptr
);
353 scoped_refptr
<net::IOBufferWithSize
> buffer(
354 new net::IOBufferWithSize(entry
->GetDataSize(INDEX_HEADERS
)));
356 // Copy the entry pointer before passing it in base::Bind.
357 disk_cache::Entry
* tmp_entry_ptr
= entry
.get();
359 net::CompletionCallback read_header_callback
=
360 base::Bind(MatchDidReadHeaderData
,
364 base::Passed(entry
.Pass()),
367 int read_rv
= tmp_entry_ptr
->ReadData(
368 INDEX_HEADERS
, 0, buffer
.get(), buffer
->size(), read_header_callback
);
370 if (read_rv
!= net::ERR_IO_PENDING
)
371 read_header_callback
.Run(read_rv
);
374 void MatchDidReadHeaderData(
375 ServiceWorkerFetchRequest
* request
,
376 const ServiceWorkerCache::ResponseCallback
& callback
,
377 base::WeakPtr
<storage::BlobStorageContext
> blob_storage
,
378 disk_cache::ScopedEntryPtr entry
,
379 const scoped_refptr
<net::IOBufferWithSize
>& buffer
,
381 if (rv
!= buffer
->size()) {
382 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
383 scoped_ptr
<ServiceWorkerResponse
>(),
384 scoped_ptr
<storage::BlobDataHandle
>());
389 ServiceWorkerRequestResponseHeaders headers
;
391 if (!headers
.ParseFromArray(buffer
->data(), buffer
->size())) {
392 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
393 scoped_ptr
<ServiceWorkerResponse
>(),
394 scoped_ptr
<storage::BlobDataHandle
>());
399 scoped_ptr
<ServiceWorkerResponse
> response(
400 new ServiceWorkerResponse(request
->url
,
401 headers
.status_code(),
402 headers
.status_text(),
403 std::map
<std::string
, std::string
>(),
406 for (int i
= 0; i
< headers
.response_headers_size(); ++i
) {
407 const ServiceWorkerRequestResponseHeaders::HeaderMap header
=
408 headers
.response_headers(i
);
409 response
->headers
.insert(std::make_pair(header
.name(), header
.value()));
412 // TODO(jkarlin): Insert vary validation here.
414 if (entry
->GetDataSize(INDEX_RESPONSE_BODY
) == 0) {
415 callback
.Run(ServiceWorkerCache::ErrorTypeOK
,
417 scoped_ptr
<storage::BlobDataHandle
>());
421 // Stream the response body into a blob.
423 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
424 scoped_ptr
<ServiceWorkerResponse
>(),
425 scoped_ptr
<storage::BlobDataHandle
>());
430 response
->blob_uuid
= base::GenerateGUID();
432 scoped_refptr
<storage::BlobData
> blob_data
=
433 new storage::BlobData(response
->blob_uuid
);
434 scoped_refptr
<net::IOBufferWithSize
> response_body_buffer(
435 new net::IOBufferWithSize(kBufferSize
));
437 scoped_ptr
<ResponseReadContext
> read_context(
438 new ResponseReadContext(response_body_buffer
, blob_data
));
440 // Copy the entry pointer before passing it in base::Bind.
441 disk_cache::Entry
* tmp_entry_ptr
= entry
.get();
443 net::CompletionCallback read_callback
=
444 base::Bind(MatchDidReadResponseBodyData
,
448 base::Passed(entry
.Pass()),
449 base::Passed(response
.Pass()),
450 base::Passed(read_context
.Pass()));
452 int read_rv
= tmp_entry_ptr
->ReadData(INDEX_RESPONSE_BODY
,
454 response_body_buffer
.get(),
455 response_body_buffer
->size(),
458 if (read_rv
!= net::ERR_IO_PENDING
)
459 read_callback
.Run(read_rv
);
462 void MatchDidReadResponseBodyData(
463 ServiceWorkerFetchRequest
* request
,
464 const ServiceWorkerCache::ResponseCallback
& callback
,
465 base::WeakPtr
<storage::BlobStorageContext
> blob_storage
,
466 disk_cache::ScopedEntryPtr entry
,
467 scoped_ptr
<ServiceWorkerResponse
> response
,
468 scoped_ptr
<ResponseReadContext
> response_context
,
471 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
472 scoped_ptr
<ServiceWorkerResponse
>(),
473 scoped_ptr
<storage::BlobDataHandle
>());
478 MatchDoneWithBody(request
,
482 response_context
.Pass());
486 // TODO(jkarlin): This copying of the the entire cache response into memory is
487 // awful. Create a new interface around SimpleCache that provides access the
488 // data directly from the file. See bug http://crbug.com/403493.
489 response_context
->blob_data
->AppendData(response_context
->buffer
->data(), rv
);
490 response_context
->total_bytes_read
+= rv
;
491 int total_bytes_read
= response_context
->total_bytes_read
;
493 // Grab some pointers before passing them in bind.
494 net::IOBufferWithSize
* buffer
= response_context
->buffer
.get();
495 disk_cache::Entry
* tmp_entry_ptr
= entry
.get();
497 net::CompletionCallback read_callback
=
498 base::Bind(MatchDidReadResponseBodyData
,
502 base::Passed(entry
.Pass()),
503 base::Passed(response
.Pass()),
504 base::Passed(response_context
.Pass()));
506 int read_rv
= tmp_entry_ptr
->ReadData(INDEX_RESPONSE_BODY
,
512 if (read_rv
!= net::ERR_IO_PENDING
)
513 read_callback
.Run(read_rv
);
516 void MatchDoneWithBody(ServiceWorkerFetchRequest
* request
,
517 const ServiceWorkerCache::ResponseCallback
& callback
,
518 base::WeakPtr
<storage::BlobStorageContext
> blob_storage
,
519 scoped_ptr
<ServiceWorkerResponse
> response
,
520 scoped_ptr
<ResponseReadContext
> response_context
) {
522 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
,
523 scoped_ptr
<ServiceWorkerResponse
>(),
524 scoped_ptr
<storage::BlobDataHandle
>());
528 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle(
529 blob_storage
->AddFinishedBlob(response_context
->blob_data
.get()));
531 callback
.Run(ServiceWorkerCache::ErrorTypeOK
,
533 blob_data_handle
.Pass());
536 void DeleteDidOpenEntry(ServiceWorkerFetchRequest
* request
,
537 const ServiceWorkerCache::ErrorCallback
& callback
,
538 scoped_ptr
<disk_cache::Entry
*> entryptr
,
541 callback
.Run(ServiceWorkerCache::ErrorTypeNotFound
);
546 disk_cache::ScopedEntryPtr
entry(*entryptr
);
549 callback
.Run(ServiceWorkerCache::ErrorTypeOK
);
552 void CreateBackendDidCreate(const ServiceWorkerCache::ErrorCallback
& callback
,
553 scoped_ptr
<ScopedBackendPtr
> backend_ptr
,
554 base::WeakPtr
<ServiceWorkerCache
> cache
,
556 if (rv
!= net::OK
|| !cache
) {
557 callback
.Run(ServiceWorkerCache::ErrorTypeStorage
);
560 cache
->set_backend(backend_ptr
->Pass());
561 callback
.Run(ServiceWorkerCache::ErrorTypeOK
);
567 scoped_ptr
<ServiceWorkerCache
> ServiceWorkerCache::CreateMemoryCache(
568 net::URLRequestContext
* request_context
,
569 base::WeakPtr
<storage::BlobStorageContext
> blob_context
) {
570 return make_scoped_ptr(
571 new ServiceWorkerCache(base::FilePath(), request_context
, blob_context
));
575 scoped_ptr
<ServiceWorkerCache
> ServiceWorkerCache::CreatePersistentCache(
576 const base::FilePath
& path
,
577 net::URLRequestContext
* request_context
,
578 base::WeakPtr
<storage::BlobStorageContext
> blob_context
) {
579 return make_scoped_ptr(
580 new ServiceWorkerCache(path
, request_context
, blob_context
));
583 ServiceWorkerCache::~ServiceWorkerCache() {
586 base::WeakPtr
<ServiceWorkerCache
> ServiceWorkerCache::AsWeakPtr() {
587 return weak_ptr_factory_
.GetWeakPtr();
590 void ServiceWorkerCache::CreateBackend(const ErrorCallback
& callback
) {
593 // Use APP_CACHE as opposed to DISK_CACHE to prevent cache eviction.
594 net::CacheType cache_type
=
595 path_
.empty() ? net::MEMORY_CACHE
: net::APP_CACHE
;
597 scoped_ptr
<ScopedBackendPtr
> backend_ptr(new ScopedBackendPtr());
599 // Temporary pointer so that backend_ptr can be Pass()'d in Bind below.
600 ScopedBackendPtr
* backend
= backend_ptr
.get();
602 net::CompletionCallback create_cache_callback
=
603 base::Bind(CreateBackendDidCreate
,
605 base::Passed(backend_ptr
.Pass()),
606 weak_ptr_factory_
.GetWeakPtr());
608 // TODO(jkarlin): Use the cache MessageLoopProxy that ServiceWorkerCacheCore
609 // has for disk caches.
610 // TODO(jkarlin): Switch to SimpleCache after it supports APP_CACHE and after
611 // debugging why the QuickStressBody unittest fails with it.
612 int rv
= disk_cache::CreateCacheBackend(
614 net::CACHE_BACKEND_DEFAULT
,
618 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::CACHE
).get(),
621 create_cache_callback
);
622 if (rv
!= net::ERR_IO_PENDING
)
623 create_cache_callback
.Run(rv
);
626 void ServiceWorkerCache::Put(ServiceWorkerFetchRequest
* request
,
627 ServiceWorkerResponse
* response
,
628 const ErrorCallback
& callback
) {
631 scoped_ptr
<disk_cache::Entry
*> entry(new disk_cache::Entry
*);
633 disk_cache::Entry
** entry_ptr
= entry
.get();
635 scoped_ptr
<storage::BlobDataHandle
> blob_data_handle
;
637 if (!response
->blob_uuid
.empty()) {
638 if (!blob_storage_context_
) {
639 callback
.Run(ErrorTypeStorage
);
643 blob_storage_context_
->GetBlobDataFromUUID(response
->blob_uuid
);
644 if (!blob_data_handle
) {
645 callback
.Run(ErrorTypeStorage
);
650 net::CompletionCallback create_entry_callback
=
651 base::Bind(PutDidCreateEntry
,
655 base::Passed(entry
.Pass()),
656 base::Passed(blob_data_handle
.Pass()),
659 int rv
= backend_
->CreateEntry(
660 request
->url
.spec(), entry_ptr
, create_entry_callback
);
662 if (rv
!= net::ERR_IO_PENDING
)
663 create_entry_callback
.Run(rv
);
666 void ServiceWorkerCache::Match(ServiceWorkerFetchRequest
* request
,
667 const ResponseCallback
& callback
) {
670 scoped_ptr
<disk_cache::Entry
*> entry(new disk_cache::Entry
*);
672 disk_cache::Entry
** entry_ptr
= entry
.get();
674 net::CompletionCallback open_entry_callback
=
675 base::Bind(MatchDidOpenEntry
,
678 blob_storage_context_
,
679 base::Passed(entry
.Pass()));
682 backend_
->OpenEntry(request
->url
.spec(), entry_ptr
, open_entry_callback
);
683 if (rv
!= net::ERR_IO_PENDING
)
684 open_entry_callback
.Run(rv
);
687 void ServiceWorkerCache::Delete(ServiceWorkerFetchRequest
* request
,
688 const ErrorCallback
& callback
) {
691 scoped_ptr
<disk_cache::Entry
*> entry(new disk_cache::Entry
*);
693 disk_cache::Entry
** entry_ptr
= entry
.get();
695 net::CompletionCallback open_entry_callback
= base::Bind(
696 DeleteDidOpenEntry
, request
, callback
, base::Passed(entry
.Pass()));
699 backend_
->OpenEntry(request
->url
.spec(), entry_ptr
, open_entry_callback
);
700 if (rv
!= net::ERR_IO_PENDING
)
701 open_entry_callback
.Run(rv
);
704 bool ServiceWorkerCache::HasCreatedBackend() const {
708 ServiceWorkerCache::ServiceWorkerCache(
709 const base::FilePath
& path
,
710 net::URLRequestContext
* request_context
,
711 base::WeakPtr
<storage::BlobStorageContext
> blob_context
)
713 request_context_(request_context
),
714 blob_storage_context_(blob_context
),
715 weak_ptr_factory_(this) {
718 } // namespace content