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.
5 #include "content/browser/service_worker/service_worker_storage.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/sequenced_task_runner.h"
11 #include "content/browser/service_worker/service_worker_context_core.h"
12 #include "content/browser/service_worker/service_worker_disk_cache.h"
13 #include "content/browser/service_worker/service_worker_info.h"
14 #include "content/browser/service_worker/service_worker_registration.h"
15 #include "content/browser/service_worker/service_worker_utils.h"
16 #include "content/browser/service_worker/service_worker_version.h"
17 #include "content/public/browser/browser_thread.h"
18 #include "net/base/net_errors.h"
19 #include "webkit/browser/quota/quota_manager_proxy.h"
25 void RunSoon(const tracked_objects::Location
& from_here
,
26 const base::Closure
& closure
) {
27 base::MessageLoop::current()->PostTask(from_here
, closure
);
31 const scoped_refptr
<ServiceWorkerRegistration
>& registration
,
32 ServiceWorkerStatusCode status
,
33 const ServiceWorkerStorage::FindRegistrationCallback
& callback
) {
34 callback
.Run(status
, registration
);
37 void CompleteFindSoon(
38 const tracked_objects::Location
& from_here
,
39 const scoped_refptr
<ServiceWorkerRegistration
>& registration
,
40 ServiceWorkerStatusCode status
,
41 const ServiceWorkerStorage::FindRegistrationCallback
& callback
) {
42 RunSoon(from_here
, base::Bind(callback
, status
, registration
));
45 const base::FilePath::CharType kServiceWorkerDirectory
[] =
46 FILE_PATH_LITERAL("ServiceWorker");
49 const int kMaxMemDiskCacheSize
= 10 * 1024 * 1024;
51 void EmptyCompletionCallback(int) {}
55 ServiceWorkerStorage::ServiceWorkerStorage(
56 const base::FilePath
& path
,
57 base::WeakPtr
<ServiceWorkerContextCore
> context
,
58 base::SequencedTaskRunner
* database_task_runner
,
59 quota::QuotaManagerProxy
* quota_manager_proxy
)
60 : last_registration_id_(0),
63 simulated_lazy_initted_(false),
65 database_task_runner_(database_task_runner
),
66 quota_manager_proxy_(quota_manager_proxy
) {
68 path_
= path
.Append(kServiceWorkerDirectory
);
71 ServiceWorkerStorage::~ServiceWorkerStorage() {
74 void ServiceWorkerStorage::FindRegistrationForPattern(
76 const FindRegistrationCallback
& callback
) {
77 simulated_lazy_initted_
= true;
78 scoped_refptr
<ServiceWorkerRegistration
> null_registration
;
81 FROM_HERE
, null_registration
, SERVICE_WORKER_ERROR_FAILED
, callback
);
85 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
86 FindInstallingRegistrationForPattern(scope
);
87 if (installing_registration
) {
89 FROM_HERE
, installing_registration
, SERVICE_WORKER_OK
, callback
);
93 // See if there are any registrations for the origin.
94 OriginRegistrationsMap::const_iterator
95 found
= stored_registrations_
.find(scope
.GetOrigin());
96 if (found
== stored_registrations_
.end()) {
98 FROM_HERE
, null_registration
, SERVICE_WORKER_ERROR_NOT_FOUND
, callback
);
102 // Find one with a matching scope.
103 for (RegistrationsMap::const_iterator it
= found
->second
.begin();
104 it
!= found
->second
.end(); ++it
) {
105 if (scope
== it
->second
.scope
) {
106 const ServiceWorkerDatabase::RegistrationData
* data
= &(it
->second
);
107 scoped_refptr
<ServiceWorkerRegistration
> registration
=
108 context_
->GetLiveRegistration(data
->registration_id
);
110 CompleteFindSoon(FROM_HERE
, registration
, SERVICE_WORKER_OK
, callback
);
114 registration
= CreateRegistration(data
);
115 CompleteFindSoon(FROM_HERE
, registration
, SERVICE_WORKER_OK
, callback
);
121 FROM_HERE
, null_registration
, SERVICE_WORKER_ERROR_NOT_FOUND
, callback
);
124 void ServiceWorkerStorage::FindRegistrationForDocument(
125 const GURL
& document_url
,
126 const FindRegistrationCallback
& callback
) {
127 simulated_lazy_initted_
= true;
128 scoped_refptr
<ServiceWorkerRegistration
> null_registration
;
130 CompleteFindNow(null_registration
, SERVICE_WORKER_ERROR_FAILED
, callback
);
134 // See if there are any registrations for the origin.
135 OriginRegistrationsMap::const_iterator
136 found
= stored_registrations_
.find(document_url
.GetOrigin());
137 if (found
== stored_registrations_
.end()) {
138 // Look for something currently being installed.
139 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
140 FindInstallingRegistrationForDocument(document_url
);
141 if (installing_registration
) {
142 CompleteFindNow(installing_registration
, SERVICE_WORKER_OK
, callback
);
146 // Return syncly to simulate this class having an in memory map of
147 // origins with registrations.
148 CompleteFindNow(null_registration
, SERVICE_WORKER_ERROR_NOT_FOUND
,
153 // Find one with a pattern match.
154 for (RegistrationsMap::const_iterator it
= found
->second
.begin();
155 it
!= found
->second
.end(); ++it
) {
156 // TODO(michaeln): if there are multiple matches the one with
157 // the longest scope should win.
158 if (ServiceWorkerUtils::ScopeMatches(it
->second
.scope
, document_url
)) {
159 const ServiceWorkerDatabase::RegistrationData
* data
= &(it
->second
);
161 // If its in the live map, return syncly to simulate this class having
162 // iterated over the values in that map instead of reading the db.
163 scoped_refptr
<ServiceWorkerRegistration
> registration
=
164 context_
->GetLiveRegistration(data
->registration_id
);
166 CompleteFindNow(registration
, SERVICE_WORKER_OK
, callback
);
170 // If we have to create a new instance, return it asyncly to simulate
171 // having had to retreive the RegistrationData from the db.
172 registration
= CreateRegistration(data
);
173 CompleteFindSoon(FROM_HERE
, registration
, SERVICE_WORKER_OK
, callback
);
178 // Look for something currently being installed.
179 // TODO(michaeln): Should be mixed in with the stored registrations
181 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
182 FindInstallingRegistrationForDocument(document_url
);
183 if (installing_registration
) {
185 FROM_HERE
, installing_registration
, SERVICE_WORKER_OK
, callback
);
189 // Return asyncly to simulate having had to look in the db since this
190 // origin does have some registations.
192 FROM_HERE
, null_registration
, SERVICE_WORKER_ERROR_NOT_FOUND
, callback
);
195 void ServiceWorkerStorage::FindRegistrationForId(
196 int64 registration_id
,
197 const FindRegistrationCallback
& callback
) {
198 simulated_lazy_initted_
= true;
199 scoped_refptr
<ServiceWorkerRegistration
> null_registration
;
201 CompleteFindNow(null_registration
, SERVICE_WORKER_ERROR_FAILED
, callback
);
204 scoped_refptr
<ServiceWorkerRegistration
> installing_registration
=
205 FindInstallingRegistrationForId(registration_id
);
206 if (installing_registration
) {
207 CompleteFindNow(installing_registration
, SERVICE_WORKER_OK
, callback
);
210 RegistrationPtrMap::const_iterator found
=
211 registrations_by_id_
.find(registration_id
);
212 if (found
== registrations_by_id_
.end()) {
213 CompleteFindNow(null_registration
, SERVICE_WORKER_ERROR_NOT_FOUND
,
217 scoped_refptr
<ServiceWorkerRegistration
> registration
=
218 context_
->GetLiveRegistration(registration_id
);
220 CompleteFindNow(registration
, SERVICE_WORKER_OK
, callback
);
223 registration
= CreateRegistration(found
->second
);
224 CompleteFindSoon(FROM_HERE
, registration
, SERVICE_WORKER_OK
, callback
);
227 void ServiceWorkerStorage::GetAllRegistrations(
228 const GetAllRegistrationInfosCallback
& callback
) {
229 simulated_lazy_initted_
= true;
230 std::vector
<ServiceWorkerRegistrationInfo
> registrations
;
232 RunSoon(FROM_HERE
, base::Bind(callback
, registrations
));
236 // Add all stored registrations.
237 for (RegistrationPtrMap::const_iterator it
= registrations_by_id_
.begin();
238 it
!= registrations_by_id_
.end(); ++it
) {
239 ServiceWorkerRegistration
* registration
=
240 context_
->GetLiveRegistration(it
->first
);
242 registrations
.push_back(registration
->GetInfo());
245 ServiceWorkerRegistrationInfo info
;
246 info
.pattern
= it
->second
->scope
;
247 info
.script_url
= it
->second
->script
;
248 info
.active_version
.is_null
= false;
249 if (it
->second
->is_active
)
250 info
.active_version
.status
= ServiceWorkerVersion::ACTIVE
;
252 info
.active_version
.status
= ServiceWorkerVersion::INSTALLED
;
253 registrations
.push_back(info
);
256 // Add unstored registrations that are being installed.
257 for (RegistrationRefsById::const_iterator it
=
258 installing_registrations_
.begin();
259 it
!= installing_registrations_
.end(); ++it
) {
260 if (registrations_by_id_
.find(it
->first
) == registrations_by_id_
.end())
261 registrations
.push_back(it
->second
->GetInfo());
264 RunSoon(FROM_HERE
, base::Bind(callback
, registrations
));
267 void ServiceWorkerStorage::StoreRegistration(
268 ServiceWorkerRegistration
* registration
,
269 ServiceWorkerVersion
* version
,
270 const StatusCallback
& callback
) {
271 DCHECK(registration
);
273 DCHECK(simulated_lazy_initted_
);
275 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
279 // Keep a database struct in the storage map.
280 RegistrationsMap
& storage_map
=
281 stored_registrations_
[registration
->script_url().GetOrigin()];
282 ServiceWorkerDatabase::RegistrationData
& data
=
283 storage_map
[registration
->id()];
284 data
.registration_id
= registration
->id();
285 data
.scope
= registration
->pattern();
286 data
.script
= registration
->script_url();
287 data
.has_fetch_handler
= true;
288 data
.version_id
= version
->version_id();
289 data
.last_update_check
= base::Time::Now();
290 data
.is_active
= false; // initially stored in the waiting state
292 // Keep a seperate map of ptrs keyed by id only.
293 registrations_by_id_
[registration
->id()] = &storage_map
[registration
->id()];
295 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_OK
));
298 void ServiceWorkerStorage::UpdateToActiveState(
299 ServiceWorkerRegistration
* registration
,
300 const StatusCallback
& callback
) {
301 DCHECK(simulated_lazy_initted_
);
303 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_FAILED
));
307 RegistrationPtrMap::const_iterator
308 found
= registrations_by_id_
.find(registration
->id());
309 if (found
== registrations_by_id_
.end()) {
310 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_NOT_FOUND
));
313 DCHECK(!found
->second
->is_active
);
314 found
->second
->is_active
= true;
315 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_OK
));
318 void ServiceWorkerStorage::DeleteRegistration(
319 int64 registration_id
,
320 const StatusCallback
& callback
) {
321 DCHECK(simulated_lazy_initted_
);
322 RegistrationPtrMap::iterator
323 found
= registrations_by_id_
.find(registration_id
);
324 if (found
== registrations_by_id_
.end()) {
325 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_ERROR_NOT_FOUND
));
329 GURL origin
= found
->second
->script
.GetOrigin();
330 stored_registrations_
[origin
].erase(registration_id
);
331 if (stored_registrations_
[origin
].empty())
332 stored_registrations_
.erase(origin
);
334 registrations_by_id_
.erase(found
);
336 RunSoon(FROM_HERE
, base::Bind(callback
, SERVICE_WORKER_OK
));
337 // TODO(michaeln): Either its instance should also be
338 // removed from liveregistrations map or the live object
339 // should marked as deleted in some way and not 'findable'
343 scoped_ptr
<ServiceWorkerResponseReader
>
344 ServiceWorkerStorage::CreateResponseReader(int64 response_id
) {
345 return make_scoped_ptr(
346 new ServiceWorkerResponseReader(response_id
, disk_cache()));
349 scoped_ptr
<ServiceWorkerResponseWriter
>
350 ServiceWorkerStorage::CreateResponseWriter(int64 response_id
) {
351 return make_scoped_ptr(
352 new ServiceWorkerResponseWriter(response_id
, disk_cache()));
355 int64
ServiceWorkerStorage::NewRegistrationId() {
356 DCHECK(simulated_lazy_initted_
);
357 return ++last_registration_id_
;
360 int64
ServiceWorkerStorage::NewVersionId() {
361 DCHECK(simulated_lazy_initted_
);
362 return ++last_version_id_
;
365 int64
ServiceWorkerStorage::NewResourceId() {
366 DCHECK(simulated_lazy_initted_
);
367 return ++last_resource_id_
;
370 void ServiceWorkerStorage::NotifyInstallingRegistration(
371 ServiceWorkerRegistration
* registration
) {
372 installing_registrations_
[registration
->id()] = registration
;
375 void ServiceWorkerStorage::NotifyDoneInstallingRegistration(
376 ServiceWorkerRegistration
* registration
) {
377 installing_registrations_
.erase(registration
->id());
380 scoped_refptr
<ServiceWorkerRegistration
>
381 ServiceWorkerStorage::CreateRegistration(
382 const ServiceWorkerDatabase::RegistrationData
* data
) {
383 scoped_refptr
<ServiceWorkerRegistration
> registration(
384 new ServiceWorkerRegistration(
385 data
->scope
, data
->script
, data
->registration_id
, context_
));
387 scoped_refptr
<ServiceWorkerVersion
> version
=
388 context_
->GetLiveVersion(data
->version_id
);
390 version
= new ServiceWorkerVersion(
391 registration
, data
->version_id
, context_
);
392 version
->SetStatus(data
->GetVersionStatus());
395 if (version
->status() == ServiceWorkerVersion::ACTIVE
)
396 registration
->set_active_version(version
);
397 else if (version
->status() == ServiceWorkerVersion::INSTALLED
)
398 registration
->set_pending_version(version
);
401 // TODO(michaeln): Hmmm, what if DeleteReg was invoked after
402 // the Find result we're returning here? NOTREACHED condition?
407 ServiceWorkerRegistration
*
408 ServiceWorkerStorage::FindInstallingRegistrationForDocument(
409 const GURL
& document_url
) {
410 // TODO(michaeln): if there are multiple matches the one with
411 // the longest scope should win, and these should on equal footing
412 // with the stored registrations in FindRegistrationForDocument().
413 for (RegistrationRefsById::const_iterator it
=
414 installing_registrations_
.begin();
415 it
!= installing_registrations_
.end(); ++it
) {
416 if (ServiceWorkerUtils::ScopeMatches(
417 it
->second
->pattern(), document_url
)) {
424 ServiceWorkerRegistration
*
425 ServiceWorkerStorage::FindInstallingRegistrationForPattern(
427 for (RegistrationRefsById::const_iterator it
=
428 installing_registrations_
.begin();
429 it
!= installing_registrations_
.end(); ++it
) {
430 if (it
->second
->pattern() == scope
)
436 ServiceWorkerRegistration
*
437 ServiceWorkerStorage::FindInstallingRegistrationForId(
438 int64 registration_id
) {
439 RegistrationRefsById::const_iterator found
=
440 installing_registrations_
.find(registration_id
);
441 if (found
== installing_registrations_
.end())
443 return found
->second
;
446 ServiceWorkerDiskCache
* ServiceWorkerStorage::disk_cache() {
448 return disk_cache_
.get();
450 // TODO(michaeln): Store data on disk and do error checking.
451 disk_cache_
.reset(new ServiceWorkerDiskCache
);
452 int rv
= disk_cache_
->InitWithMemBackend(
453 kMaxMemDiskCacheSize
,
454 base::Bind(&EmptyCompletionCallback
));
455 DCHECK_EQ(net::OK
, rv
);
456 return disk_cache_
.get();
459 } // namespace content