• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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/message_loop/message_loop.h"
11 #include "base/sequenced_task_runner.h"
12 #include "base/task_runner_util.h"
13 #include "content/browser/service_worker/service_worker_context_core.h"
14 #include "content/browser/service_worker/service_worker_disk_cache.h"
15 #include "content/browser/service_worker/service_worker_histograms.h"
16 #include "content/browser/service_worker/service_worker_info.h"
17 #include "content/browser/service_worker/service_worker_registration.h"
18 #include "content/browser/service_worker/service_worker_utils.h"
19 #include "content/browser/service_worker/service_worker_version.h"
20 #include "content/common/service_worker/service_worker_types.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "net/base/net_errors.h"
23 #include "webkit/browser/quota/quota_manager_proxy.h"
24 
25 namespace content {
26 
27 namespace {
28 
RunSoon(const tracked_objects::Location & from_here,const base::Closure & closure)29 void RunSoon(const tracked_objects::Location& from_here,
30              const base::Closure& closure) {
31   base::MessageLoop::current()->PostTask(from_here, closure);
32 }
33 
CompleteFindNow(const scoped_refptr<ServiceWorkerRegistration> & registration,ServiceWorkerStatusCode status,const ServiceWorkerStorage::FindRegistrationCallback & callback)34 void CompleteFindNow(
35     const scoped_refptr<ServiceWorkerRegistration>& registration,
36     ServiceWorkerStatusCode status,
37     const ServiceWorkerStorage::FindRegistrationCallback& callback) {
38   callback.Run(status, registration);
39 }
40 
CompleteFindSoon(const tracked_objects::Location & from_here,const scoped_refptr<ServiceWorkerRegistration> & registration,ServiceWorkerStatusCode status,const ServiceWorkerStorage::FindRegistrationCallback & callback)41 void CompleteFindSoon(
42     const tracked_objects::Location& from_here,
43     const scoped_refptr<ServiceWorkerRegistration>& registration,
44     ServiceWorkerStatusCode status,
45     const ServiceWorkerStorage::FindRegistrationCallback& callback) {
46   RunSoon(from_here, base::Bind(callback, status, registration));
47 }
48 
49 const base::FilePath::CharType kServiceWorkerDirectory[] =
50     FILE_PATH_LITERAL("Service Worker");
51 const base::FilePath::CharType kDatabaseName[] =
52     FILE_PATH_LITERAL("Database");
53 const base::FilePath::CharType kDiskCacheName[] =
54     FILE_PATH_LITERAL("Cache");
55 
56 const int kMaxMemDiskCacheSize = 10 * 1024 * 1024;
57 const int kMaxDiskCacheSize = 250 * 1024 * 1024;
58 
EmptyCompletionCallback(int)59 void EmptyCompletionCallback(int) {}
60 
DatabaseStatusToStatusCode(ServiceWorkerDatabase::Status status)61 ServiceWorkerStatusCode DatabaseStatusToStatusCode(
62     ServiceWorkerDatabase::Status status) {
63   switch (status) {
64     case ServiceWorkerDatabase::STATUS_OK:
65       return SERVICE_WORKER_OK;
66     case ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND:
67       return SERVICE_WORKER_ERROR_NOT_FOUND;
68     case ServiceWorkerDatabase::STATUS_ERROR_MAX:
69       NOTREACHED();
70     default:
71       return SERVICE_WORKER_ERROR_FAILED;
72   }
73 }
74 
75 }  // namespace
76 
InitialData()77 ServiceWorkerStorage::InitialData::InitialData()
78     : next_registration_id(kInvalidServiceWorkerRegistrationId),
79       next_version_id(kInvalidServiceWorkerVersionId),
80       next_resource_id(kInvalidServiceWorkerResourceId) {
81 }
82 
~InitialData()83 ServiceWorkerStorage::InitialData::~InitialData() {
84 }
85 
ServiceWorkerStorage(const base::FilePath & path,base::WeakPtr<ServiceWorkerContextCore> context,base::SequencedTaskRunner * database_task_runner,base::MessageLoopProxy * disk_cache_thread,quota::QuotaManagerProxy * quota_manager_proxy)86 ServiceWorkerStorage::ServiceWorkerStorage(
87     const base::FilePath& path,
88     base::WeakPtr<ServiceWorkerContextCore> context,
89     base::SequencedTaskRunner* database_task_runner,
90     base::MessageLoopProxy* disk_cache_thread,
91     quota::QuotaManagerProxy* quota_manager_proxy)
92     : next_registration_id_(kInvalidServiceWorkerRegistrationId),
93       next_version_id_(kInvalidServiceWorkerVersionId),
94       next_resource_id_(kInvalidServiceWorkerResourceId),
95       state_(UNINITIALIZED),
96       context_(context),
97       database_task_runner_(database_task_runner),
98       disk_cache_thread_(disk_cache_thread),
99       quota_manager_proxy_(quota_manager_proxy),
100       is_purge_pending_(false),
101       weak_factory_(this) {
102   if (!path.empty())
103     path_ = path.Append(kServiceWorkerDirectory);
104   database_.reset(new ServiceWorkerDatabase(GetDatabasePath()));
105 }
106 
~ServiceWorkerStorage()107 ServiceWorkerStorage::~ServiceWorkerStorage() {
108   weak_factory_.InvalidateWeakPtrs();
109   database_task_runner_->DeleteSoon(FROM_HERE, database_.release());
110 }
111 
FindRegistrationForDocument(const GURL & document_url,const FindRegistrationCallback & callback)112 void ServiceWorkerStorage::FindRegistrationForDocument(
113     const GURL& document_url,
114     const FindRegistrationCallback& callback) {
115   DCHECK(!document_url.has_ref());
116   if (!LazyInitialize(base::Bind(
117           &ServiceWorkerStorage::FindRegistrationForDocument,
118           weak_factory_.GetWeakPtr(), document_url, callback))) {
119     if (state_ != INITIALIZING || !context_) {
120       CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
121                       SERVICE_WORKER_ERROR_FAILED, callback);
122     }
123     return;
124   }
125   DCHECK_EQ(INITIALIZED, state_);
126 
127   // See if there are any stored registrations for the origin.
128   if (!ContainsKey(registered_origins_, document_url.GetOrigin())) {
129     // Look for something currently being installed.
130     scoped_refptr<ServiceWorkerRegistration> installing_registration =
131         FindInstallingRegistrationForDocument(document_url);
132     CompleteFindNow(
133         installing_registration,
134         installing_registration ?
135             SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
136         callback);
137     return;
138   }
139 
140   database_task_runner_->PostTask(
141       FROM_HERE,
142       base::Bind(
143           &FindForDocumentInDB,
144           database_.get(),
145           base::MessageLoopProxy::current(),
146           document_url,
147           base::Bind(&ServiceWorkerStorage::DidFindRegistrationForDocument,
148                      weak_factory_.GetWeakPtr(), document_url, callback)));
149 }
150 
FindRegistrationForPattern(const GURL & scope,const FindRegistrationCallback & callback)151 void ServiceWorkerStorage::FindRegistrationForPattern(
152     const GURL& scope,
153     const FindRegistrationCallback& callback) {
154   if (!LazyInitialize(base::Bind(
155           &ServiceWorkerStorage::FindRegistrationForPattern,
156           weak_factory_.GetWeakPtr(), scope, callback))) {
157     if (state_ != INITIALIZING || !context_) {
158       CompleteFindSoon(FROM_HERE, scoped_refptr<ServiceWorkerRegistration>(),
159                        SERVICE_WORKER_ERROR_FAILED, callback);
160     }
161     return;
162   }
163   DCHECK_EQ(INITIALIZED, state_);
164 
165   // See if there are any stored registrations for the origin.
166   if (!ContainsKey(registered_origins_, scope.GetOrigin())) {
167     // Look for something currently being installed.
168     scoped_refptr<ServiceWorkerRegistration> installing_registration =
169         FindInstallingRegistrationForPattern(scope);
170     CompleteFindSoon(
171         FROM_HERE, installing_registration,
172         installing_registration ?
173             SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
174         callback);
175     return;
176   }
177 
178   database_task_runner_->PostTask(
179       FROM_HERE,
180       base::Bind(
181           &FindForPatternInDB,
182           database_.get(),
183           base::MessageLoopProxy::current(),
184           scope,
185           base::Bind(&ServiceWorkerStorage::DidFindRegistrationForPattern,
186                      weak_factory_.GetWeakPtr(), scope, callback)));
187 }
188 
FindRegistrationForId(int64 registration_id,const GURL & origin,const FindRegistrationCallback & callback)189 void ServiceWorkerStorage::FindRegistrationForId(
190     int64 registration_id,
191     const GURL& origin,
192     const FindRegistrationCallback& callback) {
193   if (!LazyInitialize(base::Bind(
194           &ServiceWorkerStorage::FindRegistrationForId,
195           weak_factory_.GetWeakPtr(), registration_id, origin, callback))) {
196     if (state_ != INITIALIZING || !context_) {
197       CompleteFindNow(scoped_refptr<ServiceWorkerRegistration>(),
198                       SERVICE_WORKER_ERROR_FAILED, callback);
199     }
200     return;
201   }
202   DCHECK_EQ(INITIALIZED, state_);
203 
204   // See if there are any stored registrations for the origin.
205   if (!ContainsKey(registered_origins_, origin)) {
206     // Look for something currently being installed.
207     scoped_refptr<ServiceWorkerRegistration> installing_registration =
208         FindInstallingRegistrationForId(registration_id);
209     CompleteFindNow(
210         installing_registration,
211         installing_registration ?
212             SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
213         callback);
214     return;
215   }
216 
217   scoped_refptr<ServiceWorkerRegistration> registration =
218       context_->GetLiveRegistration(registration_id);
219   if (registration) {
220     CompleteFindNow(registration, SERVICE_WORKER_OK, callback);
221     return;
222   }
223 
224   database_task_runner_->PostTask(
225       FROM_HERE,
226       base::Bind(&FindForIdInDB,
227                  database_.get(),
228                  base::MessageLoopProxy::current(),
229                  registration_id, origin,
230                  base::Bind(&ServiceWorkerStorage::DidFindRegistrationForId,
231                             weak_factory_.GetWeakPtr(), callback)));
232 }
233 
GetAllRegistrations(const GetAllRegistrationInfosCallback & callback)234 void ServiceWorkerStorage::GetAllRegistrations(
235     const GetAllRegistrationInfosCallback& callback) {
236   if (!LazyInitialize(base::Bind(
237           &ServiceWorkerStorage::GetAllRegistrations,
238           weak_factory_.GetWeakPtr(), callback))) {
239     if (state_ != INITIALIZING || !context_) {
240       RunSoon(FROM_HERE, base::Bind(
241           callback, std::vector<ServiceWorkerRegistrationInfo>()));
242     }
243     return;
244   }
245   DCHECK_EQ(INITIALIZED, state_);
246 
247   RegistrationList* registrations = new RegistrationList;
248   PostTaskAndReplyWithResult(
249       database_task_runner_,
250       FROM_HERE,
251       base::Bind(&ServiceWorkerDatabase::GetAllRegistrations,
252                  base::Unretained(database_.get()),
253                  base::Unretained(registrations)),
254       base::Bind(&ServiceWorkerStorage::DidGetAllRegistrations,
255                  weak_factory_.GetWeakPtr(),
256                  callback,
257                  base::Owned(registrations)));
258 }
259 
StoreRegistration(ServiceWorkerRegistration * registration,ServiceWorkerVersion * version,const StatusCallback & callback)260 void ServiceWorkerStorage::StoreRegistration(
261     ServiceWorkerRegistration* registration,
262     ServiceWorkerVersion* version,
263     const StatusCallback& callback) {
264   DCHECK(registration);
265   DCHECK(version);
266 
267   DCHECK(state_ == INITIALIZED || state_ == DISABLED);
268   if (state_ != INITIALIZED || !context_) {
269     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
270     return;
271   }
272 
273   ServiceWorkerDatabase::RegistrationData data;
274   data.registration_id = registration->id();
275   data.scope = registration->pattern();
276   data.script = registration->script_url();
277   data.has_fetch_handler = true;
278   data.version_id = version->version_id();
279   data.last_update_check = base::Time::Now();
280   data.is_active = false;  // initially stored in the waiting state
281 
282   ResourceList resources;
283   version->script_cache_map()->GetResources(&resources);
284 
285   database_task_runner_->PostTask(
286       FROM_HERE,
287       base::Bind(&WriteRegistrationInDB,
288                  database_.get(),
289                  base::MessageLoopProxy::current(),
290                  data, resources,
291                  base::Bind(&ServiceWorkerStorage::DidStoreRegistration,
292                             weak_factory_.GetWeakPtr(),
293                             callback)));
294 }
295 
UpdateToActiveState(ServiceWorkerRegistration * registration,const StatusCallback & callback)296 void ServiceWorkerStorage::UpdateToActiveState(
297     ServiceWorkerRegistration* registration,
298     const StatusCallback& callback) {
299   DCHECK(registration);
300 
301   DCHECK(state_ == INITIALIZED || state_ == DISABLED);
302   if (state_ != INITIALIZED || !context_) {
303     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
304     return;
305   }
306 
307   PostTaskAndReplyWithResult(
308       database_task_runner_,
309       FROM_HERE,
310       base::Bind(&ServiceWorkerDatabase::UpdateVersionToActive,
311                  base::Unretained(database_.get()),
312                  registration->id(),
313                  registration->script_url().GetOrigin()),
314       base::Bind(&ServiceWorkerStorage::DidUpdateToActiveState,
315                  weak_factory_.GetWeakPtr(),
316                  callback));
317 }
318 
DeleteRegistration(int64 registration_id,const GURL & origin,const StatusCallback & callback)319 void ServiceWorkerStorage::DeleteRegistration(
320     int64 registration_id,
321     const GURL& origin,
322     const StatusCallback& callback) {
323   DCHECK(state_ == INITIALIZED || state_ == DISABLED);
324   if (state_ != INITIALIZED || !context_) {
325     RunSoon(FROM_HERE, base::Bind(callback, SERVICE_WORKER_ERROR_FAILED));
326     return;
327   }
328 
329   database_task_runner_->PostTask(
330       FROM_HERE,
331       base::Bind(&DeleteRegistrationFromDB,
332                  database_.get(),
333                  base::MessageLoopProxy::current(),
334                  registration_id, origin,
335                  base::Bind(&ServiceWorkerStorage::DidDeleteRegistration,
336                             weak_factory_.GetWeakPtr(), origin, callback)));
337 
338   // TODO(michaeln): Either its instance should also be
339   // removed from liveregistrations map or the live object
340   // should marked as deleted in some way and not 'findable'
341   // thereafter.
342 }
343 
344 scoped_ptr<ServiceWorkerResponseReader>
CreateResponseReader(int64 response_id)345 ServiceWorkerStorage::CreateResponseReader(int64 response_id) {
346   return make_scoped_ptr(
347       new ServiceWorkerResponseReader(response_id, disk_cache()));
348 }
349 
350 scoped_ptr<ServiceWorkerResponseWriter>
CreateResponseWriter(int64 response_id)351 ServiceWorkerStorage::CreateResponseWriter(int64 response_id) {
352   return make_scoped_ptr(
353       new ServiceWorkerResponseWriter(response_id, disk_cache()));
354 }
355 
StoreUncommittedReponseId(int64 id)356 void ServiceWorkerStorage::StoreUncommittedReponseId(int64 id) {
357   DCHECK_NE(kInvalidServiceWorkerResponseId, id);
358   database_task_runner_->PostTask(
359       FROM_HERE,
360       base::Bind(base::IgnoreResult(
361           &ServiceWorkerDatabase::WriteUncommittedResourceIds),
362           base::Unretained(database_.get()),
363           std::set<int64>(&id, &id + 1)));
364 }
365 
DoomUncommittedResponse(int64 id)366 void ServiceWorkerStorage::DoomUncommittedResponse(int64 id) {
367   DCHECK_NE(kInvalidServiceWorkerResponseId, id);
368   database_task_runner_->PostTask(
369       FROM_HERE,
370       base::Bind(base::IgnoreResult(
371           &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
372           base::Unretained(database_.get()),
373           std::set<int64>(&id, &id + 1)));
374   StartPurgingResources(std::vector<int64>(1, id));
375 }
376 
NewRegistrationId()377 int64 ServiceWorkerStorage::NewRegistrationId() {
378   if (state_ == DISABLED)
379     return kInvalidServiceWorkerRegistrationId;
380   DCHECK_EQ(INITIALIZED, state_);
381   return next_registration_id_++;
382 }
383 
NewVersionId()384 int64 ServiceWorkerStorage::NewVersionId() {
385   if (state_ == DISABLED)
386     return kInvalidServiceWorkerVersionId;
387   DCHECK_EQ(INITIALIZED, state_);
388   return next_version_id_++;
389 }
390 
NewResourceId()391 int64 ServiceWorkerStorage::NewResourceId() {
392   if (state_ == DISABLED)
393     return kInvalidServiceWorkerResourceId;
394   DCHECK_EQ(INITIALIZED, state_);
395   return next_resource_id_++;
396 }
397 
NotifyInstallingRegistration(ServiceWorkerRegistration * registration)398 void ServiceWorkerStorage::NotifyInstallingRegistration(
399       ServiceWorkerRegistration* registration) {
400   installing_registrations_[registration->id()] = registration;
401 }
402 
NotifyDoneInstallingRegistration(ServiceWorkerRegistration * registration,ServiceWorkerVersion * version,ServiceWorkerStatusCode status)403 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
404       ServiceWorkerRegistration* registration,
405       ServiceWorkerVersion* version,
406       ServiceWorkerStatusCode status) {
407   installing_registrations_.erase(registration->id());
408   if (status != SERVICE_WORKER_OK && version) {
409     ResourceList resources;
410     version->script_cache_map()->GetResources(&resources);
411 
412     std::set<int64> ids;
413     for (size_t i = 0; i < resources.size(); ++i)
414       ids.insert(resources[i].resource_id);
415 
416     database_task_runner_->PostTask(
417         FROM_HERE,
418         base::Bind(base::IgnoreResult(
419             &ServiceWorkerDatabase::PurgeUncommittedResourceIds),
420             base::Unretained(database_.get()),
421             ids));
422 
423     StartPurgingResources(resources);
424   }
425 }
426 
GetDatabasePath()427 base::FilePath ServiceWorkerStorage::GetDatabasePath() {
428   if (path_.empty())
429     return base::FilePath();
430   return path_.Append(kDatabaseName);
431 }
432 
GetDiskCachePath()433 base::FilePath ServiceWorkerStorage::GetDiskCachePath() {
434   if (path_.empty())
435     return base::FilePath();
436   return path_.Append(kDiskCacheName);
437 }
438 
LazyInitialize(const base::Closure & callback)439 bool ServiceWorkerStorage::LazyInitialize(const base::Closure& callback) {
440   if (!context_)
441     return false;
442 
443   switch (state_) {
444     case INITIALIZED:
445       return true;
446     case DISABLED:
447       return false;
448     case INITIALIZING:
449       pending_tasks_.push_back(callback);
450       return false;
451     case UNINITIALIZED:
452       pending_tasks_.push_back(callback);
453       // Fall-through.
454   }
455 
456   state_ = INITIALIZING;
457   database_task_runner_->PostTask(
458       FROM_HERE,
459       base::Bind(&ReadInitialDataFromDB,
460                  database_.get(),
461                  base::MessageLoopProxy::current(),
462                  base::Bind(&ServiceWorkerStorage::DidReadInitialData,
463                             weak_factory_.GetWeakPtr())));
464   return false;
465 }
466 
DidReadInitialData(InitialData * data,ServiceWorkerDatabase::Status status)467 void ServiceWorkerStorage::DidReadInitialData(
468     InitialData* data,
469     ServiceWorkerDatabase::Status status) {
470   DCHECK(data);
471   DCHECK_EQ(INITIALIZING, state_);
472 
473   if (status == ServiceWorkerDatabase::STATUS_OK) {
474     next_registration_id_ = data->next_registration_id;
475     next_version_id_ = data->next_version_id;
476     next_resource_id_ = data->next_resource_id;
477     registered_origins_.swap(data->origins);
478     state_ = INITIALIZED;
479   } else {
480     // TODO(nhiroki): If status==STATUS_ERROR_CORRUPTED, do corruption recovery
481     // (http://crbug.com/371675).
482     DLOG(WARNING) << "Failed to initialize: " << status;
483     state_ = DISABLED;
484   }
485 
486   for (std::vector<base::Closure>::const_iterator it = pending_tasks_.begin();
487        it != pending_tasks_.end(); ++it) {
488     RunSoon(FROM_HERE, *it);
489   }
490   pending_tasks_.clear();
491 }
492 
DidFindRegistrationForDocument(const GURL & document_url,const FindRegistrationCallback & callback,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources,ServiceWorkerDatabase::Status status)493 void ServiceWorkerStorage::DidFindRegistrationForDocument(
494     const GURL& document_url,
495     const FindRegistrationCallback& callback,
496     const ServiceWorkerDatabase::RegistrationData& data,
497     const ResourceList& resources,
498     ServiceWorkerDatabase::Status status) {
499   if (status == ServiceWorkerDatabase::STATUS_OK) {
500     callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
501     return;
502   }
503 
504   if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
505     // Look for something currently being installed.
506     scoped_refptr<ServiceWorkerRegistration> installing_registration =
507         FindInstallingRegistrationForDocument(document_url);
508     callback.Run(installing_registration ?
509         SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
510         installing_registration);
511     return;
512   }
513 
514   // TODO(nhiroki): Handle database error (http://crbug.com/371675).
515   callback.Run(DatabaseStatusToStatusCode(status),
516                scoped_refptr<ServiceWorkerRegistration>());
517 }
518 
DidFindRegistrationForPattern(const GURL & scope,const FindRegistrationCallback & callback,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources,ServiceWorkerDatabase::Status status)519 void ServiceWorkerStorage::DidFindRegistrationForPattern(
520     const GURL& scope,
521     const FindRegistrationCallback& callback,
522     const ServiceWorkerDatabase::RegistrationData& data,
523     const ResourceList& resources,
524     ServiceWorkerDatabase::Status status) {
525   if (status == ServiceWorkerDatabase::STATUS_OK) {
526     callback.Run(SERVICE_WORKER_OK, GetOrCreateRegistration(data, resources));
527     return;
528   }
529 
530   if (status == ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND) {
531     scoped_refptr<ServiceWorkerRegistration> installing_registration =
532         FindInstallingRegistrationForPattern(scope);
533     callback.Run(installing_registration ?
534         SERVICE_WORKER_OK : SERVICE_WORKER_ERROR_NOT_FOUND,
535         installing_registration);
536     return;
537   }
538 
539   // TODO(nhiroki): Handle database error (http://crbug.com/371675).
540   callback.Run(DatabaseStatusToStatusCode(status),
541                scoped_refptr<ServiceWorkerRegistration>());
542 }
543 
DidFindRegistrationForId(const FindRegistrationCallback & callback,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources,ServiceWorkerDatabase::Status status)544 void ServiceWorkerStorage::DidFindRegistrationForId(
545     const FindRegistrationCallback& callback,
546     const ServiceWorkerDatabase::RegistrationData& data,
547     const ResourceList& resources,
548     ServiceWorkerDatabase::Status status) {
549   if (status == ServiceWorkerDatabase::STATUS_OK) {
550     callback.Run(SERVICE_WORKER_OK,
551                  GetOrCreateRegistration(data, resources));
552     return;
553   }
554   // TODO(nhiroki): Handle database error (http://crbug.com/371675).
555   callback.Run(DatabaseStatusToStatusCode(status),
556                scoped_refptr<ServiceWorkerRegistration>());
557 }
558 
DidGetAllRegistrations(const GetAllRegistrationInfosCallback & callback,RegistrationList * registrations,ServiceWorkerDatabase::Status status)559 void ServiceWorkerStorage::DidGetAllRegistrations(
560     const GetAllRegistrationInfosCallback& callback,
561     RegistrationList* registrations,
562     ServiceWorkerDatabase::Status status) {
563   DCHECK(registrations);
564   if (status != ServiceWorkerDatabase::STATUS_OK) {
565     // TODO(nhiroki): Handle database error (http://crbug.com/371675).
566     callback.Run(std::vector<ServiceWorkerRegistrationInfo>());
567     return;
568   }
569 
570   // Add all stored registrations.
571   std::set<int64> pushed_registrations;
572   std::vector<ServiceWorkerRegistrationInfo> infos;
573   for (RegistrationList::const_iterator it = registrations->begin();
574        it != registrations->end(); ++it) {
575     const bool inserted =
576         pushed_registrations.insert(it->registration_id).second;
577     DCHECK(inserted);
578 
579     ServiceWorkerRegistration* registration =
580         context_->GetLiveRegistration(it->registration_id);
581     if (registration) {
582       infos.push_back(registration->GetInfo());
583       continue;
584     }
585 
586     ServiceWorkerRegistrationInfo info;
587     info.pattern = it->scope;
588     info.script_url = it->script;
589     info.registration_id = it->registration_id;
590     if (ServiceWorkerVersion* version =
591             context_->GetLiveVersion(it->version_id)) {
592       if (it->is_active)
593         info.active_version = version->GetInfo();
594       else
595         info.waiting_version = version->GetInfo();
596       infos.push_back(info);
597       continue;
598     }
599 
600     if (it->is_active) {
601       info.active_version.is_null = false;
602       info.active_version.status = ServiceWorkerVersion::ACTIVE;
603       info.active_version.version_id = it->version_id;
604     } else {
605       info.waiting_version.is_null = false;
606       info.waiting_version.status = ServiceWorkerVersion::INSTALLED;
607       info.waiting_version.version_id = it->version_id;
608     }
609     infos.push_back(info);
610   }
611 
612   // Add unstored registrations that are being installed.
613   for (RegistrationRefsById::const_iterator it =
614            installing_registrations_.begin();
615        it != installing_registrations_.end(); ++it) {
616     if (pushed_registrations.insert(it->first).second)
617       infos.push_back(it->second->GetInfo());
618   }
619 
620   callback.Run(infos);
621 }
622 
DidStoreRegistration(const StatusCallback & callback,const GURL & origin,const std::vector<int64> & newly_purgeable_resources,ServiceWorkerDatabase::Status status)623 void ServiceWorkerStorage::DidStoreRegistration(
624     const StatusCallback& callback,
625     const GURL& origin,
626     const std::vector<int64>& newly_purgeable_resources,
627     ServiceWorkerDatabase::Status status) {
628   if (status != ServiceWorkerDatabase::STATUS_OK) {
629     // TODO(nhiroki): Handle database error (http://crbug.com/371675).
630     callback.Run(DatabaseStatusToStatusCode(status));
631     return;
632   }
633   registered_origins_.insert(origin);
634   callback.Run(SERVICE_WORKER_OK);
635   StartPurgingResources(newly_purgeable_resources);
636 }
637 
DidUpdateToActiveState(const StatusCallback & callback,ServiceWorkerDatabase::Status status)638 void ServiceWorkerStorage::DidUpdateToActiveState(
639     const StatusCallback& callback,
640     ServiceWorkerDatabase::Status status) {
641   // TODO(nhiroki): Handle database error (http://crbug.com/371675).
642   callback.Run(DatabaseStatusToStatusCode(status));
643 }
644 
DidDeleteRegistration(const GURL & origin,const StatusCallback & callback,bool origin_is_deletable,const std::vector<int64> & newly_purgeable_resources,ServiceWorkerDatabase::Status status)645 void ServiceWorkerStorage::DidDeleteRegistration(
646     const GURL& origin,
647     const StatusCallback& callback,
648     bool origin_is_deletable,
649     const std::vector<int64>& newly_purgeable_resources,
650     ServiceWorkerDatabase::Status status) {
651   if (status != ServiceWorkerDatabase::STATUS_OK) {
652     // TODO(nhiroki): Handle database error (http://crbug.com/371675).
653     callback.Run(DatabaseStatusToStatusCode(status));
654     return;
655   }
656   if (origin_is_deletable)
657     registered_origins_.erase(origin);
658   callback.Run(SERVICE_WORKER_OK);
659   StartPurgingResources(newly_purgeable_resources);
660 }
661 
662 scoped_refptr<ServiceWorkerRegistration>
GetOrCreateRegistration(const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources)663 ServiceWorkerStorage::GetOrCreateRegistration(
664     const ServiceWorkerDatabase::RegistrationData& data,
665     const ResourceList& resources) {
666   scoped_refptr<ServiceWorkerRegistration> registration =
667       context_->GetLiveRegistration(data.registration_id);
668   if (registration)
669     return registration;
670 
671   registration = new ServiceWorkerRegistration(
672       data.scope, data.script, data.registration_id, context_);
673   scoped_refptr<ServiceWorkerVersion> version =
674       context_->GetLiveVersion(data.version_id);
675   if (!version) {
676     version = new ServiceWorkerVersion(registration, data.version_id, context_);
677     version->SetStatus(data.is_active ?
678         ServiceWorkerVersion::ACTIVE : ServiceWorkerVersion::INSTALLED);
679     version->script_cache_map()->SetResources(resources);
680   }
681 
682   if (version->status() == ServiceWorkerVersion::ACTIVE)
683     registration->set_active_version(version);
684   else if (version->status() == ServiceWorkerVersion::INSTALLED)
685     registration->set_waiting_version(version);
686   else
687     NOTREACHED();
688   // TODO(michaeln): Hmmm, what if DeleteReg was invoked after
689   // the Find result we're returning here? NOTREACHED condition?
690   return registration;
691 }
692 
693 ServiceWorkerRegistration*
FindInstallingRegistrationForDocument(const GURL & document_url)694 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
695     const GURL& document_url) {
696   DCHECK(!document_url.has_ref());
697 
698   LongestScopeMatcher matcher(document_url);
699   ServiceWorkerRegistration* match = NULL;
700 
701   // TODO(nhiroki): This searches over installing registrations linearly and it
702   // couldn't be scalable. Maybe the regs should be partitioned by origin.
703   for (RegistrationRefsById::const_iterator it =
704            installing_registrations_.begin();
705        it != installing_registrations_.end(); ++it) {
706     if (matcher.MatchLongest(it->second->pattern()))
707       match = it->second;
708   }
709   return match;
710 }
711 
712 ServiceWorkerRegistration*
FindInstallingRegistrationForPattern(const GURL & scope)713 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
714     const GURL& scope) {
715   for (RegistrationRefsById::const_iterator it =
716            installing_registrations_.begin();
717        it != installing_registrations_.end(); ++it) {
718     if (it->second->pattern() == scope)
719       return it->second;
720   }
721   return NULL;
722 }
723 
724 ServiceWorkerRegistration*
FindInstallingRegistrationForId(int64 registration_id)725 ServiceWorkerStorage::FindInstallingRegistrationForId(
726     int64 registration_id) {
727   RegistrationRefsById::const_iterator found =
728       installing_registrations_.find(registration_id);
729   if (found == installing_registrations_.end())
730     return NULL;
731   return found->second;
732 }
733 
disk_cache()734 ServiceWorkerDiskCache* ServiceWorkerStorage::disk_cache() {
735   if (disk_cache_)
736     return disk_cache_.get();
737 
738   disk_cache_.reset(new ServiceWorkerDiskCache);
739 
740   base::FilePath path = GetDiskCachePath();
741   if (path.empty()) {
742     int rv = disk_cache_->InitWithMemBackend(
743         kMaxMemDiskCacheSize,
744         base::Bind(&EmptyCompletionCallback));
745     DCHECK_EQ(net::OK, rv);
746     return disk_cache_.get();
747   }
748 
749   int rv = disk_cache_->InitWithDiskBackend(
750       path, kMaxDiskCacheSize, false,
751       disk_cache_thread_.get(),
752       base::Bind(&ServiceWorkerStorage::OnDiskCacheInitialized,
753                  weak_factory_.GetWeakPtr()));
754   if (rv != net::ERR_IO_PENDING)
755     OnDiskCacheInitialized(rv);
756 
757   return disk_cache_.get();
758 }
759 
OnDiskCacheInitialized(int rv)760 void ServiceWorkerStorage::OnDiskCacheInitialized(int rv) {
761   if (rv != net::OK) {
762     LOG(ERROR) << "Failed to open the serviceworker diskcache: "
763                << net::ErrorToString(rv);
764     // TODO(michaeln): DeleteAndStartOver()
765     disk_cache_->Disable();
766     state_ = DISABLED;
767   }
768   ServiceWorkerHistograms::CountInitDiskCacheResult(rv == net::OK);
769 }
770 
StartPurgingResources(const std::vector<int64> & ids)771 void ServiceWorkerStorage::StartPurgingResources(
772     const std::vector<int64>& ids) {
773   for (size_t i = 0; i < ids.size(); ++i)
774     purgeable_reource_ids_.push_back(ids[i]);
775   ContinuePurgingResources();
776 }
777 
StartPurgingResources(const ResourceList & resources)778 void ServiceWorkerStorage::StartPurgingResources(
779     const ResourceList& resources) {
780   for (size_t i = 0; i < resources.size(); ++i)
781     purgeable_reource_ids_.push_back(resources[i].resource_id);
782   ContinuePurgingResources();
783 }
784 
ContinuePurgingResources()785 void ServiceWorkerStorage::ContinuePurgingResources() {
786   if (purgeable_reource_ids_.empty() || is_purge_pending_)
787     return;
788 
789   // Do one at a time until we're done, use RunSoon to avoid recursion when
790   // DoomEntry returns immediately.
791   is_purge_pending_ = true;
792   int64 id = purgeable_reource_ids_.front();
793   purgeable_reource_ids_.pop_front();
794   RunSoon(FROM_HERE,
795           base::Bind(&ServiceWorkerStorage::PurgeResource,
796                      weak_factory_.GetWeakPtr(), id));
797 }
798 
PurgeResource(int64 id)799 void ServiceWorkerStorage::PurgeResource(int64 id) {
800   DCHECK(is_purge_pending_);
801   int rv = disk_cache()->DoomEntry(
802       id, base::Bind(&ServiceWorkerStorage::OnResourcePurged,
803                      weak_factory_.GetWeakPtr(), id));
804   if (rv != net::ERR_IO_PENDING)
805     OnResourcePurged(id, rv);
806 }
807 
OnResourcePurged(int64 id,int rv)808 void ServiceWorkerStorage::OnResourcePurged(int64 id, int rv) {
809   DCHECK(is_purge_pending_);
810   is_purge_pending_ = false;
811 
812   database_task_runner_->PostTask(
813       FROM_HERE,
814       base::Bind(base::IgnoreResult(
815           &ServiceWorkerDatabase::ClearPurgeableResourceIds),
816           base::Unretained(database_.get()),
817           std::set<int64>(&id, &id + 1)));
818 
819   ContinuePurgingResources();
820 }
821 
ReadInitialDataFromDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const InitializeCallback & callback)822 void ServiceWorkerStorage::ReadInitialDataFromDB(
823     ServiceWorkerDatabase* database,
824     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
825     const InitializeCallback& callback) {
826   DCHECK(database);
827   scoped_ptr<ServiceWorkerStorage::InitialData> data(
828       new ServiceWorkerStorage::InitialData());
829 
830   ServiceWorkerDatabase::Status status =
831       database->GetNextAvailableIds(&data->next_registration_id,
832                                     &data->next_version_id,
833                                     &data->next_resource_id);
834   if (status != ServiceWorkerDatabase::STATUS_OK) {
835     original_task_runner->PostTask(
836         FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
837     return;
838   }
839 
840   status = database->GetOriginsWithRegistrations(&data->origins);
841   original_task_runner->PostTask(
842       FROM_HERE, base::Bind(callback, base::Owned(data.release()), status));
843 }
844 
DeleteRegistrationFromDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,int64 registration_id,const GURL & origin,const DeleteRegistrationCallback & callback)845 void ServiceWorkerStorage::DeleteRegistrationFromDB(
846     ServiceWorkerDatabase* database,
847     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
848     int64 registration_id,
849     const GURL& origin,
850     const DeleteRegistrationCallback& callback) {
851   DCHECK(database);
852 
853   std::vector<int64> newly_purgeable_resources;
854   ServiceWorkerDatabase::Status status =
855       database->DeleteRegistration(registration_id, origin,
856                                    &newly_purgeable_resources);
857   if (status != ServiceWorkerDatabase::STATUS_OK) {
858     original_task_runner->PostTask(
859         FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status));
860     return;
861   }
862 
863   // TODO(nhiroki): Add convenient method to ServiceWorkerDatabase to check the
864   // unique origin list.
865   std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
866   status = database->GetRegistrationsForOrigin(origin, &registrations);
867   if (status != ServiceWorkerDatabase::STATUS_OK) {
868     original_task_runner->PostTask(
869         FROM_HERE, base::Bind(callback, false, std::vector<int64>(), status));
870     return;
871   }
872 
873   bool deletable = registrations.empty();
874   original_task_runner->PostTask(
875       FROM_HERE, base::Bind(callback, deletable,
876                             newly_purgeable_resources, status));
877 }
878 
WriteRegistrationInDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const ServiceWorkerDatabase::RegistrationData & data,const ResourceList & resources,const WriteRegistrationCallback & callback)879 void ServiceWorkerStorage::WriteRegistrationInDB(
880     ServiceWorkerDatabase* database,
881     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
882     const ServiceWorkerDatabase::RegistrationData& data,
883     const ResourceList& resources,
884     const WriteRegistrationCallback& callback) {
885   DCHECK(database);
886   std::vector<int64> newly_purgeable_resources;
887   ServiceWorkerDatabase::Status status =
888       database->WriteRegistration(data, resources, &newly_purgeable_resources);
889   original_task_runner->PostTask(
890       FROM_HERE,
891       base::Bind(callback, data.script.GetOrigin(),
892                  newly_purgeable_resources, status));
893 }
894 
FindForDocumentInDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const GURL & document_url,const FindInDBCallback & callback)895 void ServiceWorkerStorage::FindForDocumentInDB(
896     ServiceWorkerDatabase* database,
897     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
898     const GURL& document_url,
899     const FindInDBCallback& callback) {
900   GURL origin = document_url.GetOrigin();
901   RegistrationList registrations;
902   ServiceWorkerDatabase::Status status =
903       database->GetRegistrationsForOrigin(origin, &registrations);
904   if (status != ServiceWorkerDatabase::STATUS_OK) {
905     original_task_runner->PostTask(
906         FROM_HERE,
907         base::Bind(callback,
908                    ServiceWorkerDatabase::RegistrationData(),
909                    ResourceList(),
910                    status));
911     return;
912   }
913 
914   ServiceWorkerDatabase::RegistrationData data;
915   ResourceList resources;
916   status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
917 
918   // Find one with a pattern match.
919   LongestScopeMatcher matcher(document_url);
920   int64 match = kInvalidServiceWorkerRegistrationId;
921   for (size_t i = 0; i < registrations.size(); ++i) {
922     if (matcher.MatchLongest(registrations[i].scope))
923       match = registrations[i].registration_id;
924   }
925 
926   if (match != kInvalidServiceWorkerRegistrationId)
927     status = database->ReadRegistration(match, origin, &data, &resources);
928 
929   original_task_runner->PostTask(
930       FROM_HERE,
931       base::Bind(callback, data, resources, status));
932 }
933 
FindForPatternInDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,const GURL & scope,const FindInDBCallback & callback)934 void ServiceWorkerStorage::FindForPatternInDB(
935     ServiceWorkerDatabase* database,
936     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
937     const GURL& scope,
938     const FindInDBCallback& callback) {
939   GURL origin = scope.GetOrigin();
940   std::vector<ServiceWorkerDatabase::RegistrationData> registrations;
941   ServiceWorkerDatabase::Status status =
942       database->GetRegistrationsForOrigin(origin, &registrations);
943   if (status != ServiceWorkerDatabase::STATUS_OK) {
944     original_task_runner->PostTask(
945         FROM_HERE,
946         base::Bind(callback,
947                    ServiceWorkerDatabase::RegistrationData(),
948                    ResourceList(),
949                    status));
950     return;
951   }
952 
953   // Find one with an exact matching scope.
954   ServiceWorkerDatabase::RegistrationData data;
955   ResourceList resources;
956   status = ServiceWorkerDatabase::STATUS_ERROR_NOT_FOUND;
957   for (RegistrationList::const_iterator it = registrations.begin();
958        it != registrations.end(); ++it) {
959     if (scope != it->scope)
960       continue;
961     status = database->ReadRegistration(it->registration_id, origin,
962                                         &data, &resources);
963     break;  // We're done looping.
964   }
965 
966   original_task_runner->PostTask(
967       FROM_HERE,
968       base::Bind(callback, data, resources, status));
969 }
970 
FindForIdInDB(ServiceWorkerDatabase * database,scoped_refptr<base::SequencedTaskRunner> original_task_runner,int64 registration_id,const GURL & origin,const FindInDBCallback & callback)971 void ServiceWorkerStorage::FindForIdInDB(
972     ServiceWorkerDatabase* database,
973     scoped_refptr<base::SequencedTaskRunner> original_task_runner,
974     int64 registration_id,
975     const GURL& origin,
976     const FindInDBCallback& callback) {
977   ServiceWorkerDatabase::RegistrationData data;
978   ResourceList resources;
979   ServiceWorkerDatabase::Status status =
980       database->ReadRegistration(registration_id, origin, &data, &resources);
981   original_task_runner->PostTask(
982       FROM_HERE, base::Bind(callback, data, resources, status));
983 }
984 
985 }  // namespace content
986