1 // Copyright 2013 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.
4
5 #include "content/browser/service_worker/service_worker_storage.h"
6
7 #include <string>
8
9 #include "base/bind_helpers.h"
10 #include "base/debug/trace_event.h"
11 #include "base/files/file_util.h"
12 #include "base/hash.h"
13 #include "base/message_loop/message_loop.h"
14 #include "base/sequenced_task_runner.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/task_runner_util.h"
17 #include "content/browser/service_worker/service_worker_context_core.h"
18 #include "content/browser/service_worker/service_worker_disk_cache.h"
19 #include "content/browser/service_worker/service_worker_info.h"
20 #include "content/browser/service_worker/service_worker_metrics.h"
21 #include "content/browser/service_worker/service_worker_registration.h"
22 #include "content/browser/service_worker/service_worker_utils.h"
23 #include "content/browser/service_worker/service_worker_version.h"
24 #include "content/common/service_worker/service_worker_types.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "net/base/completion_callback.h"
27 #include "net/base/io_buffer.h"
28 #include "net/base/net_errors.h"
29 #include "storage/browser/quota/quota_manager_proxy.h"
30
31 namespace content {
32
33 namespace {
34
RunSoon(const tracked_objects::Location & from_here,const base::Closure & closure)35 void RunSoon(const tracked_objects::Location& from_here,
36 const base::Closure& closure) {
37 base::MessageLoop::current()->PostTask(from_here, closure);
38 }
39
CompleteFindNow(const scoped_refptr<ServiceWorkerRegistration> & registration,ServiceWorkerStatusCode status,const ServiceWorkerStorage::FindRegistrationCallback & callback)40 void CompleteFindNow(
41 const scoped_refptr<ServiceWorkerRegistration>& registration,
42 ServiceWorkerStatusCode status,
43 const ServiceWorkerStorage::FindRegistrationCallback& callback) {
44 callback.Run(status, registration);
45 }
46
CompleteFindSoon(const tracked_objects::Location & from_here,const scoped_refptr<ServiceWorkerRegistration> & registration,ServiceWorkerStatusCode status,const ServiceWorkerStorage::FindRegistrationCallback & callback)47 void CompleteFindSoon(
48 const tracked_objects::Location& from_here,
49 const scoped_refptr<ServiceWorkerRegistration>& registration,
50 ServiceWorkerStatusCode status,
51 const ServiceWorkerStorage::FindRegistrationCallback& callback) {
52 RunSoon(from_here, base::Bind(callback, status, registration));
53 }
54
55 const base::FilePath::CharType kDatabaseName[] =
56 FILE_PATH_LITERAL("Database");
57 const base::FilePath::CharType kDiskCacheName[] =
58 FILE_PATH_LITERAL("Cache");
59
60 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
61 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
62
DatabaseStatusToStatusCode(ServiceWorkerDatabase::Status status)63 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
64 ServiceWorkerDatabase::Status status) {
65 switch (status) {
66 case ServiceWorkerDatabase::STATUS_OK:
67 return SERVICE_WORKER_OK;
68 case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
69 return SERVICE_WORKER_ERROR_NOT_FOUND;
70 case ServiceWorkerDatabase::STATUS_ERROR_MAX:
71 NOTREACHED();
72 default:
73 return SERVICE_WORKER_ERROR_FAILED;
74 }
75 }
76
77 class ResponseComparer : public base::RefCounted<ResponseComparer> {
78 public:
ResponseComparer(base::WeakPtr<ServiceWorkerStorage> owner,scoped_ptr<ServiceWorkerResponseReader> lhs,scoped_ptr<ServiceWorkerResponseReader> rhs,const ServiceWorkerStorage::CompareCallback & callback)79 ResponseComparer(
80 base::WeakPtr<ServiceWorkerStorage> owner,
81 scoped_ptr<ServiceWorkerResponseReader> lhs,
82 scoped_ptr<ServiceWorkerResponseReader> rhs,
83 const ServiceWorkerStorage::CompareCallback& callback)
84 : owner_(owner),
85 completion_callback_(callback),
86 lhs_reader_(lhs.release()),
87 rhs_reader_(rhs.release()),
88 completion_count_(0),
89 previous_result_(0) {
90 }
91
92 void Start();
93
94 private:
95 friend class base::RefCounted<ResponseComparer>;
96
97 static const int kBufferSize = 16 * 1024;
98
~ResponseComparer()99 ~ResponseComparer() {}
100 void ReadInfos();
101 void OnReadInfoComplete(int result);
102 void ReadSomeData();
103 void OnReadDataComplete(int result);
104
105 base::WeakPtr<ServiceWorkerStorage> owner_;
106 ServiceWorkerStorage::CompareCallback completion_callback_;
107 scoped_ptr<ServiceWorkerResponseReader> lhs_reader_;
108 scoped_refptr<HttpResponseInfoIOBuffer> lhs_info_;
109 scoped_refptr<net::IOBuffer> lhs_buffer_;
110 scoped_ptr<ServiceWorkerResponseReader> rhs_reader_;
111 scoped_refptr<HttpResponseInfoIOBuffer> rhs_info_;
112 scoped_refptr<net::IOBuffer> rhs_buffer_;
113 int completion_count_;
114 int previous_result_;
115 DISALLOW_COPY_AND_ASSIGN(ResponseComparer);
116 };
117
Start()118 void ResponseComparer::Start() {
119 lhs_buffer_ = new net::IOBuffer(kBufferSize);
120 lhs_info_ = new HttpResponseInfoIOBuffer();
121 rhs_buffer_ = new net::IOBuffer(kBufferSize);
122 rhs_info_ = new HttpResponseInfoIOBuffer();
123
124 ReadInfos();
125 }
126
ReadInfos()127 void ResponseComparer::ReadInfos() {
128 lhs_reader_->ReadInfo(
129 lhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this));
130 rhs_reader_->ReadInfo(
131 rhs_info_.get(), base::Bind(&ResponseComparer::OnReadInfoComplete, this));
132 }
133
OnReadInfoComplete(int result)134 void ResponseComparer::OnReadInfoComplete(int result) {
135 if (completion_callback_.is_null() || !owner_)
136 return;
137 if (result < 0) {
138 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
139 completion_callback_.Reset();
140 return;
141 }
142 if (++completion_count_ != 2)
143 return;
144
145 if (lhs_info_->response_data_size != rhs_info_->response_data_size) {
146 completion_callback_.Run(SERVICE_WORKER_OK, false);
147 return;
148 }
149 ReadSomeData();
150 }
151
ReadSomeData()152 void ResponseComparer::ReadSomeData() {
153 completion_count_ = 0;
154 lhs_reader_->ReadData(
155 lhs_buffer_.get(),
156 kBufferSize,
157 base::Bind(&ResponseComparer::OnReadDataComplete, this));
158 rhs_reader_->ReadData(
159 rhs_buffer_.get(),
160 kBufferSize,
161 base::Bind(&ResponseComparer::OnReadDataComplete, this));
162 }
163
OnReadDataComplete(int result)164 void ResponseComparer::OnReadDataComplete(int result) {
165 if (completion_callback_.is_null() || !owner_)
166 return;
167 if (result < 0) {
168 completion_callback_.Run(SERVICE_WORKER_ERROR_FAILED, false);
169 completion_callback_.Reset();
170 return;
171 }
172 if (++completion_count_ != 2) {
173 previous_result_ = result;
174 return;
175 }
176
177 // TODO(michaeln): Probably shouldn't assume that the amounts read from
178 // each reader will always be the same. This would wrongly signal false
179 // in that case.
180 if (result != previous_result_) {
181 completion_callback_.Run(SERVICE_WORKER_OK, false);
182 return;
183 }
184
185 if (result == 0) {
186 completion_callback_.Run(SERVICE_WORKER_OK, true);
187 return;
188 }
189
190 int compare_result =
191 memcmp(lhs_buffer_->data(), rhs_buffer_->data(), result);
192 if (compare_result != 0) {
193 completion_callback_.Run(SERVICE_WORKER_OK, false);
194 return;
195 }
196
197 ReadSomeData();
198 }
199
200 } // namespace
201
InitialData()202 ServiceWorkerStorage::InitialData::InitialData()
203 : next_registration_id(kInvalidServiceWorkerRegistrationId),
204 next_version_id(kInvalidServiceWorkerVersionId),
205 next_resource_id(kInvalidServiceWorkerResourceId) {
206 }
207
~InitialData()208 ServiceWorkerStorage::InitialData::~InitialData() {
209 }
210
211 ServiceWorkerStorage::
DidDeleteRegistrationParams()212 DidDeleteRegistrationParams::DidDeleteRegistrationParams()
213 : registration_id(kInvalidServiceWorkerRegistrationId) {
214 }
215
216 ServiceWorkerStorage::
~DidDeleteRegistrationParams()217 DidDeleteRegistrationParams::~DidDeleteRegistrationParams() {
218 }
219
~ServiceWorkerStorage()220 ServiceWorkerStorage::~ServiceWorkerStorage() {
221 weak_factory_.InvalidateWeakPtrs();
222 database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
223 }
224
225 // static
Create(const base::FilePath & path,base::WeakPtr<ServiceWorkerContextCore> context,const scoped_refptr<base::SequencedTaskRunner> & database_task_runner,const scoped_refptr<base::SingleThreadTaskRunner> & disk_cache_thread,storage::QuotaManagerProxy * quota_manager_proxy)226 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
227 const base::FilePath& path,
228 base::WeakPtr<ServiceWorkerContextCore> context,
229 const scoped_refptr<base::SequencedTaskRunner>& database_task_runner,
230 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
231 storage::QuotaManagerProxy* quota_manager_proxy) {
232 return make_scoped_ptr(new ServiceWorkerStorage(path,
233 context,
234 database_task_runner,
235 disk_cache_thread,
236 quota_manager_proxy));
237 }
238
239 // static
Create(base::WeakPtr<ServiceWorkerContextCore> context,ServiceWorkerStorage * old_storage)240 scoped_ptr<ServiceWorkerStorage> ServiceWorkerStorage::Create(
241 base::WeakPtr<ServiceWorkerContextCore> context,
242 ServiceWorkerStorage* old_storage) {
243 return make_scoped_ptr(
244 new ServiceWorkerStorage(old_storage->path_,
245 context,
246 old_storage->database_task_runner_,
247 old_storage->disk_cache_thread_,
248 old_storage->quota_manager_proxy_.get()));
249 }
250
FindRegistrationForDocument(const GURL & document_url,const FindRegistrationCallback & callback)251 void ServiceWorkerStorage::FindRegistrationForDocument(
252 const GURL& document_url,
253 const FindRegistrationCallback& callback) {
254 DCHECK(!document_url.has_ref());
255 TRACE_EVENT_ASYNC_BEGIN1(
256 "ServiceWorker",
257 "ServiceWorkerStorage::FindRegistrationForDocument",
258 base::Hash(document_url.spec()),
259 "URL", document_url.spec());
260 if (!LazyInitialize(base::Bind(
261 &ServiceWorkerStorage::FindRegistrationForDocument,
262 weak_factory_.GetWeakPtr(), document_url, callback))) {
263 if (state_ != INITIALIZING || !context_) {
264 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
265 SERVICE_WORKER_ERROR_FAILED, callback);
266 }
267 return;
268 }
269 DCHECK_EQ(INITIALIZED, state_);
270
271 // See if there are any stored registrations for the origin.
272 if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
273 // Look for something currently being installed.
274 scoped_refptr<ServiceWorkerRegistration> installing_registration =
275 FindInstallingRegistrationForDocument(document_url);
276 CompleteFindNow(installing_registration,
277 installing_registration.get()
278 ? SERVICE_WORKER_OK
279 : SERVICE_WORKER_ERROR_NOT_FOUND,
280 callback);
281 return;
282 }
283
284 database_task_runner_->PostTask(
285 FROM_HERE,
286 base::Bind(
287 &FindForDocumentInDB,
288 database_.get(),
289 base::MessageLoopProxy::current(),
290 document_url,
291 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
292 weak_factory_.GetWeakPtr(), document_url, callback)));
293 }
294
FindRegistrationForPattern(const GURL & scope,const FindRegistrationCallback & callback)295 void ServiceWorkerStorage::FindRegistrationForPattern(
296 const GURL& scope,
297 const FindRegistrationCallback& callback) {
298 if (!LazyInitialize(base::Bind(
299 &ServiceWorkerStorage::FindRegistrationForPattern,
300 weak_factory_.GetWeakPtr(), scope, callback))) {
301 if (state_ != INITIALIZING || !context_) {
302 CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
303 SERVICE_WORKER_ERROR_FAILED, callback);
304 }
305 return;
306 }
307 DCHECK_EQ(INITIALIZED, state_);
308
309 // See if there are any stored registrations for the origin.
310 if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
311 // Look for something currently being installed.
312 scoped_refptr<ServiceWorkerRegistration> installing_registration =
313 FindInstallingRegistrationForPattern(scope);
314 CompleteFindSoon(FROM_HERE,
315 installing_registration,
316 installing_registration.get()
317 ? SERVICE_WORKER_OK
318 : SERVICE_WORKER_ERROR_NOT_FOUND,
319 callback);
320 return;
321 }
322
323 database_task_runner_->PostTask(
324 FROM_HERE,
325 base::Bind(
326 &FindForPatternInDB,
327 database_.get(),
328 base::MessageLoopProxy::current(),
329 scope,
330 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
331 weak_factory_.GetWeakPtr(), scope, callback)));
332 }
333
GetUninstallingRegistration(const GURL & scope)334 ServiceWorkerRegistration* ServiceWorkerStorage::GetUninstallingRegistration(
335 const GURL& scope) {
336 if (state_ != INITIALIZED || !context_)
337 return NULL;
338 for (RegistrationRefsById::const_iterator it =
339 uninstalling_registrations_.begin();
340 it != uninstalling_registrations_.end();
341 ++it) {
342 if (it->second->pattern() == scope) {
343 DCHECK(it->second->is_uninstalling());
344 return it->second.get();
345 }
346 }
347 return NULL;
348 }
349
FindRegistrationForId(int64 registration_id,const GURL & origin,const FindRegistrationCallback & callback)350 void ServiceWorkerStorage::FindRegistrationForId(
351 int64 registration_id,
352 const GURL& origin,
353 const FindRegistrationCallback& callback) {
354 if (!LazyInitialize(base::Bind(
355 &ServiceWorkerStorage::FindRegistrationForId,
356 weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
357 if (state_ != INITIALIZING || !context_) {
358 CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
359 SERVICE_WORKER_ERROR_FAILED, callback);
360 }
361 return;
362 }
363 DCHECK_EQ(INITIALIZED, state_);
364
365 // See if there are any stored registrations for the origin.
366 if (!ContainsKey(registered_origins_, origin)) {
367 // Look for something currently being installed.
368 scoped_refptr<ServiceWorkerRegistration> installing_registration =
369 FindInstallingRegistrationForId(registration_id);
370 CompleteFindNow(installing_registration,
371 installing_registration.get()
372 ? SERVICE_WORKER_OK
373 : SERVICE_WORKER_ERROR_NOT_FOUND,
374 callback);
375 return;
376 }
377
378 scoped_refptr<ServiceWorkerRegistration> registration =
379 context_->GetLiveRegistration(registration_id);
380 if (registration.get()) {
381 CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
382 return;
383 }
384
385 database_task_runner_->PostTask(
386 FROM_HERE,
387 base::Bind(&FindForIdInDB,
388 database_.get(),
389 base::MessageLoopProxy::current(),
390 registration_id, origin,
391 base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
392 weak_factory_.GetWeakPtr(), callback)));
393 }
394
GetAllRegistrations(const GetAllRegistrationInfosCallback & callback)395 void ServiceWorkerStorage::GetAllRegistrations(
396 const GetAllRegistrationInfosCallback& callback) {
397 if (!LazyInitialize(base::Bind(
398 &ServiceWorkerStorage::GetAllRegistrations,
399 weak_factory_.GetWeakPtr(), callback))) {
400 if (state_ != INITIALIZING || !context_) {
401 RunSoon(FROM_HERE, base::Bind(
402 callback, std::vector<ServiceWorkerRegistrationInfo>()));
403 }
404 return;
405 }
406 DCHECK_EQ(INITIALIZED, state_);
407
408 RegistrationList* registrations = new RegistrationList;
409 PostTaskAndReplyWithResult(
410 database_task_runner_.get(),
411 FROM_HERE,
412 base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
413 base::Unretained(database_.get()),
414 base::Unretained(registrations)),
415 base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
416 weak_factory_.GetWeakPtr(),
417 callback,
418 base::Owned(registrations)));
419 }
420
StoreRegistration(ServiceWorkerRegistration * registration,ServiceWorkerVersion * version,const StatusCallback & callback)421 void ServiceWorkerStorage::StoreRegistration(
422 ServiceWorkerRegistration* registration,
423 ServiceWorkerVersion* version,
424 const StatusCallback& callback) {
425 DCHECK(registration);
426 DCHECK(version);
427
428 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
429 if (IsDisabled() || !context_) {
430 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
431 return;
432 }
433
434 ServiceWorkerDatabase::RegistrationData data;
435 data.registration_id = registration->id();
436 data.scope = registration->pattern();
437 data.script = version->script_url();
438 data.has_fetch_handler = true;
439 data.version_id = version->version_id();
440 data.last_update_check = registration->last_update_check();
441 data.is_active = (version == registration->active_version());
442
443 ResourceList resources;
444 version->script_cache_map()->GetResources(&resources);
445
446 if (!has_checked_for_stale_resources_)
447 DeleteStaleResources();
448
449 database_task_runner_->PostTask(
450 FROM_HERE,
451 base::Bind(&WriteRegistrationInDB,
452 database_.get(),
453 base::MessageLoopProxy::current(),
454 data, resources,
455 base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
456 weak_factory_.GetWeakPtr(),
457 callback)));
458
459 registration->set_is_deleted(false);
460 }
461
UpdateToActiveState(ServiceWorkerRegistration * registration,const StatusCallback & callback)462 void ServiceWorkerStorage::UpdateToActiveState(
463 ServiceWorkerRegistration* registration,
464 const StatusCallback& callback) {
465 DCHECK(registration);
466
467 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
468 if (IsDisabled() || !context_) {
469 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
470 return;
471 }
472
473 PostTaskAndReplyWithResult(
474 database_task_runner_.get(),
475 FROM_HERE,
476 base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
477 base::Unretained(database_.get()),
478 registration->id(),
479 registration->pattern().GetOrigin()),
480 base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
481 weak_factory_.GetWeakPtr(),
482 callback));
483 }
484
UpdateLastUpdateCheckTime(ServiceWorkerRegistration * registration)485 void ServiceWorkerStorage::UpdateLastUpdateCheckTime(
486 ServiceWorkerRegistration* registration) {
487 DCHECK(registration);
488
489 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
490 if (IsDisabled() || !context_)
491 return;
492
493 database_task_runner_->PostTask(
494 FROM_HERE,
495 base::Bind(
496 base::IgnoreResult(&ServiceWorkerDatabase::UpdateLastCheckTime),
497 base::Unretained(database_.get()),
498 registration->id(),
499 registration->pattern().GetOrigin(),
500 registration->last_update_check()));
501 }
502
DeleteRegistration(int64 registration_id,const GURL & origin,const StatusCallback & callback)503 void ServiceWorkerStorage::DeleteRegistration(
504 int64 registration_id,
505 const GURL& origin,
506 const StatusCallback& callback) {
507 DCHECK(state_ == INITIALIZED || state_ == DISABLED) << state_;
508 if (IsDisabled() || !context_) {
509 RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
510 return;
511 }
512
513 if (!has_checked_for_stale_resources_)
514 DeleteStaleResources();
515
516 DidDeleteRegistrationParams params;
517 params.registration_id = registration_id;
518 params.origin = origin;
519 params.callback = callback;
520
521 database_task_runner_->PostTask(
522 FROM_HERE,
523 base::Bind(&DeleteRegistrationFromDB,
524 database_.get(),
525 base::MessageLoopProxy::current(),
526 registration_id, origin,
527 base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
528 weak_factory_.GetWeakPtr(), params)));
529
530 // The registration should no longer be findable.
531 pending_deletions_.insert(registration_id);
532 ServiceWorkerRegistration* registration =
533 context_->GetLiveRegistration(registration_id);
534 if (registration)
535 registration->set_is_deleted(true);
536 }
537
538 scoped_ptr<ServiceWorkerResponseReader>
CreateResponseReader(int64 response_id)539 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
540 return make_scoped_ptr(
541 new ServiceWorkerResponseReader(response_id, disk_cache()));
542 }
543
544 scoped_ptr<ServiceWorkerResponseWriter>
CreateResponseWriter(int64 response_id)545 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
546 return make_scoped_ptr(
547 new ServiceWorkerResponseWriter(response_id, disk_cache()));
548 }
549
StoreUncommittedResponseId(int64 id)550 void ServiceWorkerStorage::StoreUncommittedResponseId(int64 id) {
551 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
552 DCHECK_EQ(INITIALIZED, state_);
553
554 if (!has_checked_for_stale_resources_)
555 DeleteStaleResources();
556
557 database_task_runner_->PostTask(
558 FROM_HERE,
559 base::Bind(base::IgnoreResult(
560 &ServiceWorkerDatabase::WriteUncommittedResourceIds),
561 base::Unretained(database_.get()),
562 std::set<int64>(&id, &id + 1)));
563 }
564
DoomUncommittedResponse(int64 id)565 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
566 DCHECK_NE(kInvalidServiceWorkerResponseId, id);
567 database_task_runner_->PostTask(
568 FROM_HERE,
569 base::Bind(base::IgnoreResult(
570 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
571 base::Unretained(database_.get()),
572 std::set<int64>(&id, &id + 1)));
573 StartPurgingResources(std::vector<int64>(1, id));
574 }
575
CompareScriptResources(int64 lhs_id,int64 rhs_id,const CompareCallback & callback)576 void ServiceWorkerStorage::CompareScriptResources(
577 int64 lhs_id, int64 rhs_id,
578 const CompareCallback& callback) {
579 DCHECK(!callback.is_null());
580 scoped_refptr<ResponseComparer> comparer =
581 new ResponseComparer(weak_factory_.GetWeakPtr(),
582 CreateResponseReader(lhs_id),
583 CreateResponseReader(rhs_id),
584 callback);
585 comparer->Start(); // It deletes itself when done.
586 }
587
DeleteAndStartOver(const StatusCallback & callback)588 void ServiceWorkerStorage::DeleteAndStartOver(const StatusCallback& callback) {
589 Disable();
590
591 // Delete the database on the database thread.
592 PostTaskAndReplyWithResult(
593 database_task_runner_.get(),
594 FROM_HERE,
595 base::Bind(&ServiceWorkerDatabase::DestroyDatabase,
596 base::Unretained(database_.get())),
597 base::Bind(&ServiceWorkerStorage::DidDeleteDatabase,
598 weak_factory_.GetWeakPtr(),
599 callback));
600 }
601
NewRegistrationId()602 int64 ServiceWorkerStorage::NewRegistrationId() {
603 if (state_ == DISABLED)
604 return kInvalidServiceWorkerRegistrationId;
605 DCHECK_EQ(INITIALIZED, state_);
606 return next_registration_id_++;
607 }
608
NewVersionId()609 int64 ServiceWorkerStorage::NewVersionId() {
610 if (state_ == DISABLED)
611 return kInvalidServiceWorkerVersionId;
612 DCHECK_EQ(INITIALIZED, state_);
613 return next_version_id_++;
614 }
615
NewResourceId()616 int64 ServiceWorkerStorage::NewResourceId() {
617 if (state_ == DISABLED)
618 return kInvalidServiceWorkerResourceId;
619 DCHECK_EQ(INITIALIZED, state_);
620 return next_resource_id_++;
621 }
622
NotifyInstallingRegistration(ServiceWorkerRegistration * registration)623 void ServiceWorkerStorage::NotifyInstallingRegistration(
624 ServiceWorkerRegistration* registration) {
625 DCHECK(installing_registrations_.find(registration->id()) ==
626 installing_registrations_.end());
627 installing_registrations_[registration->id()] = registration;
628 }
629
NotifyDoneInstallingRegistration(ServiceWorkerRegistration * registration,ServiceWorkerVersion * version,ServiceWorkerStatusCode status)630 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
631 ServiceWorkerRegistration* registration,
632 ServiceWorkerVersion* version,
633 ServiceWorkerStatusCode status) {
634 installing_registrations_.erase(registration->id());
635 if (status != SERVICE_WORKER_OK && version) {
636 ResourceList resources;
637 version->script_cache_map()->GetResources(&resources);
638
639 std::set<int64> ids;
640 for (size_t i = 0; i < resources.size(); ++i)
641 ids.insert(resources[i].resource_id);
642
643 database_task_runner_->PostTask(
644 FROM_HERE,
645 base::Bind(base::IgnoreResult(
646 &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
647 base::Unretained(database_.get()),
648 ids));
649 }
650 }
651
NotifyUninstallingRegistration(ServiceWorkerRegistration * registration)652 void ServiceWorkerStorage::NotifyUninstallingRegistration(
653 ServiceWorkerRegistration* registration) {
654 DCHECK(uninstalling_registrations_.find(registration->id()) ==
655 uninstalling_registrations_.end());
656 uninstalling_registrations_[registration->id()] = registration;
657 }
658
NotifyDoneUninstallingRegistration(ServiceWorkerRegistration * registration)659 void ServiceWorkerStorage::NotifyDoneUninstallingRegistration(
660 ServiceWorkerRegistration* registration) {
661 uninstalling_registrations_.erase(registration->id());
662 }
663
Disable()664 void ServiceWorkerStorage::Disable() {
665 state_ = DISABLED;
666 if (disk_cache_)
667 disk_cache_->Disable();
668 }
669
IsDisabled() const670 bool ServiceWorkerStorage::IsDisabled() const {
671 return state_ == DISABLED;
672 }
673
PurgeResources(const ResourceList & resources)674 void ServiceWorkerStorage::PurgeResources(const ResourceList& resources) {
675 if (!has_checked_for_stale_resources_)
676 DeleteStaleResources();
677 StartPurgingResources(resources);
678 }
679
ServiceWorkerStorage(const base::FilePath & path,base::WeakPtr<ServiceWorkerContextCore> context,const scoped_refptr<base::SequencedTaskRunner> & database_task_runner,const scoped_refptr<base::SingleThreadTaskRunner> & disk_cache_thread,storage::QuotaManagerProxy * quota_manager_proxy)680 ServiceWorkerStorage::ServiceWorkerStorage(
681 const base::FilePath& path,
682 base::WeakPtr<ServiceWorkerContextCore> context,
683 const scoped_refptr<base::SequencedTaskRunner>& database_task_runner,
684 const scoped_refptr<base::SingleThreadTaskRunner>& disk_cache_thread,
685 storage::QuotaManagerProxy* quota_manager_proxy)
686 : next_registration_id_(kInvalidServiceWorkerRegistrationId),
687 next_version_id_(kInvalidServiceWorkerVersionId),
688 next_resource_id_(kInvalidServiceWorkerResourceId),
689 state_(UNINITIALIZED),
690 path_(path),
691 context_(context),
692 database_task_runner_(database_task_runner),
693 disk_cache_thread_(disk_cache_thread),
694 quota_manager_proxy_(quota_manager_proxy),
695 is_purge_pending_(false),
696 has_checked_for_stale_resources_(false),
697 weak_factory_(this) {
698 database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
699 }
700
GetDatabasePath()701 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
702 if (path_.empty())
703 return base::FilePath();
704 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
705 .Append(kDatabaseName);
706 }
707
GetDiskCachePath()708 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
709 if (path_.empty())
710 return base::FilePath();
711 return path_.Append(ServiceWorkerContextCore::kServiceWorkerDirectory)
712 .Append(kDiskCacheName);
713 }
714
LazyInitialize(const base::Closure & callback)715 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
716 if (!context_)
717 return false;
718
719 switch (state_) {
720 case INITIALIZED:
721 return true;
722 case DISABLED:
723 return false;
724 case INITIALIZING:
725 pending_tasks_.push_back(callback);
726 return false;
727 case UNINITIALIZED:
728 pending_tasks_.push_back(callback);
729 // Fall-through.
730 }
731
732 state_ = INITIALIZING;
733 database_task_runner_->PostTask(
734 FROM_HERE,
735 base::Bind(&ReadInitialDataFromDB,
736 database_.get(),
737 base::MessageLoopProxy::current(),
738 base::Bind(&ServiceWorkerStorage::DidReadInitialData,
739 weak_factory_.GetWeakPtr())));
740 return false;
741 }
742
DidReadInitialData(InitialData * data,ServiceWorkerDatabase::Status status)743 void ServiceWorkerStorage::DidReadInitialData(
744 InitialData* data,
745 ServiceWorkerDatabase::Status status) {
746 DCHECK(data);
747 DCHECK_EQ(INITIALIZING, state_);
748
749 if (status == ServiceWorkerDatabase::STATUS_OK) {
750 next_registration_id_ = data->next_registration_id;
751 next_version_id_ = data->next_version_id;
752 next_resource_id_ = data->next_resource_id;
753 registered_origins_.swap(data->origins);
754 state_ = INITIALIZED;
755 } else {
756 // TODO(nhiroki): Stringify |status| using StatusToString() defined in
757 // service_worker_database.cc.
758 DVLOG(2) << "Failed to initialize: " << status;
759 ScheduleDeleteAndStartOver();
760 }
761
762 for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
763 it != pending_tasks_.end(); ++it) {
764 RunSoon(FROM_HERE, *it);
765 }
766 pending_tasks_.clear();
767 }
768
DidFindRegistrationForDocument(const GURL & document_url,const FindRegistrationCallback & callback,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources,ServiceWorkerDatabase::Status status)769 void ServiceWorkerStorage::DidFindRegistrationForDocument(
770 const GURL& document_url,
771 const FindRegistrationCallback& callback,
772 const ServiceWorkerDatabase::RegistrationData& data,
773 const ResourceList& resources,
774 ServiceWorkerDatabase::Status status) {
775 if (status == ServiceWorkerDatabase::STATUS_OK) {
776 ReturnFoundRegistration(callback, data, resources);
777 TRACE_EVENT_ASYNC_END1(
778 "ServiceWorker",
779 "ServiceWorkerStorage::FindRegistrationForDocument",
780 base::Hash(document_url.spec()),
781 "Status", "OK");
782 return;
783 }
784
785 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
786 // Look for something currently being installed.
787 scoped_refptr<ServiceWorkerRegistration> installing_registration =
788 FindInstallingRegistrationForDocument(document_url);
789 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
790 : SERVICE_WORKER_ERROR_NOT_FOUND,
791 installing_registration);
792 TRACE_EVENT_ASYNC_END1(
793 "ServiceWorker",
794 "ServiceWorkerStorage::FindRegistrationForDocument",
795 base::Hash(document_url.spec()),
796 "Status", status);
797 return;
798 }
799
800 ScheduleDeleteAndStartOver();
801 callback.Run(DatabaseStatusToStatusCode(status),
802 scoped_refptr<ServiceWorkerRegistration>());
803 TRACE_EVENT_ASYNC_END1(
804 "ServiceWorker",
805 "ServiceWorkerStorage::FindRegistrationForDocument",
806 base::Hash(document_url.spec()),
807 "Status", status);
808 }
809
DidFindRegistrationForPattern(const GURL & scope,const FindRegistrationCallback & callback,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources,ServiceWorkerDatabase::Status status)810 void ServiceWorkerStorage::DidFindRegistrationForPattern(
811 const GURL& scope,
812 const FindRegistrationCallback& callback,
813 const ServiceWorkerDatabase::RegistrationData& data,
814 const ResourceList& resources,
815 ServiceWorkerDatabase::Status status) {
816 if (status == ServiceWorkerDatabase::STATUS_OK) {
817 ReturnFoundRegistration(callback, data, resources);
818 return;
819 }
820
821 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
822 scoped_refptr<ServiceWorkerRegistration> installing_registration =
823 FindInstallingRegistrationForPattern(scope);
824 callback.Run(installing_registration.get() ? SERVICE_WORKER_OK
825 : SERVICE_WORKER_ERROR_NOT_FOUND,
826 installing_registration);
827 return;
828 }
829
830 ScheduleDeleteAndStartOver();
831 callback.Run(DatabaseStatusToStatusCode(status),
832 scoped_refptr<ServiceWorkerRegistration>());
833 }
834
DidFindRegistrationForId(const FindRegistrationCallback & callback,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources,ServiceWorkerDatabase::Status status)835 void ServiceWorkerStorage::DidFindRegistrationForId(
836 const FindRegistrationCallback& callback,
837 const ServiceWorkerDatabase::RegistrationData& data,
838 const ResourceList& resources,
839 ServiceWorkerDatabase::Status status) {
840 if (status == ServiceWorkerDatabase::STATUS_OK) {
841 ReturnFoundRegistration(callback, data, resources);
842 return;
843 }
844
845 if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
846 // TODO(nhiroki): Find a registration in |installing_registrations_|.
847 callback.Run(DatabaseStatusToStatusCode(status),
848 scoped_refptr<ServiceWorkerRegistration>());
849 return;
850 }
851
852 ScheduleDeleteAndStartOver();
853 callback.Run(DatabaseStatusToStatusCode(status),
854 scoped_refptr<ServiceWorkerRegistration>());
855 }
856
ReturnFoundRegistration(const FindRegistrationCallback & callback,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources)857 void ServiceWorkerStorage::ReturnFoundRegistration(
858 const FindRegistrationCallback& callback,
859 const ServiceWorkerDatabase::RegistrationData& data,
860 const ResourceList& resources) {
861 scoped_refptr<ServiceWorkerRegistration> registration =
862 GetOrCreateRegistration(data, resources);
863 if (registration->is_deleted()) {
864 // It's past the point of no return and no longer findable.
865 callback.Run(SERVICE_WORKER_ERROR_NOT_FOUND, NULL);
866 return;
867 }
868 callback.Run(SERVICE_WORKER_OK, registration);
869 }
870
DidGetAllRegistrations(const GetAllRegistrationInfosCallback & callback,RegistrationList * registrations,ServiceWorkerDatabase::Status status)871 void ServiceWorkerStorage::DidGetAllRegistrations(
872 const GetAllRegistrationInfosCallback& callback,
873 RegistrationList* registrations,
874 ServiceWorkerDatabase::Status status) {
875 DCHECK(registrations);
876 if (status != ServiceWorkerDatabase::STATUS_OK &&
877 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
878 ScheduleDeleteAndStartOver();
879 callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
880 return;
881 }
882
883 // Add all stored registrations.
884 std::set<int64> pushed_registrations;
885 std::vector<ServiceWorkerRegistrationInfo> infos;
886 for (RegistrationList::const_iterator it = registrations->begin();
887 it != registrations->end(); ++it) {
888 const bool inserted =
889 pushed_registrations.insert(it->registration_id).second;
890 DCHECK(inserted);
891
892 ServiceWorkerRegistration* registration =
893 context_->GetLiveRegistration(it->registration_id);
894 if (registration) {
895 infos.push_back(registration->GetInfo());
896 continue;
897 }
898
899 ServiceWorkerRegistrationInfo info;
900 info.pattern = it->scope;
901 info.registration_id = it->registration_id;
902 if (ServiceWorkerVersion* version =
903 context_->GetLiveVersion(it->version_id)) {
904 if (it->is_active)
905 info.active_version = version->GetInfo();
906 else
907 info.waiting_version = version->GetInfo();
908 infos.push_back(info);
909 continue;
910 }
911
912 if (it->is_active) {
913 info.active_version.is_null = false;
914 info.active_version.status = ServiceWorkerVersion::ACTIVATED;
915 info.active_version.version_id = it->version_id;
916 } else {
917 info.waiting_version.is_null = false;
918 info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
919 info.waiting_version.version_id = it->version_id;
920 }
921 infos.push_back(info);
922 }
923
924 // Add unstored registrations that are being installed.
925 for (RegistrationRefsById::const_iterator it =
926 installing_registrations_.begin();
927 it != installing_registrations_.end(); ++it) {
928 if (pushed_registrations.insert(it->first).second)
929 infos.push_back(it->second->GetInfo());
930 }
931
932 callback.Run(infos);
933 }
934
DidStoreRegistration(const StatusCallback & callback,const GURL & origin,int64 deleted_version_id,const std::vector<int64> & newly_purgeable_resources,ServiceWorkerDatabase::Status status)935 void ServiceWorkerStorage::DidStoreRegistration(
936 const StatusCallback& callback,
937 const GURL& origin,
938 int64 deleted_version_id,
939 const std::vector<int64>& newly_purgeable_resources,
940 ServiceWorkerDatabase::Status status) {
941 if (status != ServiceWorkerDatabase::STATUS_OK) {
942 ScheduleDeleteAndStartOver();
943 callback.Run(DatabaseStatusToStatusCode(status));
944 return;
945 }
946 registered_origins_.insert(origin);
947 callback.Run(SERVICE_WORKER_OK);
948
949 if (!context_ || !context_->GetLiveVersion(deleted_version_id))
950 StartPurgingResources(newly_purgeable_resources);
951 }
952
DidUpdateToActiveState(const StatusCallback & callback,ServiceWorkerDatabase::Status status)953 void ServiceWorkerStorage::DidUpdateToActiveState(
954 const StatusCallback& callback,
955 ServiceWorkerDatabase::Status status) {
956 if (status != ServiceWorkerDatabase::STATUS_OK &&
957 status != ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
958 ScheduleDeleteAndStartOver();
959 }
960 callback.Run(DatabaseStatusToStatusCode(status));
961 }
962
DidDeleteRegistration(const DidDeleteRegistrationParams & params,bool origin_is_deletable,int64 version_id,const std::vector<int64> & newly_purgeable_resources,ServiceWorkerDatabase::Status status)963 void ServiceWorkerStorage::DidDeleteRegistration(
964 const DidDeleteRegistrationParams& params,
965 bool origin_is_deletable,
966 int64 version_id,
967 const std::vector<int64>& newly_purgeable_resources,
968 ServiceWorkerDatabase::Status status) {
969 pending_deletions_.erase(params.registration_id);
970 if (status != ServiceWorkerDatabase::STATUS_OK) {
971 ScheduleDeleteAndStartOver();
972 params.callback.Run(DatabaseStatusToStatusCode(status));
973 return;
974 }
975 if (origin_is_deletable)
976 registered_origins_.erase(params.origin);
977 params.callback.Run(SERVICE_WORKER_OK);
978
979 if (!context_ || !context_->GetLiveVersion(version_id))
980 StartPurgingResources(newly_purgeable_resources);
981 }
982
983 scoped_refptr<ServiceWorkerRegistration>
GetOrCreateRegistration(const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources)984 ServiceWorkerStorage::GetOrCreateRegistration(
985 const ServiceWorkerDatabase::RegistrationData& data,
986 const ResourceList& resources) {
987 scoped_refptr<ServiceWorkerRegistration> registration =
988 context_->GetLiveRegistration(data.registration_id);
989 if (registration.get())
990 return registration;
991
992 registration = new ServiceWorkerRegistration(
993 data.scope, data.registration_id, context_);
994 registration->set_last_update_check(data.last_update_check);
995 if (pending_deletions_.find(data.registration_id) !=
996 pending_deletions_.end()) {
997 registration->set_is_deleted(true);
998 }
999 scoped_refptr<ServiceWorkerVersion> version =
1000 context_->GetLiveVersion(data.version_id);
1001 if (!version.get()) {
1002 version = new ServiceWorkerVersion(
1003 registration.get(), data.script, data.version_id, context_);
1004 version->SetStatus(data.is_active ?
1005 ServiceWorkerVersion::ACTIVATED : ServiceWorkerVersion::INSTALLED);
1006 version->script_cache_map()->SetResources(resources);
1007 }
1008
1009 if (version->status() == ServiceWorkerVersion::ACTIVATED)
1010 registration->SetActiveVersion(version.get());
1011 else if (version->status() == ServiceWorkerVersion::INSTALLED)
1012 registration->SetWaitingVersion(version.get());
1013 else
1014 NOTREACHED();
1015
1016 return registration;
1017 }
1018
1019 ServiceWorkerRegistration*
FindInstallingRegistrationForDocument(const GURL & document_url)1020 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
1021 const GURL& document_url) {
1022 DCHECK(!document_url.has_ref());
1023
1024 LongestScopeMatcher matcher(document_url);
1025 ServiceWorkerRegistration* match = NULL;
1026
1027 // TODO(nhiroki): This searches over installing registrations linearly and it
1028 // couldn't be scalable. Maybe the regs should be partitioned by origin.
1029 for (RegistrationRefsById::const_iterator it =
1030 installing_registrations_.begin();
1031 it != installing_registrations_.end(); ++it) {
1032 if (matcher.MatchLongest(it->second->pattern()))
1033 match = it->second.get();
1034 }
1035 return match;
1036 }
1037
1038 ServiceWorkerRegistration*
FindInstallingRegistrationForPattern(const GURL & scope)1039 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
1040 const GURL& scope) {
1041 for (RegistrationRefsById::const_iterator it =
1042 installing_registrations_.begin();
1043 it != installing_registrations_.end(); ++it) {
1044 if (it->second->pattern() == scope)
1045 return it->second.get();
1046 }
1047 return NULL;
1048 }
1049
1050 ServiceWorkerRegistration*
FindInstallingRegistrationForId(int64 registration_id)1051 ServiceWorkerStorage::FindInstallingRegistrationForId(
1052 int64 registration_id) {
1053 RegistrationRefsById::const_iterator found =
1054 installing_registrations_.find(registration_id);
1055 if (found == installing_registrations_.end())
1056 return NULL;
1057 return found->second.get();
1058 }
1059
disk_cache()1060 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
1061 if (disk_cache_)
1062 return disk_cache_.get();
1063
1064 disk_cache_.reset(new ServiceWorkerDiskCache);
1065
1066 base::FilePath path = GetDiskCachePath();
1067 if (path.empty()) {
1068 int rv = disk_cache_->InitWithMemBackend(kMaxMemDiskCacheSize,
1069 net::CompletionCallback());
1070 DCHECK_EQ(net::OK, rv);
1071 return disk_cache_.get();
1072 }
1073
1074 int rv = disk_cache_->InitWithDiskBackend(
1075 path,
1076 kMaxDiskCacheSize,
1077 false,
1078 disk_cache_thread_,
1079 base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
1080 weak_factory_.GetWeakPtr()));
1081 if (rv != net::ERR_IO_PENDING)
1082 OnDiskCacheInitialized(rv);
1083
1084 return disk_cache_.get();
1085 }
1086
OnDiskCacheInitialized(int rv)1087 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
1088 if (rv != net::OK) {
1089 LOG(ERROR) << "Failed to open the serviceworker diskcache: "
1090 << net::ErrorToString(rv);
1091 ScheduleDeleteAndStartOver();
1092 }
1093 ServiceWorkerMetrics::CountInitDiskCacheResult(rv == net::OK);
1094 }
1095
StartPurgingResources(const std::vector<int64> & ids)1096 void ServiceWorkerStorage::StartPurgingResources(
1097 const std::vector<int64>& ids) {
1098 DCHECK(has_checked_for_stale_resources_);
1099 for (size_t i = 0; i < ids.size(); ++i)
1100 purgeable_resource_ids_.push_back(ids[i]);
1101 ContinuePurgingResources();
1102 }
1103
StartPurgingResources(const ResourceList & resources)1104 void ServiceWorkerStorage::StartPurgingResources(
1105 const ResourceList& resources) {
1106 DCHECK(has_checked_for_stale_resources_);
1107 for (size_t i = 0; i < resources.size(); ++i)
1108 purgeable_resource_ids_.push_back(resources[i].resource_id);
1109 ContinuePurgingResources();
1110 }
1111
ContinuePurgingResources()1112 void ServiceWorkerStorage::ContinuePurgingResources() {
1113 if (purgeable_resource_ids_.empty() || is_purge_pending_)
1114 return;
1115
1116 // Do one at a time until we're done, use RunSoon to avoid recursion when
1117 // DoomEntry returns immediately.
1118 is_purge_pending_ = true;
1119 int64 id = purgeable_resource_ids_.front();
1120 purgeable_resource_ids_.pop_front();
1121 RunSoon(FROM_HERE,
1122 base::Bind(&ServiceWorkerStorage::PurgeResource,
1123 weak_factory_.GetWeakPtr(), id));
1124 }
1125
PurgeResource(int64 id)1126 void ServiceWorkerStorage::PurgeResource(int64 id) {
1127 DCHECK(is_purge_pending_);
1128 int rv = disk_cache()->DoomEntry(
1129 id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
1130 weak_factory_.GetWeakPtr(), id));
1131 if (rv != net::ERR_IO_PENDING)
1132 OnResourcePurged(id, rv);
1133 }
1134
OnResourcePurged(int64 id,int rv)1135 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
1136 DCHECK(is_purge_pending_);
1137 is_purge_pending_ = false;
1138
1139 database_task_runner_->PostTask(
1140 FROM_HERE,
1141 base::Bind(base::IgnoreResult(
1142 &ServiceWorkerDatabase::ClearPurgeableResourceIds),
1143 base::Unretained(database_.get()),
1144 std::set<int64>(&id, &id + 1)));
1145
1146 ContinuePurgingResources();
1147 }
1148
DeleteStaleResources()1149 void ServiceWorkerStorage::DeleteStaleResources() {
1150 DCHECK(!has_checked_for_stale_resources_);
1151 has_checked_for_stale_resources_ = true;
1152 database_task_runner_->PostTask(
1153 FROM_HERE,
1154 base::Bind(&ServiceWorkerStorage::CollectStaleResourcesFromDB,
1155 database_.get(),
1156 base::MessageLoopProxy::current(),
1157 base::Bind(&ServiceWorkerStorage::DidCollectStaleResources,
1158 weak_factory_.GetWeakPtr())));
1159 }
1160
DidCollectStaleResources(const std::vector<int64> & stale_resource_ids,ServiceWorkerDatabase::Status status)1161 void ServiceWorkerStorage::DidCollectStaleResources(
1162 const std::vector<int64>& stale_resource_ids,
1163 ServiceWorkerDatabase::Status status) {
1164 DCHECK_EQ(ServiceWorkerDatabase::STATUS_OK, status);
1165 if (status != ServiceWorkerDatabase::STATUS_OK)
1166 return;
1167 StartPurgingResources(stale_resource_ids);
1168 }
1169
CollectStaleResourcesFromDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const GetResourcesCallback & callback)1170 void ServiceWorkerStorage::CollectStaleResourcesFromDB(
1171 ServiceWorkerDatabase* database,
1172 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1173 const GetResourcesCallback& callback) {
1174 std::set<int64> ids;
1175 ServiceWorkerDatabase::Status status =
1176 database->GetUncommittedResourceIds(&ids);
1177 if (status != ServiceWorkerDatabase::STATUS_OK) {
1178 original_task_runner->PostTask(
1179 FROM_HERE,
1180 base::Bind(
1181 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1182 return;
1183 }
1184
1185 status = database->PurgeUncommittedResourceIds(ids);
1186 if (status != ServiceWorkerDatabase::STATUS_OK) {
1187 original_task_runner->PostTask(
1188 FROM_HERE,
1189 base::Bind(
1190 callback, std::vector<int64>(ids.begin(), ids.end()), status));
1191 return;
1192 }
1193
1194 ids.clear();
1195 status = database->GetPurgeableResourceIds(&ids);
1196 original_task_runner->PostTask(
1197 FROM_HERE,
1198 base::Bind(callback, std::vector<int64>(ids.begin(), ids.end()), status));
1199 }
1200
ReadInitialDataFromDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const InitializeCallback & callback)1201 void ServiceWorkerStorage::ReadInitialDataFromDB(
1202 ServiceWorkerDatabase* database,
1203 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1204 const InitializeCallback& callback) {
1205 DCHECK(database);
1206 scoped_ptr<ServiceWorkerStorage::InitialData> data(
1207 new ServiceWorkerStorage::InitialData());
1208
1209 ServiceWorkerDatabase::Status status =
1210 database->GetNextAvailableIds(&data->next_registration_id,
1211 &data->next_version_id,
1212 &data->next_resource_id);
1213 if (status != ServiceWorkerDatabase::STATUS_OK) {
1214 original_task_runner->PostTask(
1215 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1216 return;
1217 }
1218
1219 status = database->GetOriginsWithRegistrations(&data->origins);
1220 original_task_runner->PostTask(
1221 FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
1222 }
1223
DeleteRegistrationFromDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,int64 registration_id,const GURL & origin,const DeleteRegistrationCallback & callback)1224 void ServiceWorkerStorage::DeleteRegistrationFromDB(
1225 ServiceWorkerDatabase* database,
1226 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1227 int64 registration_id,
1228 const GURL& origin,
1229 const DeleteRegistrationCallback& callback) {
1230 DCHECK(database);
1231
1232 int64 version_id = kInvalidServiceWorkerVersionId;
1233 std::vector<int64> newly_purgeable_resources;
1234 ServiceWorkerDatabase::Status status = database->DeleteRegistration(
1235 registration_id, origin, &version_id, &newly_purgeable_resources);
1236 if (status != ServiceWorkerDatabase::STATUS_OK) {
1237 original_task_runner->PostTask(FROM_HERE,
1238 base::Bind(callback,
1239 false,
1240 kInvalidServiceWorkerVersionId,
1241 std::vector<int64>(),
1242 status));
1243 return;
1244 }
1245
1246 // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
1247 // unique origin list.
1248 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1249 status = database->GetRegistrationsForOrigin(origin, ®istrations);
1250 if (status != ServiceWorkerDatabase::STATUS_OK) {
1251 original_task_runner->PostTask(FROM_HERE,
1252 base::Bind(callback,
1253 false,
1254 kInvalidServiceWorkerVersionId,
1255 std::vector<int64>(),
1256 status));
1257 return;
1258 }
1259
1260 bool deletable = registrations.empty();
1261 original_task_runner->PostTask(
1262 FROM_HERE,
1263 base::Bind(
1264 callback, deletable, version_id, newly_purgeable_resources, status));
1265 }
1266
WriteRegistrationInDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources,const WriteRegistrationCallback & callback)1267 void ServiceWorkerStorage::WriteRegistrationInDB(
1268 ServiceWorkerDatabase* database,
1269 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1270 const ServiceWorkerDatabase::RegistrationData& data,
1271 const ResourceList& resources,
1272 const WriteRegistrationCallback& callback) {
1273 DCHECK(database);
1274 int64 deleted_version_id = kInvalidServiceWorkerVersionId;
1275 std::vector<int64> newly_purgeable_resources;
1276 ServiceWorkerDatabase::Status status = database->WriteRegistration(
1277 data, resources, &deleted_version_id, &newly_purgeable_resources);
1278 original_task_runner->PostTask(FROM_HERE,
1279 base::Bind(callback,
1280 data.script.GetOrigin(),
1281 deleted_version_id,
1282 newly_purgeable_resources,
1283 status));
1284 }
1285
FindForDocumentInDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const GURL & document_url,const FindInDBCallback & callback)1286 void ServiceWorkerStorage::FindForDocumentInDB(
1287 ServiceWorkerDatabase* database,
1288 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1289 const GURL& document_url,
1290 const FindInDBCallback& callback) {
1291 GURL origin = document_url.GetOrigin();
1292 RegistrationList registrations;
1293 ServiceWorkerDatabase::Status status =
1294 database->GetRegistrationsForOrigin(origin, ®istrations);
1295 if (status != ServiceWorkerDatabase::STATUS_OK) {
1296 original_task_runner->PostTask(
1297 FROM_HERE,
1298 base::Bind(callback,
1299 ServiceWorkerDatabase::RegistrationData(),
1300 ResourceList(),
1301 status));
1302 return;
1303 }
1304
1305 ServiceWorkerDatabase::RegistrationData data;
1306 ResourceList resources;
1307 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1308
1309 // Find one with a pattern match.
1310 LongestScopeMatcher matcher(document_url);
1311 int64 match = kInvalidServiceWorkerRegistrationId;
1312 for (size_t i = 0; i < registrations.size(); ++i) {
1313 if (matcher.MatchLongest(registrations[i].scope))
1314 match = registrations[i].registration_id;
1315 }
1316
1317 if (match != kInvalidServiceWorkerRegistrationId)
1318 status = database->ReadRegistration(match, origin, &data, &resources);
1319
1320 original_task_runner->PostTask(
1321 FROM_HERE,
1322 base::Bind(callback, data, resources, status));
1323 }
1324
FindForPatternInDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const GURL & scope,const FindInDBCallback & callback)1325 void ServiceWorkerStorage::FindForPatternInDB(
1326 ServiceWorkerDatabase* database,
1327 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1328 const GURL& scope,
1329 const FindInDBCallback& callback) {
1330 GURL origin = scope.GetOrigin();
1331 std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
1332 ServiceWorkerDatabase::Status status =
1333 database->GetRegistrationsForOrigin(origin, ®istrations);
1334 if (status != ServiceWorkerDatabase::STATUS_OK) {
1335 original_task_runner->PostTask(
1336 FROM_HERE,
1337 base::Bind(callback,
1338 ServiceWorkerDatabase::RegistrationData(),
1339 ResourceList(),
1340 status));
1341 return;
1342 }
1343
1344 // Find one with an exact matching scope.
1345 ServiceWorkerDatabase::RegistrationData data;
1346 ResourceList resources;
1347 status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
1348 for (RegistrationList::const_iterator it = registrations.begin();
1349 it != registrations.end(); ++it) {
1350 if (scope != it->scope)
1351 continue;
1352 status = database->ReadRegistration(it->registration_id, origin,
1353 &data, &resources);
1354 break; // We're done looping.
1355 }
1356
1357 original_task_runner->PostTask(
1358 FROM_HERE,
1359 base::Bind(callback, data, resources, status));
1360 }
1361
FindForIdInDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,int64 registration_id,const GURL & origin,const FindInDBCallback & callback)1362 void ServiceWorkerStorage::FindForIdInDB(
1363 ServiceWorkerDatabase* database,
1364 scoped_refptr<base::SequencedTaskRunner> original_task_runner,
1365 int64 registration_id,
1366 const GURL& origin,
1367 const FindInDBCallback& callback) {
1368 ServiceWorkerDatabase::RegistrationData data;
1369 ResourceList resources;
1370 ServiceWorkerDatabase::Status status =
1371 database->ReadRegistration(registration_id, origin, &data, &resources);
1372 original_task_runner->PostTask(
1373 FROM_HERE, base::Bind(callback, data, resources, status));
1374 }
1375
1376 // TODO(nhiroki): The corruption recovery should not be scheduled if the error
1377 // is transient and it can get healed soon (e.g. IO error). To do that, the
1378 // database should not disable itself when an error occurs and the storage
1379 // controls it instead.
ScheduleDeleteAndStartOver()1380 void ServiceWorkerStorage::ScheduleDeleteAndStartOver() {
1381 if (state_ == DISABLED) {
1382 // Recovery process has already been scheduled.
1383 return;
1384 }
1385 Disable();
1386
1387 DVLOG(1) << "Schedule to delete the context and start over.";
1388 context_->ScheduleDeleteAndStartOver();
1389 }
1390
DidDeleteDatabase(const StatusCallback & callback,ServiceWorkerDatabase::Status status)1391 void ServiceWorkerStorage::DidDeleteDatabase(
1392 const StatusCallback& callback,
1393 ServiceWorkerDatabase::Status status) {
1394 DCHECK_EQ(DISABLED, state_);
1395 if (status != ServiceWorkerDatabase::STATUS_OK) {
1396 // Give up the corruption recovery until the browser restarts.
1397 LOG(ERROR) << "Failed to delete the database: " << status;
1398 callback.Run(DatabaseStatusToStatusCode(status));
1399 return;
1400 }
1401 DVLOG(1) << "Deleted ServiceWorkerDatabase successfully.";
1402
1403 // Delete the disk cache on the cache thread.
1404 // TODO(nhiroki): What if there is a bunch of files in the cache directory?
1405 // Deleting the directory could take a long time and restart could be delayed.
1406 // We should probably rename the directory and delete it later.
1407 PostTaskAndReplyWithResult(
1408 database_task_runner_.get(),
1409 FROM_HERE,
1410 base::Bind(&base::DeleteFile, GetDiskCachePath(), true),
1411 base::Bind(&ServiceWorkerStorage::DidDeleteDiskCache,
1412 weak_factory_.GetWeakPtr(),
1413 callback));
1414 }
1415
DidDeleteDiskCache(const StatusCallback & callback,bool result)1416 void ServiceWorkerStorage::DidDeleteDiskCache(
1417 const StatusCallback& callback, bool result) {
1418 DCHECK_EQ(DISABLED, state_);
1419 if (!result) {
1420 // Give up the corruption recovery until the browser restarts.
1421 LOG(ERROR) << "Failed to delete the diskcache.";
1422 callback.Run(SERVICE_WORKER_ERROR_FAILED);
1423 return;
1424 }
1425 DVLOG(1) << "Deleted ServiceWorkerDiskCache successfully.";
1426 callback.Run(SERVICE_WORKER_OK);
1427 }
1428
1429 } // namespace content
1430