1 // Copyright 2015 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/background_sync/background_sync_manager.h"
7 #include "base/barrier_closure.h"
9 #include "content/browser/background_sync/background_sync_network_observer.h"
10 #include "content/browser/service_worker/service_worker_context_wrapper.h"
11 #include "content/browser/service_worker/service_worker_storage.h"
12 #include "content/public/browser/browser_thread.h"
15 const char kBackgroundSyncUserDataKey
[] = "BackgroundSyncUserData";
20 const BackgroundSyncManager::BackgroundSyncRegistration::RegistrationId
21 BackgroundSyncManager::BackgroundSyncRegistration::kInvalidRegistrationId
=
24 const BackgroundSyncManager::BackgroundSyncRegistration::RegistrationId
25 BackgroundSyncManager::BackgroundSyncRegistration::kInitialId
= 0;
27 BackgroundSyncManager::BackgroundSyncRegistrations::
28 BackgroundSyncRegistrations()
29 : next_id(BackgroundSyncRegistration::kInitialId
) {
32 BackgroundSyncManager::BackgroundSyncRegistrations::
33 ~BackgroundSyncRegistrations() {
37 scoped_ptr
<BackgroundSyncManager
> BackgroundSyncManager::Create(
38 const scoped_refptr
<ServiceWorkerContextWrapper
>& service_worker_context
) {
39 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
41 BackgroundSyncManager
* sync_manager
=
42 new BackgroundSyncManager(service_worker_context
);
44 return make_scoped_ptr(sync_manager
);
47 BackgroundSyncManager::~BackgroundSyncManager() {
48 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
50 service_worker_context_
->RemoveObserver(this);
53 BackgroundSyncManager::RegistrationKey::RegistrationKey(
54 const BackgroundSyncRegistration
& registration
)
55 : RegistrationKey(registration
.tag
, registration
.periodicity
) {
58 BackgroundSyncManager::RegistrationKey::RegistrationKey(
59 const std::string
& tag
,
60 SyncPeriodicity periodicity
)
61 : value_(periodicity
== SYNC_ONE_SHOT
? "o_" + tag
: "p_" + tag
) {
64 void BackgroundSyncManager::Register(
66 int64 sw_registration_id
,
67 const BackgroundSyncRegistration
& sync_registration
,
68 const StatusAndRegistrationCallback
& callback
) {
69 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
70 DCHECK_EQ(BackgroundSyncRegistration::kInvalidRegistrationId
,
71 sync_registration
.id
);
74 base::MessageLoop::current()->PostTask(
76 base::Bind(callback
, ERROR_TYPE_STORAGE
, BackgroundSyncRegistration()));
80 op_scheduler_
.ScheduleOperation(base::Bind(
81 &BackgroundSyncManager::RegisterImpl
, weak_ptr_factory_
.GetWeakPtr(),
82 origin
, sw_registration_id
, sync_registration
,
83 MakeStatusAndRegistrationCompletion(callback
)));
86 void BackgroundSyncManager::Unregister(
88 int64 sw_registration_id
,
89 const std::string
& sync_registration_tag
,
90 SyncPeriodicity periodicity
,
91 BackgroundSyncRegistration::RegistrationId sync_registration_id
,
92 const StatusCallback
& callback
) {
93 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
96 base::MessageLoop::current()->PostTask(
97 FROM_HERE
, base::Bind(callback
, ERROR_TYPE_STORAGE
));
101 RegistrationKey
registration_key(sync_registration_tag
, periodicity
);
103 op_scheduler_
.ScheduleOperation(base::Bind(
104 &BackgroundSyncManager::UnregisterImpl
, weak_ptr_factory_
.GetWeakPtr(),
105 origin
, sw_registration_id
, registration_key
, sync_registration_id
,
106 MakeStatusCompletion(callback
)));
109 void BackgroundSyncManager::GetRegistration(
111 int64 sw_registration_id
,
112 const std::string sync_registration_tag
,
113 SyncPeriodicity periodicity
,
114 const StatusAndRegistrationCallback
& callback
) {
115 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
118 base::MessageLoop::current()->PostTask(
120 base::Bind(callback
, ERROR_TYPE_STORAGE
, BackgroundSyncRegistration()));
124 RegistrationKey
registration_key(sync_registration_tag
, periodicity
);
126 op_scheduler_
.ScheduleOperation(base::Bind(
127 &BackgroundSyncManager::GetRegistrationImpl
,
128 weak_ptr_factory_
.GetWeakPtr(), origin
, sw_registration_id
,
129 registration_key
, MakeStatusAndRegistrationCompletion(callback
)));
132 void BackgroundSyncManager::OnRegistrationDeleted(int64 registration_id
,
133 const GURL
& pattern
) {
134 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
136 // Operations already in the queue will either fail when they write to storage
137 // or return stale results based on registrations loaded in memory. This is
138 // inconsequential since the service worker is gone.
139 op_scheduler_
.ScheduleOperation(base::Bind(
140 &BackgroundSyncManager::OnRegistrationDeletedImpl
,
141 weak_ptr_factory_
.GetWeakPtr(), registration_id
, MakeEmptyCompletion()));
144 void BackgroundSyncManager::OnStorageWiped() {
145 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
146 // Operations already in the queue will either fail when they write to storage
147 // or return stale results based on registrations loaded in memory. This is
148 // inconsequential since the service workers are gone.
149 op_scheduler_
.ScheduleOperation(
150 base::Bind(&BackgroundSyncManager::OnStorageWipedImpl
,
151 weak_ptr_factory_
.GetWeakPtr(), MakeEmptyCompletion()));
154 BackgroundSyncManager::BackgroundSyncManager(
155 const scoped_refptr
<ServiceWorkerContextWrapper
>& service_worker_context
)
156 : service_worker_context_(service_worker_context
),
158 weak_ptr_factory_(this) {
159 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
161 service_worker_context_
->AddObserver(this);
163 network_observer_
.reset(new BackgroundSyncNetworkObserver(
164 base::Bind(&BackgroundSyncManager::OnNetworkChanged
,
165 weak_ptr_factory_
.GetWeakPtr())));
168 void BackgroundSyncManager::Init() {
169 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
170 DCHECK(!op_scheduler_
.ScheduledOperations());
173 op_scheduler_
.ScheduleOperation(base::Bind(&BackgroundSyncManager::InitImpl
,
174 weak_ptr_factory_
.GetWeakPtr(),
175 MakeEmptyCompletion()));
178 void BackgroundSyncManager::InitImpl(const base::Closure
& callback
) {
179 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
182 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(callback
));
187 kBackgroundSyncUserDataKey
,
188 base::Bind(&BackgroundSyncManager::InitDidGetDataFromBackend
,
189 weak_ptr_factory_
.GetWeakPtr(), callback
));
192 void BackgroundSyncManager::InitDidGetDataFromBackend(
193 const base::Closure
& callback
,
194 const std::vector
<std::pair
<int64
, std::string
>>& user_data
,
195 ServiceWorkerStatusCode status
) {
196 if (status
!= SERVICE_WORKER_OK
&& status
!= SERVICE_WORKER_ERROR_NOT_FOUND
) {
197 LOG(ERROR
) << "BackgroundSync failed to init due to backend failure.";
198 DisableAndClearManager(base::Bind(callback
));
202 bool corruption_detected
= false;
203 for (const std::pair
<int64
, std::string
>& data
: user_data
) {
204 BackgroundSyncRegistrationsProto registrations_proto
;
205 if (registrations_proto
.ParseFromString(data
.second
)) {
206 BackgroundSyncRegistrations
* registrations
=
207 &sw_to_registrations_map_
[data
.first
];
208 registrations
->next_id
= registrations_proto
.next_registration_id();
209 registrations
->origin
= GURL(registrations_proto
.origin());
211 for (int i
= 0, max
= registrations_proto
.registration_size(); i
< max
;
213 const BackgroundSyncRegistrationProto
& registration_proto
=
214 registrations_proto
.registration(i
);
216 if (registration_proto
.id() >= registrations
->next_id
) {
217 corruption_detected
= true;
221 RegistrationKey
registration_key(registration_proto
.tag(),
222 registration_proto
.periodicity());
223 BackgroundSyncRegistration
* registration
=
224 ®istrations
->registration_map
[registration_key
];
226 registration
->id
= registration_proto
.id();
227 registration
->tag
= registration_proto
.tag();
228 registration
->periodicity
= registration_proto
.periodicity();
229 registration
->min_period
= registration_proto
.min_period();
230 registration
->network_state
= registration_proto
.network_state();
231 registration
->power_state
= registration_proto
.power_state();
235 if (corruption_detected
)
239 if (corruption_detected
) {
240 LOG(ERROR
) << "Corruption detected in background sync backend";
241 DisableAndClearManager(base::Bind(callback
));
245 // TODO(jkarlin): Call the scheduling algorithm here.
247 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(callback
));
250 void BackgroundSyncManager::RegisterImpl(
252 int64 sw_registration_id
,
253 const BackgroundSyncRegistration
& sync_registration
,
254 const StatusAndRegistrationCallback
& callback
) {
256 base::MessageLoop::current()->PostTask(
258 base::Bind(callback
, ERROR_TYPE_STORAGE
, BackgroundSyncRegistration()));
262 const BackgroundSyncRegistration
* existing_registration
= LookupRegistration(
263 sw_registration_id
, RegistrationKey(sync_registration
));
264 if (existing_registration
&&
265 existing_registration
->Equals(sync_registration
)) {
266 base::MessageLoop::current()->PostTask(
267 FROM_HERE
, base::Bind(callback
, ERROR_TYPE_OK
, *existing_registration
));
271 BackgroundSyncRegistration new_registration
= sync_registration
;
272 BackgroundSyncRegistrations
* registrations
=
273 &sw_to_registrations_map_
[sw_registration_id
];
274 new_registration
.id
= registrations
->next_id
++;
276 AddRegistrationToMap(sw_registration_id
, origin
, new_registration
);
280 base::Bind(&BackgroundSyncManager::RegisterDidStore
,
281 weak_ptr_factory_
.GetWeakPtr(), sw_registration_id
,
282 new_registration
, callback
));
285 void BackgroundSyncManager::DisableAndClearManager(
286 const base::Closure
& callback
) {
288 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(callback
));
293 sw_to_registrations_map_
.clear();
295 // Delete all backend entries. The memory representation of registered syncs
296 // may be out of sync with storage (e.g., due to corruption detection on
297 // loading from storage), so reload the registrations from storage again.
299 kBackgroundSyncUserDataKey
,
300 base::Bind(&BackgroundSyncManager::DisableAndClearDidGetRegistrations
,
301 weak_ptr_factory_
.GetWeakPtr(), callback
));
304 void BackgroundSyncManager::DisableAndClearDidGetRegistrations(
305 const base::Closure
& callback
,
306 const std::vector
<std::pair
<int64
, std::string
>>& user_data
,
307 ServiceWorkerStatusCode status
) {
308 if (status
!= SERVICE_WORKER_OK
|| user_data
.empty()) {
309 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(callback
));
313 base::Closure barrier_closure
=
314 base::BarrierClosure(user_data
.size(), base::Bind(callback
));
316 for (const auto& sw_id_and_regs
: user_data
) {
317 service_worker_context_
->ClearRegistrationUserData(
318 sw_id_and_regs
.first
, kBackgroundSyncUserDataKey
,
319 base::Bind(&BackgroundSyncManager::DisableAndClearManagerClearedOne
,
320 weak_ptr_factory_
.GetWeakPtr(), barrier_closure
));
324 void BackgroundSyncManager::DisableAndClearManagerClearedOne(
325 const base::Closure
& barrier_closure
,
326 ServiceWorkerStatusCode status
) {
327 // The status doesn't matter at this point, there is nothing else to be done.
328 base::MessageLoop::current()->PostTask(FROM_HERE
,
329 base::Bind(barrier_closure
));
332 BackgroundSyncManager::BackgroundSyncRegistration
*
333 BackgroundSyncManager::LookupRegistration(
334 int64 sw_registration_id
,
335 const RegistrationKey
& registration_key
) {
336 SWIdToRegistrationsMap::iterator it
=
337 sw_to_registrations_map_
.find(sw_registration_id
);
338 if (it
== sw_to_registrations_map_
.end())
341 BackgroundSyncRegistrations
& registrations
= it
->second
;
342 DCHECK_LE(BackgroundSyncRegistration::kInitialId
, registrations
.next_id
);
343 DCHECK(!registrations
.origin
.is_empty());
345 auto key_and_registration_iter
=
346 registrations
.registration_map
.find(registration_key
);
347 if (key_and_registration_iter
== registrations
.registration_map
.end())
350 return &key_and_registration_iter
->second
;
353 void BackgroundSyncManager::StoreRegistrations(
354 int64 sw_registration_id
,
355 const ServiceWorkerStorage::StatusCallback
& callback
) {
356 // Serialize the data.
357 const BackgroundSyncRegistrations
& registrations
=
358 sw_to_registrations_map_
[sw_registration_id
];
359 BackgroundSyncRegistrationsProto registrations_proto
;
360 registrations_proto
.set_next_registration_id(registrations
.next_id
);
361 registrations_proto
.set_origin(registrations
.origin
.spec());
363 for (const auto& key_and_registration
: registrations
.registration_map
) {
364 const BackgroundSyncRegistration
& registration
=
365 key_and_registration
.second
;
366 BackgroundSyncRegistrationProto
* registration_proto
=
367 registrations_proto
.add_registration();
368 registration_proto
->set_id(registration
.id
);
369 registration_proto
->set_tag(registration
.tag
);
370 registration_proto
->set_periodicity(registration
.periodicity
);
371 registration_proto
->set_min_period(registration
.min_period
);
372 registration_proto
->set_network_state(registration
.network_state
);
373 registration_proto
->set_power_state(registration
.power_state
);
375 std::string serialized
;
376 bool success
= registrations_proto
.SerializeToString(&serialized
);
379 StoreDataInBackend(sw_registration_id
, registrations
.origin
,
380 kBackgroundSyncUserDataKey
, serialized
, callback
);
383 void BackgroundSyncManager::RegisterDidStore(
384 int64 sw_registration_id
,
385 const BackgroundSyncRegistration
& new_registration
,
386 const StatusAndRegistrationCallback
& callback
,
387 ServiceWorkerStatusCode status
) {
388 if (status
== SERVICE_WORKER_ERROR_NOT_FOUND
) {
389 // The registration is gone.
390 sw_to_registrations_map_
.erase(sw_registration_id
);
391 base::MessageLoop::current()->PostTask(
393 base::Bind(callback
, ERROR_TYPE_STORAGE
, BackgroundSyncRegistration()));
397 if (status
!= SERVICE_WORKER_OK
) {
398 LOG(ERROR
) << "BackgroundSync failed to store registration due to backend "
400 DisableAndClearManager(
401 base::Bind(callback
, ERROR_TYPE_STORAGE
, BackgroundSyncRegistration()));
405 // TODO(jkarlin): Run the registration algorithm.
406 base::MessageLoop::current()->PostTask(
407 FROM_HERE
, base::Bind(callback
, ERROR_TYPE_OK
, new_registration
));
410 void BackgroundSyncManager::RemoveRegistrationFromMap(
411 int64 sw_registration_id
,
412 const RegistrationKey
& registration_key
) {
413 DCHECK(LookupRegistration(sw_registration_id
, registration_key
));
415 BackgroundSyncRegistrations
* registrations
=
416 &sw_to_registrations_map_
[sw_registration_id
];
418 registrations
->registration_map
.erase(registration_key
);
421 void BackgroundSyncManager::AddRegistrationToMap(
422 int64 sw_registration_id
,
424 const BackgroundSyncRegistration
& sync_registration
) {
425 DCHECK_NE(BackgroundSyncRegistration::kInvalidRegistrationId
,
428 BackgroundSyncRegistrations
* registrations
=
429 &sw_to_registrations_map_
[sw_registration_id
];
430 registrations
->origin
= origin
;
432 RegistrationKey
registration_key(sync_registration
);
433 registrations
->registration_map
[registration_key
] = sync_registration
;
436 void BackgroundSyncManager::StoreDataInBackend(
437 int64 sw_registration_id
,
439 const std::string
& backend_key
,
440 const std::string
& data
,
441 const ServiceWorkerStorage::StatusCallback
& callback
) {
442 service_worker_context_
->StoreRegistrationUserData(
443 sw_registration_id
, origin
, backend_key
, data
, callback
);
446 void BackgroundSyncManager::GetDataFromBackend(
447 const std::string
& backend_key
,
448 const ServiceWorkerStorage::GetUserDataForAllRegistrationsCallback
&
450 DCHECK_CURRENTLY_ON(BrowserThread::IO
);
452 service_worker_context_
->GetUserDataForAllRegistrations(backend_key
,
456 void BackgroundSyncManager::UnregisterImpl(
458 int64 sw_registration_id
,
459 const RegistrationKey
& registration_key
,
460 BackgroundSyncRegistration::RegistrationId sync_registration_id
,
461 const StatusCallback
& callback
) {
463 base::MessageLoop::current()->PostTask(
464 FROM_HERE
, base::Bind(callback
, ERROR_TYPE_STORAGE
));
468 const BackgroundSyncRegistration
* existing_registration
=
469 LookupRegistration(sw_registration_id
, registration_key
);
470 if (!existing_registration
||
471 existing_registration
->id
!= sync_registration_id
) {
472 base::MessageLoop::current()->PostTask(
473 FROM_HERE
, base::Bind(callback
, ERROR_TYPE_NOT_FOUND
));
477 RemoveRegistrationFromMap(sw_registration_id
, registration_key
);
481 base::Bind(&BackgroundSyncManager::UnregisterDidStore
,
482 weak_ptr_factory_
.GetWeakPtr(), sw_registration_id
, callback
));
485 void BackgroundSyncManager::UnregisterDidStore(
486 int64 sw_registration_id
,
487 const StatusCallback
& callback
,
488 ServiceWorkerStatusCode status
) {
489 if (status
== SERVICE_WORKER_ERROR_NOT_FOUND
) {
490 // ServiceWorker was unregistered.
491 sw_to_registrations_map_
.erase(sw_registration_id
);
492 base::MessageLoop::current()->PostTask(
493 FROM_HERE
, base::Bind(callback
, ERROR_TYPE_STORAGE
));
497 if (status
!= SERVICE_WORKER_OK
) {
498 LOG(ERROR
) << "BackgroundSync failed to unregister due to backend failure.";
499 DisableAndClearManager(base::Bind(callback
, ERROR_TYPE_STORAGE
));
503 // TODO(jkarlin): Run the registration algorithm.
504 base::MessageLoop::current()->PostTask(FROM_HERE
,
505 base::Bind(callback
, ERROR_TYPE_OK
));
508 void BackgroundSyncManager::GetRegistrationImpl(
510 int64 sw_registration_id
,
511 const RegistrationKey
& registration_key
,
512 const StatusAndRegistrationCallback
& callback
) {
514 base::MessageLoop::current()->PostTask(
516 base::Bind(callback
, ERROR_TYPE_STORAGE
, BackgroundSyncRegistration()));
520 const BackgroundSyncRegistration
* out_registration
=
521 LookupRegistration(sw_registration_id
, registration_key
);
522 if (!out_registration
) {
523 base::MessageLoop::current()->PostTask(
524 FROM_HERE
, base::Bind(callback
, ERROR_TYPE_NOT_FOUND
,
525 BackgroundSyncRegistration()));
529 base::MessageLoop::current()->PostTask(
530 FROM_HERE
, base::Bind(callback
, ERROR_TYPE_OK
, *out_registration
));
533 void BackgroundSyncManager::OnRegistrationDeletedImpl(
534 int64 registration_id
,
535 const base::Closure
& callback
) {
536 // The backend (ServiceWorkerStorage) will delete the data, so just delete the
537 // memory representation here.
538 sw_to_registrations_map_
.erase(registration_id
);
539 base::MessageLoop::current()->PostTask(FROM_HERE
, base::Bind(callback
));
542 void BackgroundSyncManager::OnStorageWipedImpl(const base::Closure
& callback
) {
543 sw_to_registrations_map_
.clear();
548 void BackgroundSyncManager::OnNetworkChanged() {
549 // TODO(jkarlin): Run the scheduling algorithm here if initialized and not
553 void BackgroundSyncManager::PendingStatusAndRegistrationCallback(
554 const StatusAndRegistrationCallback
& callback
,
556 const BackgroundSyncRegistration
& sync_registration
) {
557 // The callback might delete this object, so hang onto a weak ptr to find out.
558 base::WeakPtr
<BackgroundSyncManager
> manager
= weak_ptr_factory_
.GetWeakPtr();
559 callback
.Run(error
, sync_registration
);
561 op_scheduler_
.CompleteOperationAndRunNext();
564 void BackgroundSyncManager::PendingStatusCallback(
565 const StatusCallback
& callback
,
567 // The callback might delete this object, so hang onto a weak ptr to find out.
568 base::WeakPtr
<BackgroundSyncManager
> manager
= weak_ptr_factory_
.GetWeakPtr();
571 op_scheduler_
.CompleteOperationAndRunNext();
574 void BackgroundSyncManager::PendingClosure(const base::Closure
& callback
) {
575 // The callback might delete this object, so hang onto a weak ptr to find out.
576 base::WeakPtr
<BackgroundSyncManager
> manager
= weak_ptr_factory_
.GetWeakPtr();
579 op_scheduler_
.CompleteOperationAndRunNext();
582 base::Closure
BackgroundSyncManager::MakeEmptyCompletion() {
583 return base::Bind(&BackgroundSyncManager::PendingClosure
,
584 weak_ptr_factory_
.GetWeakPtr(),
585 base::Bind(base::DoNothing
));
588 BackgroundSyncManager::StatusAndRegistrationCallback
589 BackgroundSyncManager::MakeStatusAndRegistrationCompletion(
590 const StatusAndRegistrationCallback
& callback
) {
592 &BackgroundSyncManager::PendingStatusAndRegistrationCallback
,
593 weak_ptr_factory_
.GetWeakPtr(), callback
);
596 BackgroundSyncManager::StatusCallback
597 BackgroundSyncManager::MakeStatusCompletion(const StatusCallback
& callback
) {
598 return base::Bind(&BackgroundSyncManager::PendingStatusCallback
,
599 weak_ptr_factory_
.GetWeakPtr(), callback
);
602 } // namespace content