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