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_registration.h"
6
7 #include <utility>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/threading/thread_task_runner_handle.h"
12 #include "content/browser/service_worker/embedded_worker_status.h"
13 #include "content/browser/service_worker/service_worker_container_host.h"
14 #include "content/browser/service_worker/service_worker_context_core.h"
15 #include "content/browser/service_worker/service_worker_context_wrapper.h"
16 #include "content/browser/service_worker/service_worker_info.h"
17 #include "content/browser/service_worker/service_worker_job_coordinator.h"
18 #include "content/browser/service_worker/service_worker_metrics.h"
19 #include "content/browser/service_worker/service_worker_register_job.h"
20 #include "content/browser/service_worker/service_worker_version.h"
21 #include "content/common/content_navigation_policy.h"
22 #include "content/public/browser/browser_thread.h"
23 #include "third_party/blink/public/mojom/service_worker/service_worker_registration.mojom.h"
24
25 namespace content {
26
27 namespace {
28
29 // If an outgoing active worker has no controllees or the waiting worker called
30 // skipWaiting(), it is given |kMaxLameDuckTime| time to finish its requests
31 // before it is removed. If the waiting worker called skipWaiting() more than
32 // this time ago, or the outgoing worker has had no controllees for a continuous
33 // period of time exceeding this time, the outgoing worker will be removed even
34 // if it has ongoing requests.
35 constexpr base::TimeDelta kMaxLameDuckTime = base::TimeDelta::FromMinutes(5);
36
GetVersionInfo(ServiceWorkerVersion * version)37 ServiceWorkerVersionInfo GetVersionInfo(ServiceWorkerVersion* version) {
38 if (!version)
39 return ServiceWorkerVersionInfo();
40 return version->GetInfo();
41 }
42
43 } // namespace
44
ServiceWorkerRegistration(const blink::mojom::ServiceWorkerRegistrationOptions & options,int64_t registration_id,base::WeakPtr<ServiceWorkerContextCore> context)45 ServiceWorkerRegistration::ServiceWorkerRegistration(
46 const blink::mojom::ServiceWorkerRegistrationOptions& options,
47 int64_t registration_id,
48 base::WeakPtr<ServiceWorkerContextCore> context)
49 : scope_(options.scope),
50 // Safe to convert GURL to Origin because service workers are restricted
51 // to secure contexts.
52 origin_(url::Origin::Create(options.scope)),
53 update_via_cache_(options.update_via_cache),
54 registration_id_(registration_id),
55 status_(Status::kIntact),
56 store_state_(StoreState::kNotStored),
57 should_activate_when_ready_(false),
58 resources_total_size_bytes_(0),
59 context_(context),
60 task_runner_(base::ThreadTaskRunnerHandle::Get()) {
61 DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
62 DCHECK_NE(blink::mojom::kInvalidServiceWorkerRegistrationId, registration_id);
63 DCHECK(context_);
64 context_->AddLiveRegistration(this);
65 }
66
~ServiceWorkerRegistration()67 ServiceWorkerRegistration::~ServiceWorkerRegistration() {
68 DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
69 DCHECK(!listeners_.might_have_observers());
70 if (context_)
71 context_->RemoveLiveRegistration(registration_id_);
72 }
73
SetStatus(Status status)74 void ServiceWorkerRegistration::SetStatus(Status status) {
75 if (status_ == status)
76 return;
77 #if DCHECK_IS_ON()
78 switch (status_) {
79 case Status::kIntact:
80 DCHECK_EQ(status, Status::kUninstalling);
81 break;
82 case Status::kUninstalling:
83 // All transitions are allowed:
84 // - To kIntact: resurrected.
85 // - To kUninstalled: finished uninstalling.
86 break;
87 case Status::kUninstalled:
88 NOTREACHED();
89 break;
90 }
91 #endif // DCHECK_IS_ON()
92
93 status_ = status;
94
95 if (active_version_)
96 active_version_->SetRegistrationStatus(status_);
97 if (waiting_version_)
98 waiting_version_->SetRegistrationStatus(status_);
99 if (installing_version_)
100 installing_version_->SetRegistrationStatus(status_);
101 }
102
IsStored() const103 bool ServiceWorkerRegistration::IsStored() const {
104 return context_ && store_state_ == StoreState::kStored;
105 }
106
SetStored()107 void ServiceWorkerRegistration::SetStored() {
108 store_state_ = StoreState::kStored;
109 }
110
UnsetStored()111 void ServiceWorkerRegistration::UnsetStored() {
112 store_state_ = StoreState::kNotStored;
113 }
114
GetNewestVersion() const115 ServiceWorkerVersion* ServiceWorkerRegistration::GetNewestVersion() const {
116 if (installing_version())
117 return installing_version();
118 if (waiting_version())
119 return waiting_version();
120 return active_version();
121 }
122
AddListener(Listener * listener)123 void ServiceWorkerRegistration::AddListener(Listener* listener) {
124 listeners_.AddObserver(listener);
125 }
126
RemoveListener(Listener * listener)127 void ServiceWorkerRegistration::RemoveListener(Listener* listener) {
128 listeners_.RemoveObserver(listener);
129 }
130
NotifyRegistrationFailed()131 void ServiceWorkerRegistration::NotifyRegistrationFailed() {
132 for (auto& observer : listeners_)
133 observer.OnRegistrationFailed(this);
134 NotifyRegistrationFinished();
135 }
136
NotifyUpdateFound()137 void ServiceWorkerRegistration::NotifyUpdateFound() {
138 for (auto& observer : listeners_)
139 observer.OnUpdateFound(this);
140 }
141
NotifyVersionAttributesChanged(blink::mojom::ChangedServiceWorkerObjectsMaskPtr mask)142 void ServiceWorkerRegistration::NotifyVersionAttributesChanged(
143 blink::mojom::ChangedServiceWorkerObjectsMaskPtr mask) {
144 for (auto& observer : listeners_)
145 observer.OnVersionAttributesChanged(this, mask.Clone());
146 if (mask->active || mask->waiting)
147 NotifyRegistrationFinished();
148 }
149
GetInfo()150 ServiceWorkerRegistrationInfo ServiceWorkerRegistration::GetInfo() {
151 DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
152 return ServiceWorkerRegistrationInfo(
153 scope(), update_via_cache(), registration_id_,
154 is_deleted() ? ServiceWorkerRegistrationInfo::IS_DELETED
155 : ServiceWorkerRegistrationInfo::IS_NOT_DELETED,
156 GetVersionInfo(active_version_.get()),
157 GetVersionInfo(waiting_version_.get()),
158 GetVersionInfo(installing_version_.get()), resources_total_size_bytes_,
159 navigation_preload_state_.enabled,
160 navigation_preload_state_.header.length());
161 }
162
SetActiveVersion(const scoped_refptr<ServiceWorkerVersion> & version)163 void ServiceWorkerRegistration::SetActiveVersion(
164 const scoped_refptr<ServiceWorkerVersion>& version) {
165 if (active_version_ == version)
166 return;
167
168 should_activate_when_ready_ = false;
169
170 auto mask =
171 blink::mojom::ChangedServiceWorkerObjectsMask::New(false, false, false);
172 if (version) {
173 UnsetVersionInternal(version.get(), mask.get());
174 version->SetRegistrationStatus(status_);
175 }
176 active_version_ = version;
177 if (active_version_)
178 active_version_->SetNavigationPreloadState(navigation_preload_state_);
179 mask->active = true;
180
181 NotifyVersionAttributesChanged(std::move(mask));
182 }
183
SetWaitingVersion(const scoped_refptr<ServiceWorkerVersion> & version)184 void ServiceWorkerRegistration::SetWaitingVersion(
185 const scoped_refptr<ServiceWorkerVersion>& version) {
186 if (waiting_version_ == version)
187 return;
188
189 should_activate_when_ready_ = false;
190
191 auto mask =
192 blink::mojom::ChangedServiceWorkerObjectsMask::New(false, false, false);
193 if (version) {
194 UnsetVersionInternal(version.get(), mask.get());
195 version->SetRegistrationStatus(status_);
196 }
197 waiting_version_ = version;
198 mask->waiting = true;
199
200 NotifyVersionAttributesChanged(std::move(mask));
201 }
202
SetInstallingVersion(const scoped_refptr<ServiceWorkerVersion> & version)203 void ServiceWorkerRegistration::SetInstallingVersion(
204 const scoped_refptr<ServiceWorkerVersion>& version) {
205 if (installing_version_ == version)
206 return;
207 auto mask =
208 blink::mojom::ChangedServiceWorkerObjectsMask::New(false, false, false);
209 if (version) {
210 UnsetVersionInternal(version.get(), mask.get());
211 version->SetRegistrationStatus(status_);
212 }
213 installing_version_ = version;
214 mask->installing = true;
215 NotifyVersionAttributesChanged(std::move(mask));
216 }
217
UnsetVersion(ServiceWorkerVersion * version)218 void ServiceWorkerRegistration::UnsetVersion(ServiceWorkerVersion* version) {
219 if (!version)
220 return;
221 auto mask =
222 blink::mojom::ChangedServiceWorkerObjectsMask::New(false, false, false);
223 UnsetVersionInternal(version, mask.get());
224 if (mask->installing || mask->waiting || mask->active)
225 NotifyVersionAttributesChanged(std::move(mask));
226 }
227
UnsetVersionInternal(ServiceWorkerVersion * version,blink::mojom::ChangedServiceWorkerObjectsMask * mask)228 void ServiceWorkerRegistration::UnsetVersionInternal(
229 ServiceWorkerVersion* version,
230 blink::mojom::ChangedServiceWorkerObjectsMask* mask) {
231 DCHECK(version);
232
233 if (installing_version_.get() == version) {
234 installing_version_ = nullptr;
235 mask->installing = true;
236 } else if (waiting_version_.get() == version) {
237 waiting_version_ = nullptr;
238 should_activate_when_ready_ = false;
239 mask->waiting = true;
240 } else if (active_version_.get() == version) {
241 active_version_ = nullptr;
242 mask->active = true;
243 }
244 }
245
SetUpdateViaCache(blink::mojom::ServiceWorkerUpdateViaCache update_via_cache)246 void ServiceWorkerRegistration::SetUpdateViaCache(
247 blink::mojom::ServiceWorkerUpdateViaCache update_via_cache) {
248 if (update_via_cache_ == update_via_cache)
249 return;
250 update_via_cache_ = update_via_cache;
251 for (auto& observer : listeners_)
252 observer.OnUpdateViaCacheChanged(this);
253 }
254
ActivateWaitingVersionWhenReady()255 void ServiceWorkerRegistration::ActivateWaitingVersionWhenReady() {
256 DCHECK(waiting_version());
257 should_activate_when_ready_ = true;
258 if (IsReadyToActivate()) {
259 ActivateWaitingVersion(false /* delay */);
260 return;
261 }
262
263 if (IsLameDuckActiveVersion()) {
264 if (active_version()->running_status() == EmbeddedWorkerStatus::RUNNING) {
265 // If the waiting worker is ready and the active worker needs to be
266 // swapped out, ask the active worker to trigger idle timer as soon as
267 // possible.
268 active_version()->TriggerIdleTerminationAsap();
269 }
270 StartLameDuckTimer();
271 }
272 }
273
ClaimClients()274 void ServiceWorkerRegistration::ClaimClients() {
275 DCHECK(context_);
276 DCHECK(active_version());
277
278 // https://w3c.github.io/ServiceWorker/#clients-claim
279 //
280 // "For each service worker client client whose origin is the same as the
281 // service worker's origin:
282 const bool include_reserved_clients = false;
283 // Include clients in BackForwardCache in order to evict them if needed.
284 const bool include_back_forward_cached_clients = true;
285 for (std::unique_ptr<ServiceWorkerContextCore::ContainerHostIterator> it =
286 context_->GetClientContainerHostIterator(
287 scope_.GetOrigin(), include_reserved_clients,
288 include_back_forward_cached_clients);
289 !it->IsAtEnd(); it->Advance()) {
290 ServiceWorkerContainerHost* container_host = it->GetContainerHost();
291 // "1. If client’s execution ready flag is unset or client’s discarded flag
292 // is set, continue."
293 // |include_reserved_clients| ensures only execution ready clients are
294 // returned.
295 DCHECK(container_host->is_execution_ready());
296
297 // This is part of step 5 but performed here as an optimization. Do nothing
298 // if this version is already the controller.
299 if (container_host->controller() == active_version())
300 continue;
301
302 // "2. If client is not a secure context, continue."
303 if (!container_host->IsEligibleForServiceWorkerController())
304 continue;
305
306 // "3. Let registration be the result of running Match Service Worker
307 // Registration algorithm passing client’s creation URL as the argument.
308 // 4. If registration is not the service worker's containing service worker
309 // registration, continue."
310 if (container_host->MatchRegistration() != this)
311 continue;
312
313 // Evict the client in BackForwardCache.
314 if (container_host->IsInBackForwardCache())
315 container_host->EvictFromBackForwardCache(
316 BackForwardCacheMetrics::NotRestoredReason::kServiceWorkerClaim);
317
318 // The remaining steps are performed here:
319 container_host->ClaimedByRegistration(this);
320 }
321 }
322
DeleteAndClearWhenReady()323 void ServiceWorkerRegistration::DeleteAndClearWhenReady() {
324 DCHECK(context_);
325 if (is_deleted()) {
326 // We already deleted and are waiting to clear, or the registration is
327 // already cleared.
328 return;
329 }
330
331 context_->registry()->DeleteRegistration(
332 this, scope().GetOrigin(),
333 AdaptCallbackForRepeating(
334 base::BindOnce(&ServiceWorkerRegistration::OnDeleteFinished, this)));
335
336 if (!active_version() || !active_version()->HasControllee())
337 Clear();
338 }
339
DeleteAndClearImmediately()340 void ServiceWorkerRegistration::DeleteAndClearImmediately() {
341 DCHECK(context_);
342 if (!is_deleted()) {
343 context_->registry()->DeleteRegistration(
344 this, scope().GetOrigin(),
345 AdaptCallbackForRepeating(base::BindOnce(
346 &ServiceWorkerRegistration::OnDeleteFinished, this)));
347 }
348
349 if (is_uninstalling())
350 Clear();
351 }
352
AbortPendingClear(StatusCallback callback)353 void ServiceWorkerRegistration::AbortPendingClear(StatusCallback callback) {
354 DCHECK(context_);
355
356 switch (status_) {
357 case Status::kIntact:
358 std::move(callback).Run(blink::ServiceWorkerStatusCode::kOk);
359 return;
360 case Status::kUninstalling:
361 break;
362 case Status::kUninstalled:
363 NOTREACHED()
364 << "attempt to resurrect a completely uninstalled registration";
365 break;
366 }
367
368 context_->registry()->NotifyDoneUninstallingRegistration(this,
369 Status::kIntact);
370
371 scoped_refptr<ServiceWorkerVersion> most_recent_version =
372 waiting_version() ? waiting_version() : active_version();
373 DCHECK(most_recent_version.get());
374 context_->registry()->NotifyInstallingRegistration(this);
375 context_->registry()->StoreRegistration(
376 this, most_recent_version.get(),
377 base::BindOnce(&ServiceWorkerRegistration::OnRestoreFinished, this,
378 std::move(callback), most_recent_version));
379 }
380
OnNoControllees(ServiceWorkerVersion * version)381 void ServiceWorkerRegistration::OnNoControllees(ServiceWorkerVersion* version) {
382 DCHECK(context_);
383 if (version != active_version())
384 return;
385
386 if (is_uninstalling()) {
387 // TODO(falken): This can destroy the caller (ServiceWorkerVersion). Try to
388 // make this async.
389 Clear();
390 return;
391 }
392
393 if (IsReadyToActivate()) {
394 ActivateWaitingVersion(true /* delay */);
395 return;
396 }
397
398 if (IsLameDuckActiveVersion()) {
399 if (should_activate_when_ready_ &&
400 active_version()->running_status() == EmbeddedWorkerStatus::RUNNING) {
401 // If the waiting worker is ready and the active worker needs to be
402 // swapped out, ask the active worker to trigger idle timer as soon as
403 // possible.
404 active_version()->TriggerIdleTerminationAsap();
405 }
406 StartLameDuckTimer();
407 }
408 }
409
OnNoWork(ServiceWorkerVersion * version)410 void ServiceWorkerRegistration::OnNoWork(ServiceWorkerVersion* version) {
411 DCHECK(context_);
412
413 if (version == active_version() && IsReadyToActivate())
414 ActivateWaitingVersion(true /* delay */);
415 }
416
IsReadyToActivate() const417 bool ServiceWorkerRegistration::IsReadyToActivate() const {
418 if (!should_activate_when_ready_)
419 return false;
420
421 DCHECK(waiting_version());
422 const ServiceWorkerVersion* waiting = waiting_version();
423 const ServiceWorkerVersion* active = active_version();
424 if (!active) {
425 return true;
426 }
427 if (IsLameDuckActiveVersion()) {
428 return active->HasNoWork() ||
429 waiting->TimeSinceSkipWaiting() > kMaxLameDuckTime ||
430 active->TimeSinceNoControllees() > kMaxLameDuckTime;
431 }
432 return false;
433 }
434
IsLameDuckActiveVersion() const435 bool ServiceWorkerRegistration::IsLameDuckActiveVersion() const {
436 if (!waiting_version() || !active_version())
437 return false;
438 return waiting_version()->skip_waiting() ||
439 !active_version()->HasControllee();
440 }
441
StartLameDuckTimer()442 void ServiceWorkerRegistration::StartLameDuckTimer() {
443 DCHECK(IsLameDuckActiveVersion());
444 if (lame_duck_timer_.IsRunning())
445 return;
446
447 lame_duck_timer_.Start(
448 FROM_HERE, kMaxLameDuckTime,
449 base::BindRepeating(
450 &ServiceWorkerRegistration::RemoveLameDuckIfNeeded,
451 Unretained(this) /* OK because |this| owns the timer */));
452 }
453
RemoveLameDuckIfNeeded()454 void ServiceWorkerRegistration::RemoveLameDuckIfNeeded() {
455 if (!should_activate_when_ready_) {
456 lame_duck_timer_.Stop();
457 return;
458 }
459
460 if (IsReadyToActivate()) {
461 ActivateWaitingVersion(false /* delay */);
462 return;
463 }
464
465 if (!IsLameDuckActiveVersion()) {
466 lame_duck_timer_.Stop();
467 }
468 }
469
ActivateWaitingVersion(bool delay)470 void ServiceWorkerRegistration::ActivateWaitingVersion(bool delay) {
471 DCHECK_CURRENTLY_ON(ServiceWorkerContext::GetCoreThreadId());
472 DCHECK(context_);
473 DCHECK(IsReadyToActivate());
474 should_activate_when_ready_ = false;
475 lame_duck_timer_.Stop();
476
477 scoped_refptr<ServiceWorkerVersion> activating_version = waiting_version();
478 scoped_refptr<ServiceWorkerVersion> exiting_version = active_version();
479
480 if (activating_version->is_redundant())
481 return; // Activation is no longer relevant.
482
483 // "5. If exitingWorker is not null,
484 if (exiting_version.get()) {
485 // Whenever activation happens, evict bfcached controllees.
486 if (IsBackForwardCacheEnabled()) {
487 exiting_version->EvictBackForwardCachedControllees(
488 BackForwardCacheMetrics::NotRestoredReason::
489 kServiceWorkerVersionActivation);
490 }
491
492 // TODO(falken): Update the quoted spec comments once
493 // https://github.com/slightlyoff/ServiceWorker/issues/916 is codified in
494 // the spec.
495 // "1. Wait for exitingWorker to finish handling any in-progress requests."
496 // This is already handled by IsReadyToActivate().
497 // "2. Terminate exitingWorker."
498 exiting_version->StopWorker(base::DoNothing());
499 // "3. Run the [[UpdateState]] algorithm passing exitingWorker and
500 // "redundant" as the arguments."
501 exiting_version->SetStatus(ServiceWorkerVersion::REDUNDANT);
502 }
503
504 // "6. Set serviceWorkerRegistration.activeWorker to activatingWorker."
505 // "7. Set serviceWorkerRegistration.waitingWorker to null."
506 SetActiveVersion(activating_version);
507
508 // "8. Run the [[UpdateState]] algorithm passing registration.activeWorker and
509 // "activating" as arguments."
510 activating_version->SetStatus(ServiceWorkerVersion::ACTIVATING);
511 // "9. Fire a simple event named controllerchange..."
512 if (activating_version->skip_waiting()) {
513 for (auto& observer : listeners_)
514 observer.OnSkippedWaiting(this);
515 }
516
517 // "10. Queue a task to fire an event named activate..."
518 // The browser could be shutting down. To avoid spurious start worker
519 // failures, wait a bit before continuing.
520 if (delay) {
521 task_runner_->PostDelayedTask(
522 FROM_HERE,
523 base::BindOnce(&ServiceWorkerRegistration::ContinueActivation, this,
524 activating_version),
525 base::TimeDelta::FromSeconds(1));
526 } else {
527 ContinueActivation(std::move(activating_version));
528 }
529 }
530
ContinueActivation(scoped_refptr<ServiceWorkerVersion> activating_version)531 void ServiceWorkerRegistration::ContinueActivation(
532 scoped_refptr<ServiceWorkerVersion> activating_version) {
533 if (!context_)
534 return;
535 if (active_version() != activating_version.get())
536 return;
537 DCHECK_EQ(ServiceWorkerVersion::ACTIVATING, activating_version->status());
538 activating_version->RunAfterStartWorker(
539 ServiceWorkerMetrics::EventType::ACTIVATE,
540 base::BindOnce(&ServiceWorkerRegistration::DispatchActivateEvent, this,
541 activating_version));
542 }
543
ForceDelete()544 void ServiceWorkerRegistration::ForceDelete() {
545 DCHECK(context_);
546 DCHECK(!is_uninstalled()) << "attempt to delete registration twice";
547
548 // Protect the registration since version->Doom() can stop |version|, which
549 // destroys start worker callbacks, which might be the only things holding a
550 // reference to |this|.
551 scoped_refptr<ServiceWorkerRegistration> protect(this);
552
553 // Abort any queued or running jobs for this registration.
554 context_->job_coordinator()->Abort(scope());
555
556 // The rest of this function is similar to Clear() but is slightly different
557 // because this emergency deletion isn't part of the spec and happens
558 // outside of the normal job coordinator.
559 // TODO(falken): Consider merging the two.
560 should_activate_when_ready_ = false;
561
562 // Doom versions. This sets the versions to redundant and tells the
563 // controllees that they are gone.
564 //
565 // There can't be an installing version since we aborted any register job.
566 DCHECK(!installing_version_);
567 auto mask =
568 blink::mojom::ChangedServiceWorkerObjectsMask::New(false, false, false);
569 // Unset the version first so we stop listening to the version as it might
570 // invoke listener methods during Doom().
571 if (scoped_refptr<ServiceWorkerVersion> waiting_version = waiting_version_) {
572 UnsetVersionInternal(waiting_version.get(), mask.get());
573 waiting_version->Doom();
574 }
575 if (scoped_refptr<ServiceWorkerVersion> active_version = active_version_) {
576 UnsetVersionInternal(active_version.get(), mask.get());
577 active_version->Doom();
578 }
579
580 // Delete the registration and its state from storage.
581 if (status() == Status::kIntact) {
582 context_->registry()->DeleteRegistration(
583 this, scope().GetOrigin(),
584 base::BindOnce(&ServiceWorkerRegistration::OnDeleteFinished, protect));
585 }
586 DCHECK(is_uninstalling());
587 context_->registry()->NotifyDoneUninstallingRegistration(
588 this, Status::kUninstalled);
589
590 // Tell observers that this registration is gone.
591 NotifyRegistrationFailed();
592 }
593
NotifyRegistrationFinished()594 void ServiceWorkerRegistration::NotifyRegistrationFinished() {
595 std::vector<base::OnceClosure> callbacks;
596 callbacks.swap(registration_finished_callbacks_);
597 for (auto& callback : callbacks)
598 std::move(callback).Run();
599 }
600
SetTaskRunnerForTest(scoped_refptr<base::SingleThreadTaskRunner> task_runner)601 void ServiceWorkerRegistration::SetTaskRunnerForTest(
602 scoped_refptr<base::SingleThreadTaskRunner> task_runner) {
603 task_runner_ = task_runner;
604 }
605
EnableNavigationPreload(bool enable)606 void ServiceWorkerRegistration::EnableNavigationPreload(bool enable) {
607 navigation_preload_state_.enabled = enable;
608 if (active_version_)
609 active_version_->SetNavigationPreloadState(navigation_preload_state_);
610 }
611
SetNavigationPreloadHeader(const std::string & header)612 void ServiceWorkerRegistration::SetNavigationPreloadHeader(
613 const std::string& header) {
614 navigation_preload_state_.header = header;
615 if (active_version_)
616 active_version_->SetNavigationPreloadState(navigation_preload_state_);
617 }
618
RegisterRegistrationFinishedCallback(base::OnceClosure callback)619 void ServiceWorkerRegistration::RegisterRegistrationFinishedCallback(
620 base::OnceClosure callback) {
621 // This should only be called if the registration is in progress.
622 DCHECK(!active_version() && !waiting_version() && !is_uninstalled() &&
623 !is_uninstalling());
624 registration_finished_callbacks_.push_back(std::move(callback));
625 }
626
DispatchActivateEvent(scoped_refptr<ServiceWorkerVersion> activating_version,blink::ServiceWorkerStatusCode start_worker_status)627 void ServiceWorkerRegistration::DispatchActivateEvent(
628 scoped_refptr<ServiceWorkerVersion> activating_version,
629 blink::ServiceWorkerStatusCode start_worker_status) {
630 if (start_worker_status != blink::ServiceWorkerStatusCode::kOk) {
631 OnActivateEventFinished(activating_version, start_worker_status);
632 return;
633 }
634 if (activating_version != active_version()) {
635 OnActivateEventFinished(activating_version,
636 blink::ServiceWorkerStatusCode::kErrorFailed);
637 return;
638 }
639
640 DCHECK_EQ(ServiceWorkerVersion::ACTIVATING, activating_version->status());
641 DCHECK_EQ(EmbeddedWorkerStatus::RUNNING, activating_version->running_status())
642 << "Worker stopped too soon after it was started.";
643 int request_id = activating_version->StartRequest(
644 ServiceWorkerMetrics::EventType::ACTIVATE,
645 base::BindOnce(&ServiceWorkerRegistration::OnActivateEventFinished, this,
646 activating_version));
647 activating_version->endpoint()->DispatchActivateEvent(
648 activating_version->CreateSimpleEventCallback(request_id));
649 }
650
OnActivateEventFinished(scoped_refptr<ServiceWorkerVersion> activating_version,blink::ServiceWorkerStatusCode status)651 void ServiceWorkerRegistration::OnActivateEventFinished(
652 scoped_refptr<ServiceWorkerVersion> activating_version,
653 blink::ServiceWorkerStatusCode status) {
654 // Activate is prone to failing due to shutdown, because it's triggered when
655 // tabs close.
656 bool is_shutdown =
657 !context_ || context_->wrapper()->process_manager()->IsShutdown();
658 ServiceWorkerMetrics::RecordActivateEventStatus(status, is_shutdown);
659
660 if (!context_ || activating_version != active_version() ||
661 activating_version->status() != ServiceWorkerVersion::ACTIVATING) {
662 return;
663 }
664
665 // Normally, the worker is committed to become activated once we get here, per
666 // spec. E.g., if the script rejected waitUntil or had an unhandled exception,
667 // it should still be activated. However, if the failure occurred during
668 // shutdown, ignore it to give the worker another chance the next time the
669 // browser starts up.
670 if (is_shutdown && status != blink::ServiceWorkerStatusCode::kOk)
671 return;
672
673 // "Run the Update State algorithm passing registration's active worker and
674 // 'activated' as the arguments."
675 activating_version->SetStatus(ServiceWorkerVersion::ACTIVATED);
676 context_->registry()->UpdateToActiveState(id(), scope().GetOrigin(),
677 base::DoNothing());
678 }
679
OnDeleteFinished(blink::ServiceWorkerStatusCode status)680 void ServiceWorkerRegistration::OnDeleteFinished(
681 blink::ServiceWorkerStatusCode status) {
682 for (auto& listener : listeners_)
683 listener.OnRegistrationDeleted(this);
684 }
685
Clear()686 void ServiceWorkerRegistration::Clear() {
687 DCHECK(is_uninstalling());
688 SetStatus(Status::kUninstalled);
689 should_activate_when_ready_ = false;
690
691 // Some callbacks, at least OnRegistrationFinishedUninstalling and
692 // NotifyDoneUninstallingRegistration, may drop their references to
693 // |this|, so protect it first.
694 // TODO(falken): Clean this up, can we call the observers from a task
695 // or make the observers more polite?
696 auto protect = base::WrapRefCounted(this);
697
698 if (context_) {
699 context_->registry()->NotifyDoneUninstallingRegistration(
700 this, Status::kUninstalled);
701 }
702
703 std::vector<scoped_refptr<ServiceWorkerVersion>> versions_to_doom;
704 auto mask =
705 blink::mojom::ChangedServiceWorkerObjectsMask::New(false, false, false);
706 if (installing_version_.get()) {
707 versions_to_doom.push_back(installing_version_);
708 installing_version_ = nullptr;
709 mask->installing = true;
710 }
711 if (waiting_version_.get()) {
712 versions_to_doom.push_back(waiting_version_);
713 waiting_version_ = nullptr;
714 mask->waiting = true;
715 }
716 if (active_version_.get()) {
717 versions_to_doom.push_back(active_version_);
718 active_version_ = nullptr;
719 mask->active = true;
720 }
721
722 if (mask->installing || mask->waiting || mask->active) {
723 NotifyVersionAttributesChanged(std::move(mask));
724
725 // Doom only after notifying attributes changed, because the spec requires
726 // the attributes to be cleared by the time the statechange event is
727 // dispatched.
728 for (const auto& version : versions_to_doom)
729 version->Doom();
730 }
731
732 for (auto& observer : listeners_)
733 observer.OnRegistrationFinishedUninstalling(this);
734 }
735
OnRestoreFinished(StatusCallback callback,scoped_refptr<ServiceWorkerVersion> version,blink::ServiceWorkerStatusCode status)736 void ServiceWorkerRegistration::OnRestoreFinished(
737 StatusCallback callback,
738 scoped_refptr<ServiceWorkerVersion> version,
739 blink::ServiceWorkerStatusCode status) {
740 if (!context_) {
741 std::move(callback).Run(blink::ServiceWorkerStatusCode::kErrorAbort);
742 return;
743 }
744 context_->registry()->NotifyDoneInstallingRegistration(this, version.get(),
745 status);
746 std::move(callback).Run(status);
747 }
748
749 } // namespace content
750