Bug 1850713: remove duplicated setting of early hint preloader id in `ScriptLoader...
[gecko.git] / dom / serviceworkers / ServiceWorkerRegistrar.cpp
blob55840c5b8760d09f23d8edebee410ec4ea9488ac
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "ServiceWorkerRegistrar.h"
8 #include "ServiceWorkerManager.h"
9 #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
10 #include "mozilla/dom/DOMException.h"
11 #include "mozilla/net/MozURL.h"
12 #include "mozilla/StaticPrefs_dom.h"
14 #include "nsIEventTarget.h"
15 #include "nsIInputStream.h"
16 #include "nsILineInputStream.h"
17 #include "nsIObserverService.h"
18 #include "nsIOutputStream.h"
19 #include "nsISafeOutputStream.h"
20 #include "nsIServiceWorkerManager.h"
21 #include "nsIWritablePropertyBag2.h"
23 #include "MainThreadUtils.h"
24 #include "mozilla/ClearOnShutdown.h"
25 #include "mozilla/CycleCollectedJSContext.h"
26 #include "mozilla/dom/StorageActivityService.h"
27 #include "mozilla/ErrorNames.h"
28 #include "mozilla/ipc/BackgroundChild.h"
29 #include "mozilla/ipc/BackgroundParent.h"
30 #include "mozilla/ipc/PBackgroundChild.h"
31 #include "mozilla/ModuleUtils.h"
32 #include "mozilla/Result.h"
33 #include "mozilla/ResultExtensions.h"
34 #include "mozilla/Services.h"
35 #include "mozilla/StaticPtr.h"
36 #include "nsAppDirectoryServiceDefs.h"
37 #include "nsComponentManagerUtils.h"
38 #include "nsContentUtils.h"
39 #include "nsDirectoryServiceUtils.h"
40 #include "nsNetCID.h"
41 #include "nsNetUtil.h"
42 #include "nsServiceManagerUtils.h"
43 #include "nsThreadUtils.h"
44 #include "nsXULAppAPI.h"
45 #include "ServiceWorkerUtils.h"
47 using namespace mozilla::ipc;
49 extern mozilla::LazyLogModule sWorkerTelemetryLog;
51 #ifdef LOG
52 # undef LOG
53 #endif
54 #define LOG(_args) MOZ_LOG(sWorkerTelemetryLog, LogLevel::Debug, _args);
56 namespace mozilla::dom {
58 namespace {
60 static const char* gSupportedRegistrarVersions[] = {
61 SERVICEWORKERREGISTRAR_VERSION, "8", "7", "6", "5", "4", "3", "2"};
63 static const uint32_t kInvalidGeneration = static_cast<uint32_t>(-1);
65 StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
67 nsresult GetOriginAndBaseDomain(const nsACString& aURL, nsACString& aOrigin,
68 nsACString& aBaseDomain) {
69 RefPtr<net::MozURL> url;
70 nsresult rv = net::MozURL::Init(getter_AddRefs(url), aURL);
71 if (NS_WARN_IF(NS_FAILED(rv))) {
72 return rv;
75 url->Origin(aOrigin);
77 rv = url->BaseDomain(aBaseDomain);
78 if (NS_WARN_IF(NS_FAILED(rv))) {
79 return rv;
82 return NS_OK;
85 nsresult ReadLine(nsILineInputStream* aStream, nsACString& aValue) {
86 bool hasMoreLines;
87 nsresult rv = aStream->ReadLine(aValue, &hasMoreLines);
88 if (NS_WARN_IF(NS_FAILED(rv))) {
89 return rv;
92 if (NS_WARN_IF(!hasMoreLines)) {
93 return NS_ERROR_FAILURE;
96 return NS_OK;
99 nsresult CreatePrincipalInfo(nsILineInputStream* aStream,
100 ServiceWorkerRegistrationData* aEntry,
101 bool aSkipSpec = false) {
102 nsAutoCString suffix;
103 nsresult rv = ReadLine(aStream, suffix);
104 if (NS_WARN_IF(NS_FAILED(rv))) {
105 return rv;
108 OriginAttributes attrs;
109 if (!attrs.PopulateFromSuffix(suffix)) {
110 return NS_ERROR_INVALID_ARG;
113 if (aSkipSpec) {
114 nsAutoCString unused;
115 nsresult rv = ReadLine(aStream, unused);
116 if (NS_WARN_IF(NS_FAILED(rv))) {
117 return rv;
121 rv = ReadLine(aStream, aEntry->scope());
122 if (NS_WARN_IF(NS_FAILED(rv))) {
123 return rv;
126 nsCString origin;
127 nsCString baseDomain;
128 rv = GetOriginAndBaseDomain(aEntry->scope(), origin, baseDomain);
129 if (NS_WARN_IF(NS_FAILED(rv))) {
130 return rv;
133 aEntry->principal() = mozilla::ipc::ContentPrincipalInfo(
134 attrs, origin, aEntry->scope(), Nothing(), baseDomain);
136 return NS_OK;
139 const IPCNavigationPreloadState gDefaultNavigationPreloadState(false,
140 "true"_ns);
142 } // namespace
144 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar, nsIObserver, nsIAsyncShutdownBlocker)
146 void ServiceWorkerRegistrar::Initialize() {
147 MOZ_ASSERT(!gServiceWorkerRegistrar);
149 if (!XRE_IsParentProcess()) {
150 return;
153 gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
154 ClearOnShutdown(&gServiceWorkerRegistrar);
156 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
157 if (obs) {
158 DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
159 "profile-after-change", false);
160 MOZ_ASSERT(NS_SUCCEEDED(rv));
164 /* static */
165 already_AddRefed<ServiceWorkerRegistrar> ServiceWorkerRegistrar::Get() {
166 MOZ_ASSERT(XRE_IsParentProcess());
168 MOZ_ASSERT(gServiceWorkerRegistrar);
169 RefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
170 return service.forget();
173 ServiceWorkerRegistrar::ServiceWorkerRegistrar()
174 : mMonitor("ServiceWorkerRegistrar.mMonitor"),
175 mDataLoaded(false),
176 mDataGeneration(kInvalidGeneration),
177 mFileGeneration(kInvalidGeneration),
178 mRetryCount(0),
179 mShuttingDown(false),
180 mSaveDataRunnableDispatched(false) {
181 MOZ_ASSERT(NS_IsMainThread());
184 ServiceWorkerRegistrar::~ServiceWorkerRegistrar() {
185 MOZ_ASSERT(!mSaveDataRunnableDispatched);
188 void ServiceWorkerRegistrar::GetRegistrations(
189 nsTArray<ServiceWorkerRegistrationData>& aValues) {
190 MOZ_ASSERT(NS_IsMainThread());
191 MOZ_ASSERT(aValues.IsEmpty());
193 MonitorAutoLock lock(mMonitor);
195 // If we don't have the profile directory, profile is not started yet (and
196 // probably we are in a utest).
197 if (!mProfileDir) {
198 return;
201 // We care just about the first execution because this can be blocked by
202 // loading data from disk.
203 static bool firstTime = true;
204 TimeStamp startTime;
206 if (firstTime) {
207 startTime = TimeStamp::NowLoRes();
210 // Waiting for data loaded.
211 mMonitor.AssertCurrentThreadOwns();
212 while (!mDataLoaded) {
213 mMonitor.Wait();
216 aValues.AppendElements(mData);
218 MaybeResetGeneration();
219 MOZ_DIAGNOSTIC_ASSERT(mDataGeneration != kInvalidGeneration);
220 MOZ_DIAGNOSTIC_ASSERT(mFileGeneration != kInvalidGeneration);
222 if (firstTime) {
223 firstTime = false;
224 Telemetry::AccumulateTimeDelta(
225 Telemetry::SERVICE_WORKER_REGISTRATION_LOADING, startTime);
229 namespace {
231 bool Equivalent(const ServiceWorkerRegistrationData& aLeft,
232 const ServiceWorkerRegistrationData& aRight) {
233 MOZ_ASSERT(aLeft.principal().type() ==
234 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
235 MOZ_ASSERT(aRight.principal().type() ==
236 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
238 const auto& leftPrincipal = aLeft.principal().get_ContentPrincipalInfo();
239 const auto& rightPrincipal = aRight.principal().get_ContentPrincipalInfo();
241 // Only compare the attributes, not the spec part of the principal.
242 // The scope comparison above already covers the origin and codebase
243 // principals include the full path in their spec which is not what
244 // we want here.
245 return aLeft.scope() == aRight.scope() &&
246 leftPrincipal.attrs() == rightPrincipal.attrs();
249 } // anonymous namespace
251 void ServiceWorkerRegistrar::RegisterServiceWorker(
252 const ServiceWorkerRegistrationData& aData) {
253 AssertIsOnBackgroundThread();
255 if (mShuttingDown) {
256 NS_WARNING("Failed to register a serviceWorker during shutting down.");
257 return;
261 MonitorAutoLock lock(mMonitor);
262 MOZ_ASSERT(mDataLoaded);
263 RegisterServiceWorkerInternal(aData);
266 MaybeScheduleSaveData();
267 StorageActivityService::SendActivity(aData.principal());
270 void ServiceWorkerRegistrar::UnregisterServiceWorker(
271 const PrincipalInfo& aPrincipalInfo, const nsACString& aScope) {
272 AssertIsOnBackgroundThread();
274 if (mShuttingDown) {
275 NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
276 return;
279 bool deleted = false;
282 MonitorAutoLock lock(mMonitor);
283 MOZ_ASSERT(mDataLoaded);
285 ServiceWorkerRegistrationData tmp;
286 tmp.principal() = aPrincipalInfo;
287 tmp.scope() = aScope;
289 for (uint32_t i = 0; i < mData.Length(); ++i) {
290 if (Equivalent(tmp, mData[i])) {
291 gServiceWorkersRegistered--;
292 if (mData[i].currentWorkerHandlesFetch()) {
293 gServiceWorkersRegisteredFetch--;
295 // Update Telemetry
296 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_REGISTRATIONS,
297 u"All"_ns, gServiceWorkersRegistered);
298 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_REGISTRATIONS,
299 u"Fetch"_ns, gServiceWorkersRegisteredFetch);
300 LOG(("Unregister ServiceWorker: %u, fetch %u\n",
301 gServiceWorkersRegistered, gServiceWorkersRegisteredFetch));
303 mData.RemoveElementAt(i);
304 mDataGeneration = GetNextGeneration();
305 deleted = true;
306 break;
311 if (deleted) {
312 MaybeScheduleSaveData();
313 StorageActivityService::SendActivity(aPrincipalInfo);
317 void ServiceWorkerRegistrar::RemoveAll() {
318 AssertIsOnBackgroundThread();
320 if (mShuttingDown) {
321 NS_WARNING("Failed to remove all the serviceWorkers during shutting down.");
322 return;
325 bool deleted = false;
327 nsTArray<ServiceWorkerRegistrationData> data;
329 MonitorAutoLock lock(mMonitor);
330 MOZ_ASSERT(mDataLoaded);
332 // Let's take a copy in order to inform StorageActivityService.
333 data = mData.Clone();
335 deleted = !mData.IsEmpty();
336 mData.Clear();
338 mDataGeneration = GetNextGeneration();
341 if (!deleted) {
342 return;
345 MaybeScheduleSaveData();
347 for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
348 StorageActivityService::SendActivity(data[i].principal());
352 void ServiceWorkerRegistrar::LoadData() {
353 MOZ_ASSERT(!NS_IsMainThread());
354 #ifdef DEBUG
356 MonitorAutoLock lock(mMonitor);
357 MOZ_ASSERT(!mDataLoaded);
359 #endif
361 nsresult rv = ReadData();
363 if (NS_WARN_IF(NS_FAILED(rv))) {
364 DeleteData();
365 // Also if the reading failed we have to notify what is waiting for data.
368 MonitorAutoLock lock(mMonitor);
369 MOZ_ASSERT(!mDataLoaded);
370 mDataLoaded = true;
371 mMonitor.Notify();
374 bool ServiceWorkerRegistrar::ReloadDataForTest() {
375 if (NS_WARN_IF(!StaticPrefs::dom_serviceWorkers_testing_enabled())) {
376 return false;
379 MOZ_ASSERT(NS_IsMainThread());
380 MonitorAutoLock lock(mMonitor);
381 mData.Clear();
382 mDataLoaded = false;
384 nsCOMPtr<nsIEventTarget> target =
385 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
386 MOZ_ASSERT(target, "Must have stream transport service");
388 nsCOMPtr<nsIRunnable> runnable =
389 NewRunnableMethod("dom::ServiceWorkerRegistrar::LoadData", this,
390 &ServiceWorkerRegistrar::LoadData);
391 nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
392 if (NS_FAILED(rv)) {
393 NS_WARNING("Failed to dispatch the LoadDataRunnable.");
394 return false;
397 mMonitor.AssertCurrentThreadOwns();
398 while (!mDataLoaded) {
399 mMonitor.Wait();
402 return mDataLoaded;
405 nsresult ServiceWorkerRegistrar::ReadData() {
406 // We cannot assert about the correct thread because normally this method
407 // runs on a IO thread, but in gTests we call it from the main-thread.
409 nsCOMPtr<nsIFile> file;
412 MonitorAutoLock lock(mMonitor);
414 if (!mProfileDir) {
415 return NS_ERROR_FAILURE;
418 nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
419 if (NS_WARN_IF(NS_FAILED(rv))) {
420 return rv;
424 nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
425 if (NS_WARN_IF(NS_FAILED(rv))) {
426 return rv;
429 bool exists;
430 rv = file->Exists(&exists);
431 if (NS_WARN_IF(NS_FAILED(rv))) {
432 return rv;
435 if (!exists) {
436 return NS_OK;
439 nsCOMPtr<nsIInputStream> stream;
440 rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
441 if (NS_WARN_IF(NS_FAILED(rv))) {
442 return rv;
445 nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
446 MOZ_ASSERT(lineInputStream);
448 nsAutoCString version;
449 bool hasMoreLines;
450 rv = lineInputStream->ReadLine(version, &hasMoreLines);
451 if (NS_WARN_IF(NS_FAILED(rv))) {
452 return rv;
455 if (!IsSupportedVersion(version)) {
456 nsContentUtils::LogMessageToConsole(
457 nsPrintfCString("Unsupported service worker registrar version: %s",
458 version.get())
459 .get());
460 return NS_ERROR_FAILURE;
463 nsTArray<ServiceWorkerRegistrationData> tmpData;
465 bool overwrite = false;
466 bool dedupe = false;
467 while (hasMoreLines) {
468 ServiceWorkerRegistrationData* entry = tmpData.AppendElement();
470 #define GET_LINE(x) \
471 rv = lineInputStream->ReadLine(x, &hasMoreLines); \
472 if (NS_WARN_IF(NS_FAILED(rv))) { \
473 return rv; \
475 if (NS_WARN_IF(!hasMoreLines)) { \
476 return NS_ERROR_FAILURE; \
479 nsAutoCString line;
480 if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
481 rv = CreatePrincipalInfo(lineInputStream, entry);
482 if (NS_WARN_IF(NS_FAILED(rv))) {
483 return rv;
486 GET_LINE(entry->currentWorkerURL());
488 nsAutoCString fetchFlag;
489 GET_LINE(fetchFlag);
490 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
491 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
492 return NS_ERROR_INVALID_ARG;
494 entry->currentWorkerHandlesFetch() =
495 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
497 nsAutoCString cacheName;
498 GET_LINE(cacheName);
499 CopyUTF8toUTF16(cacheName, entry->cacheName());
501 nsAutoCString updateViaCache;
502 GET_LINE(updateViaCache);
503 entry->updateViaCache() = updateViaCache.ToInteger(&rv, 16);
504 if (NS_WARN_IF(NS_FAILED(rv))) {
505 return rv;
507 if (entry->updateViaCache() >
508 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE) {
509 return NS_ERROR_INVALID_ARG;
512 nsAutoCString installedTimeStr;
513 GET_LINE(installedTimeStr);
514 int64_t installedTime = installedTimeStr.ToInteger64(&rv);
515 if (NS_WARN_IF(NS_FAILED(rv))) {
516 return rv;
518 entry->currentWorkerInstalledTime() = installedTime;
520 nsAutoCString activatedTimeStr;
521 GET_LINE(activatedTimeStr);
522 int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
523 if (NS_WARN_IF(NS_FAILED(rv))) {
524 return rv;
526 entry->currentWorkerActivatedTime() = activatedTime;
528 nsAutoCString lastUpdateTimeStr;
529 GET_LINE(lastUpdateTimeStr);
530 int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
531 if (NS_WARN_IF(NS_FAILED(rv))) {
532 return rv;
534 entry->lastUpdateTime() = lastUpdateTime;
536 nsAutoCString navigationPreloadEnabledStr;
537 GET_LINE(navigationPreloadEnabledStr);
538 bool navigationPreloadEnabled =
539 navigationPreloadEnabledStr.ToInteger(&rv);
540 if (NS_WARN_IF(NS_FAILED(rv))) {
541 return rv;
543 entry->navigationPreloadState().enabled() = navigationPreloadEnabled;
545 GET_LINE(entry->navigationPreloadState().headerValue());
546 } else if (version.EqualsLiteral("8")) {
547 rv = CreatePrincipalInfo(lineInputStream, entry);
548 if (NS_WARN_IF(NS_FAILED(rv))) {
549 return rv;
552 GET_LINE(entry->currentWorkerURL());
554 nsAutoCString fetchFlag;
555 GET_LINE(fetchFlag);
556 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
557 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
558 return NS_ERROR_INVALID_ARG;
560 entry->currentWorkerHandlesFetch() =
561 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
563 nsAutoCString cacheName;
564 GET_LINE(cacheName);
565 CopyUTF8toUTF16(cacheName, entry->cacheName());
567 nsAutoCString updateViaCache;
568 GET_LINE(updateViaCache);
569 entry->updateViaCache() = updateViaCache.ToInteger(&rv, 16);
570 if (NS_WARN_IF(NS_FAILED(rv))) {
571 return rv;
573 if (entry->updateViaCache() >
574 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE) {
575 return NS_ERROR_INVALID_ARG;
578 nsAutoCString installedTimeStr;
579 GET_LINE(installedTimeStr);
580 int64_t installedTime = installedTimeStr.ToInteger64(&rv);
581 if (NS_WARN_IF(NS_FAILED(rv))) {
582 return rv;
584 entry->currentWorkerInstalledTime() = installedTime;
586 nsAutoCString activatedTimeStr;
587 GET_LINE(activatedTimeStr);
588 int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
589 if (NS_WARN_IF(NS_FAILED(rv))) {
590 return rv;
592 entry->currentWorkerActivatedTime() = activatedTime;
594 nsAutoCString lastUpdateTimeStr;
595 GET_LINE(lastUpdateTimeStr);
596 int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
597 if (NS_WARN_IF(NS_FAILED(rv))) {
598 return rv;
600 entry->lastUpdateTime() = lastUpdateTime;
602 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
603 } else if (version.EqualsLiteral("7")) {
604 rv = CreatePrincipalInfo(lineInputStream, entry);
605 if (NS_WARN_IF(NS_FAILED(rv))) {
606 return rv;
609 GET_LINE(entry->currentWorkerURL());
611 nsAutoCString fetchFlag;
612 GET_LINE(fetchFlag);
613 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
614 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
615 return NS_ERROR_INVALID_ARG;
617 entry->currentWorkerHandlesFetch() =
618 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
620 nsAutoCString cacheName;
621 GET_LINE(cacheName);
622 CopyUTF8toUTF16(cacheName, entry->cacheName());
624 nsAutoCString loadFlags;
625 GET_LINE(loadFlags);
626 entry->updateViaCache() =
627 loadFlags.ToInteger(&rv, 16) == nsIRequest::LOAD_NORMAL
628 ? nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL
629 : nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
631 if (NS_WARN_IF(NS_FAILED(rv))) {
632 return rv;
635 nsAutoCString installedTimeStr;
636 GET_LINE(installedTimeStr);
637 int64_t installedTime = installedTimeStr.ToInteger64(&rv);
638 if (NS_WARN_IF(NS_FAILED(rv))) {
639 return rv;
641 entry->currentWorkerInstalledTime() = installedTime;
643 nsAutoCString activatedTimeStr;
644 GET_LINE(activatedTimeStr);
645 int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
646 if (NS_WARN_IF(NS_FAILED(rv))) {
647 return rv;
649 entry->currentWorkerActivatedTime() = activatedTime;
651 nsAutoCString lastUpdateTimeStr;
652 GET_LINE(lastUpdateTimeStr);
653 int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
654 if (NS_WARN_IF(NS_FAILED(rv))) {
655 return rv;
657 entry->lastUpdateTime() = lastUpdateTime;
659 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
660 } else if (version.EqualsLiteral("6")) {
661 rv = CreatePrincipalInfo(lineInputStream, entry);
662 if (NS_WARN_IF(NS_FAILED(rv))) {
663 return rv;
666 GET_LINE(entry->currentWorkerURL());
668 nsAutoCString fetchFlag;
669 GET_LINE(fetchFlag);
670 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
671 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
672 return NS_ERROR_INVALID_ARG;
674 entry->currentWorkerHandlesFetch() =
675 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
677 nsAutoCString cacheName;
678 GET_LINE(cacheName);
679 CopyUTF8toUTF16(cacheName, entry->cacheName());
681 nsAutoCString loadFlags;
682 GET_LINE(loadFlags);
683 entry->updateViaCache() =
684 loadFlags.ToInteger(&rv, 16) == nsIRequest::LOAD_NORMAL
685 ? nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL
686 : nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
688 if (NS_WARN_IF(NS_FAILED(rv))) {
689 return rv;
692 entry->currentWorkerInstalledTime() = 0;
693 entry->currentWorkerActivatedTime() = 0;
694 entry->lastUpdateTime() = 0;
696 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
697 } else if (version.EqualsLiteral("5")) {
698 overwrite = true;
699 dedupe = true;
701 rv = CreatePrincipalInfo(lineInputStream, entry);
702 if (NS_WARN_IF(NS_FAILED(rv))) {
703 return rv;
706 GET_LINE(entry->currentWorkerURL());
708 nsAutoCString fetchFlag;
709 GET_LINE(fetchFlag);
710 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
711 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
712 return NS_ERROR_INVALID_ARG;
714 entry->currentWorkerHandlesFetch() =
715 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
717 nsAutoCString cacheName;
718 GET_LINE(cacheName);
719 CopyUTF8toUTF16(cacheName, entry->cacheName());
721 entry->updateViaCache() =
722 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
724 entry->currentWorkerInstalledTime() = 0;
725 entry->currentWorkerActivatedTime() = 0;
726 entry->lastUpdateTime() = 0;
728 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
729 } else if (version.EqualsLiteral("4")) {
730 overwrite = true;
731 dedupe = true;
733 rv = CreatePrincipalInfo(lineInputStream, entry);
734 if (NS_WARN_IF(NS_FAILED(rv))) {
735 return rv;
738 GET_LINE(entry->currentWorkerURL());
740 // default handlesFetch flag to Enabled
741 entry->currentWorkerHandlesFetch() = true;
743 nsAutoCString cacheName;
744 GET_LINE(cacheName);
745 CopyUTF8toUTF16(cacheName, entry->cacheName());
747 entry->updateViaCache() =
748 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
750 entry->currentWorkerInstalledTime() = 0;
751 entry->currentWorkerActivatedTime() = 0;
752 entry->lastUpdateTime() = 0;
754 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
755 } else if (version.EqualsLiteral("3")) {
756 overwrite = true;
757 dedupe = true;
759 rv = CreatePrincipalInfo(lineInputStream, entry, true);
760 if (NS_WARN_IF(NS_FAILED(rv))) {
761 return rv;
764 GET_LINE(entry->currentWorkerURL());
766 // default handlesFetch flag to Enabled
767 entry->currentWorkerHandlesFetch() = true;
769 nsAutoCString cacheName;
770 GET_LINE(cacheName);
771 CopyUTF8toUTF16(cacheName, entry->cacheName());
773 entry->updateViaCache() =
774 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
776 entry->currentWorkerInstalledTime() = 0;
777 entry->currentWorkerActivatedTime() = 0;
778 entry->lastUpdateTime() = 0;
780 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
781 } else if (version.EqualsLiteral("2")) {
782 overwrite = true;
783 dedupe = true;
785 rv = CreatePrincipalInfo(lineInputStream, entry, true);
786 if (NS_WARN_IF(NS_FAILED(rv))) {
787 return rv;
790 // scriptSpec is no more used in latest version.
791 nsAutoCString unused;
792 GET_LINE(unused);
794 GET_LINE(entry->currentWorkerURL());
796 // default handlesFetch flag to Enabled
797 entry->currentWorkerHandlesFetch() = true;
799 nsAutoCString cacheName;
800 GET_LINE(cacheName);
801 CopyUTF8toUTF16(cacheName, entry->cacheName());
803 // waitingCacheName is no more used in latest version.
804 GET_LINE(unused);
806 entry->updateViaCache() =
807 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
809 entry->currentWorkerInstalledTime() = 0;
810 entry->currentWorkerActivatedTime() = 0;
811 entry->lastUpdateTime() = 0;
813 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
814 } else {
815 MOZ_ASSERT_UNREACHABLE("Should never get here!");
818 #undef GET_LINE
820 rv = lineInputStream->ReadLine(line, &hasMoreLines);
821 if (NS_WARN_IF(NS_FAILED(rv))) {
822 return rv;
825 if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
826 return NS_ERROR_FAILURE;
830 stream->Close();
832 // We currently only call this at startup where we block the main thread
833 // preventing further operation until it completes, however take the lock
834 // in case that changes
837 MonitorAutoLock lock(mMonitor);
838 // Copy data over to mData.
839 for (uint32_t i = 0; i < tmpData.Length(); ++i) {
840 // Older versions could sometimes write out empty, useless entries.
841 // Prune those here.
842 if (!ServiceWorkerRegistrationDataIsValid(tmpData[i])) {
843 continue;
846 bool match = false;
847 if (dedupe) {
848 MOZ_ASSERT(overwrite);
849 // If this is an old profile, then we might need to deduplicate. In
850 // theory this can be removed in the future (Bug 1248449)
851 for (uint32_t j = 0; j < mData.Length(); ++j) {
852 // Use same comparison as RegisterServiceWorker. Scope contains
853 // basic origin information. Combine with any principal attributes.
854 if (Equivalent(tmpData[i], mData[j])) {
855 // Last match wins, just like legacy loading used to do in
856 // the ServiceWorkerManager.
857 mData[j] = tmpData[i];
858 // Dupe found, so overwrite file with reduced list.
859 match = true;
860 break;
863 } else {
864 #ifdef DEBUG
865 // Otherwise assert no duplications in debug builds.
866 for (uint32_t j = 0; j < mData.Length(); ++j) {
867 MOZ_ASSERT(!Equivalent(tmpData[i], mData[j]));
869 #endif
871 if (!match) {
872 mData.AppendElement(tmpData[i]);
876 // Overwrite previous version.
877 // Cannot call SaveData directly because gtest uses main-thread.
879 // XXX NOTE: if we could be accessed multi-threaded here, we would need to
880 // find a way to lock around access to mData. Since we can't, suppress the
881 // thread-safety warnings.
882 MOZ_PUSH_IGNORE_THREAD_SAFETY
883 if (overwrite && NS_FAILED(WriteData(mData))) {
884 NS_WARNING("Failed to write data for the ServiceWorker Registations.");
885 DeleteData();
887 MOZ_POP_THREAD_SAFETY
889 return NS_OK;
892 void ServiceWorkerRegistrar::DeleteData() {
893 // We cannot assert about the correct thread because normally this method
894 // runs on a IO thread, but in gTests we call it from the main-thread.
896 nsCOMPtr<nsIFile> file;
899 MonitorAutoLock lock(mMonitor);
900 mData.Clear();
902 if (!mProfileDir) {
903 return;
906 nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
907 if (NS_WARN_IF(NS_FAILED(rv))) {
908 return;
912 nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
913 if (NS_WARN_IF(NS_FAILED(rv))) {
914 return;
917 rv = file->Remove(false);
918 if (rv == NS_ERROR_FILE_NOT_FOUND) {
919 return;
922 if (NS_WARN_IF(NS_FAILED(rv))) {
923 return;
927 void ServiceWorkerRegistrar::RegisterServiceWorkerInternal(
928 const ServiceWorkerRegistrationData& aData) {
929 bool found = false;
930 for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
931 if (Equivalent(aData, mData[i])) {
932 found = true;
933 if (mData[i].currentWorkerHandlesFetch()) {
934 // Decrement here if we found it, in case the new registration no
935 // longer handles Fetch. If it continues to handle fetch, we'll
936 // bump it back later.
937 gServiceWorkersRegisteredFetch--;
939 mData[i] = aData;
940 break;
944 if (!found) {
945 MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData));
946 mData.AppendElement(aData);
947 // We didn't find an entry to update, so we have 1 more
948 gServiceWorkersRegistered++;
950 // Handles bumping both for new registrations and updates
951 if (aData.currentWorkerHandlesFetch()) {
952 gServiceWorkersRegisteredFetch++;
954 // Update Telemetry
955 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_REGISTRATIONS,
956 u"All"_ns, gServiceWorkersRegistered);
957 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_REGISTRATIONS,
958 u"Fetch"_ns, gServiceWorkersRegisteredFetch);
959 LOG(("Register: %u, fetch %u\n", gServiceWorkersRegistered,
960 gServiceWorkersRegisteredFetch));
962 mDataGeneration = GetNextGeneration();
965 class ServiceWorkerRegistrarSaveDataRunnable final : public Runnable {
966 nsCOMPtr<nsIEventTarget> mEventTarget;
967 const nsTArray<ServiceWorkerRegistrationData> mData;
968 const uint32_t mGeneration;
970 public:
971 ServiceWorkerRegistrarSaveDataRunnable(
972 nsTArray<ServiceWorkerRegistrationData>&& aData, uint32_t aGeneration)
973 : Runnable("dom::ServiceWorkerRegistrarSaveDataRunnable"),
974 mEventTarget(GetCurrentSerialEventTarget()),
975 mData(std::move(aData)),
976 mGeneration(aGeneration) {
977 AssertIsOnBackgroundThread();
978 MOZ_DIAGNOSTIC_ASSERT(mGeneration != kInvalidGeneration);
981 NS_IMETHOD
982 Run() override {
983 RefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
984 MOZ_ASSERT(service);
986 uint32_t fileGeneration = kInvalidGeneration;
988 if (NS_SUCCEEDED(service->SaveData(mData))) {
989 fileGeneration = mGeneration;
992 RefPtr<Runnable> runnable = NewRunnableMethod<uint32_t>(
993 "ServiceWorkerRegistrar::DataSaved", service,
994 &ServiceWorkerRegistrar::DataSaved, fileGeneration);
995 MOZ_ALWAYS_SUCCEEDS(
996 mEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
998 return NS_OK;
1002 void ServiceWorkerRegistrar::MaybeScheduleSaveData() {
1003 AssertIsOnBackgroundThread();
1004 MOZ_ASSERT(!mShuttingDown);
1006 if (mShuttingDown || mSaveDataRunnableDispatched ||
1007 mDataGeneration <= mFileGeneration) {
1008 return;
1011 nsCOMPtr<nsIEventTarget> target =
1012 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
1013 MOZ_ASSERT(target, "Must have stream transport service");
1015 uint32_t generation = kInvalidGeneration;
1016 nsTArray<ServiceWorkerRegistrationData> data;
1019 MonitorAutoLock lock(mMonitor);
1020 generation = mDataGeneration;
1021 data.AppendElements(mData);
1024 RefPtr<Runnable> runnable =
1025 new ServiceWorkerRegistrarSaveDataRunnable(std::move(data), generation);
1026 nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1027 NS_ENSURE_SUCCESS_VOID(rv);
1029 mSaveDataRunnableDispatched = true;
1032 void ServiceWorkerRegistrar::ShutdownCompleted() {
1033 MOZ_ASSERT(NS_IsMainThread());
1035 DebugOnly<nsresult> rv = GetShutdownPhase()->RemoveBlocker(this);
1036 MOZ_ASSERT(NS_SUCCEEDED(rv));
1039 nsresult ServiceWorkerRegistrar::SaveData(
1040 const nsTArray<ServiceWorkerRegistrationData>& aData) {
1041 MOZ_ASSERT(!NS_IsMainThread());
1043 nsresult rv = WriteData(aData);
1044 if (NS_FAILED(rv)) {
1045 NS_WARNING("Failed to write data for the ServiceWorker Registations.");
1046 // Don't touch the file or in-memory state. Writing files can
1047 // sometimes fail due to virus scanning, etc. We should just leave
1048 // things as is so the next save operation can pick up any changes
1049 // without losing data.
1051 return rv;
1054 void ServiceWorkerRegistrar::DataSaved(uint32_t aFileGeneration) {
1055 AssertIsOnBackgroundThread();
1056 MOZ_ASSERT(mSaveDataRunnableDispatched);
1058 mSaveDataRunnableDispatched = false;
1060 // Check for shutdown before possibly triggering any more saves
1061 // runnables.
1062 MaybeScheduleShutdownCompleted();
1063 if (mShuttingDown) {
1064 return;
1067 // If we got a valid generation, then the save was successful.
1068 if (aFileGeneration != kInvalidGeneration) {
1069 // Update the file generation. We also check to see if we
1070 // can reset the generation back to zero if the file and data
1071 // are now in sync. This allows us to avoid dealing with wrap
1072 // around of the generation count.
1073 mFileGeneration = aFileGeneration;
1074 MaybeResetGeneration();
1076 // Successful write resets the retry count.
1077 mRetryCount = 0;
1079 // Possibly schedule another save operation if more data
1080 // has come in while processing this one.
1081 MaybeScheduleSaveData();
1083 return;
1086 // Otherwise, the save failed since the generation is invalid. We
1087 // want to retry the save, but only a limited number of times.
1088 static const uint32_t kMaxRetryCount = 2;
1089 if (mRetryCount >= kMaxRetryCount) {
1090 return;
1093 mRetryCount += 1;
1094 MaybeScheduleSaveData();
1097 void ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted() {
1098 AssertIsOnBackgroundThread();
1100 if (mSaveDataRunnableDispatched || !mShuttingDown) {
1101 return;
1104 RefPtr<Runnable> runnable =
1105 NewRunnableMethod("dom::ServiceWorkerRegistrar::ShutdownCompleted", this,
1106 &ServiceWorkerRegistrar::ShutdownCompleted);
1107 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
1110 uint32_t ServiceWorkerRegistrar::GetNextGeneration() {
1111 uint32_t ret = mDataGeneration + 1;
1112 if (ret == kInvalidGeneration) {
1113 ret += 1;
1115 return ret;
1118 void ServiceWorkerRegistrar::MaybeResetGeneration() {
1119 if (mDataGeneration != mFileGeneration) {
1120 return;
1122 mDataGeneration = mFileGeneration = 0;
1125 bool ServiceWorkerRegistrar::IsSupportedVersion(
1126 const nsACString& aVersion) const {
1127 uint32_t numVersions = ArrayLength(gSupportedRegistrarVersions);
1128 for (uint32_t i = 0; i < numVersions; i++) {
1129 if (aVersion.EqualsASCII(gSupportedRegistrarVersions[i])) {
1130 return true;
1133 return false;
1136 nsresult ServiceWorkerRegistrar::WriteData(
1137 const nsTArray<ServiceWorkerRegistrationData>& aData) {
1138 // We cannot assert about the correct thread because normally this method
1139 // runs on a IO thread, but in gTests we call it from the main-thread.
1141 nsCOMPtr<nsIFile> file;
1144 MonitorAutoLock lock(mMonitor);
1146 if (!mProfileDir) {
1147 return NS_ERROR_FAILURE;
1150 nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
1151 if (NS_WARN_IF(NS_FAILED(rv))) {
1152 return rv;
1156 nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
1157 if (NS_WARN_IF(NS_FAILED(rv))) {
1158 return rv;
1161 nsCOMPtr<nsIOutputStream> stream;
1162 rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
1163 if (NS_WARN_IF(NS_FAILED(rv))) {
1164 return rv;
1167 nsAutoCString buffer;
1168 buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
1169 buffer.Append('\n');
1171 uint32_t count;
1172 rv = stream->Write(buffer.Data(), buffer.Length(), &count);
1173 if (NS_WARN_IF(NS_FAILED(rv))) {
1174 return rv;
1177 if (count != buffer.Length()) {
1178 return NS_ERROR_UNEXPECTED;
1181 for (uint32_t i = 0, len = aData.Length(); i < len; ++i) {
1182 // We have an assertion further up the stack, but as a last
1183 // resort avoid writing out broken entries here.
1184 if (!ServiceWorkerRegistrationDataIsValid(aData[i])) {
1185 continue;
1188 const mozilla::ipc::PrincipalInfo& info = aData[i].principal();
1190 MOZ_ASSERT(info.type() ==
1191 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
1193 const mozilla::ipc::ContentPrincipalInfo& cInfo =
1194 info.get_ContentPrincipalInfo();
1196 nsAutoCString suffix;
1197 cInfo.attrs().CreateSuffix(suffix);
1199 buffer.Truncate();
1200 buffer.Append(suffix.get());
1201 buffer.Append('\n');
1203 buffer.Append(aData[i].scope());
1204 buffer.Append('\n');
1206 buffer.Append(aData[i].currentWorkerURL());
1207 buffer.Append('\n');
1209 buffer.Append(aData[i].currentWorkerHandlesFetch()
1210 ? SERVICEWORKERREGISTRAR_TRUE
1211 : SERVICEWORKERREGISTRAR_FALSE);
1212 buffer.Append('\n');
1214 buffer.Append(NS_ConvertUTF16toUTF8(aData[i].cacheName()));
1215 buffer.Append('\n');
1217 buffer.AppendInt(aData[i].updateViaCache(), 16);
1218 buffer.Append('\n');
1219 MOZ_DIAGNOSTIC_ASSERT(
1220 aData[i].updateViaCache() ==
1221 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS ||
1222 aData[i].updateViaCache() ==
1223 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL ||
1224 aData[i].updateViaCache() ==
1225 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE);
1227 static_assert(nsIRequest::LOAD_NORMAL == 0,
1228 "LOAD_NORMAL matches serialized value.");
1229 static_assert(nsIRequest::VALIDATE_ALWAYS == (1 << 11),
1230 "VALIDATE_ALWAYS matches serialized value");
1232 buffer.AppendInt(aData[i].currentWorkerInstalledTime());
1233 buffer.Append('\n');
1235 buffer.AppendInt(aData[i].currentWorkerActivatedTime());
1236 buffer.Append('\n');
1238 buffer.AppendInt(aData[i].lastUpdateTime());
1239 buffer.Append('\n');
1241 buffer.AppendInt(
1242 static_cast<int32_t>(aData[i].navigationPreloadState().enabled()));
1243 buffer.Append('\n');
1245 buffer.Append(aData[i].navigationPreloadState().headerValue());
1246 buffer.Append('\n');
1248 buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
1249 buffer.Append('\n');
1251 rv = stream->Write(buffer.Data(), buffer.Length(), &count);
1252 if (NS_WARN_IF(NS_FAILED(rv))) {
1253 return rv;
1256 if (count != buffer.Length()) {
1257 return NS_ERROR_UNEXPECTED;
1261 nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
1262 MOZ_ASSERT(safeStream);
1264 rv = safeStream->Finish();
1265 if (NS_WARN_IF(NS_FAILED(rv))) {
1266 return rv;
1269 return NS_OK;
1272 void ServiceWorkerRegistrar::ProfileStarted() {
1273 MOZ_ASSERT(NS_IsMainThread());
1275 MonitorAutoLock lock(mMonitor);
1276 MOZ_DIAGNOSTIC_ASSERT(!mProfileDir);
1278 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1279 getter_AddRefs(mProfileDir));
1280 if (NS_WARN_IF(NS_FAILED(rv))) {
1281 return;
1284 nsAutoString blockerName;
1285 MOZ_ALWAYS_SUCCEEDS(GetName(blockerName));
1287 rv = GetShutdownPhase()->AddBlocker(
1288 this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, blockerName);
1289 if (NS_WARN_IF(NS_FAILED(rv))) {
1290 return;
1293 nsCOMPtr<nsIEventTarget> target =
1294 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
1295 MOZ_ASSERT(target, "Must have stream transport service");
1297 nsCOMPtr<nsIRunnable> runnable =
1298 NewRunnableMethod("dom::ServiceWorkerRegistrar::LoadData", this,
1299 &ServiceWorkerRegistrar::LoadData);
1300 rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1301 if (NS_FAILED(rv)) {
1302 NS_WARNING("Failed to dispatch the LoadDataRunnable.");
1306 void ServiceWorkerRegistrar::ProfileStopped() {
1307 MOZ_ASSERT(NS_IsMainThread());
1309 MonitorAutoLock lock(mMonitor);
1311 if (!mProfileDir) {
1312 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1313 getter_AddRefs(mProfileDir));
1314 if (NS_WARN_IF(NS_FAILED(rv))) {
1315 // If we do not have a profile directory, we are somehow screwed.
1316 MOZ_DIAGNOSTIC_ASSERT(
1317 false,
1318 "NS_GetSpecialDirectory for NS_APP_USER_PROFILE_50_DIR failed!");
1322 // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
1323 // issued by the ServiceWorkerManagerService, so the appropriate place to
1324 // trigger shutdown is on that thread.
1326 // However, it's quite possible that the PBackground thread was not brought
1327 // into existence for xpcshell tests. We don't cause it to be created
1328 // ourselves for any reason, for example.
1330 // In this scenario, we know that:
1331 // - We will receive exactly one call to ourself from BlockShutdown() and
1332 // BlockShutdown() will be called (at most) once.
1333 // - The only way our Shutdown() method gets called is via
1334 // BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
1335 // invoked, which only happens if we get to that send below here that we
1336 // can't get to.
1337 // - All Shutdown() does is set mShuttingDown=true (essential for
1338 // invariants) and invoke MaybeScheduleShutdownCompleted().
1339 // - Since there is no PBackground thread, mSaveDataRunnableDispatched must
1340 // be false because only MaybeScheduleSaveData() set it and it only runs
1341 // on the background thread, so it cannot have run. And so we would
1342 // expect MaybeScheduleShutdownCompleted() to schedule an invocation of
1343 // ShutdownCompleted on the main thread.
1344 PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
1345 if (mProfileDir && child) {
1346 if (child->SendShutdownServiceWorkerRegistrar()) {
1347 // Normal shutdown sequence has been initiated, go home.
1348 return;
1350 // If we get here, the PBackground thread has probably gone nuts and we
1351 // want to know it.
1352 MOZ_DIAGNOSTIC_ASSERT(
1353 false, "Unable to send the ShutdownServiceWorkerRegistrar message.");
1356 // On any error it's appropriate to set mShuttingDown=true (as Shutdown
1357 // would do) and directly invoke ShutdownCompleted() (as Shutdown would
1358 // indirectly do via MaybeScheduleShutdownCompleted) in order to unblock
1359 // shutdown.
1360 mShuttingDown = true;
1361 ShutdownCompleted();
1364 // Async shutdown blocker methods
1366 NS_IMETHODIMP
1367 ServiceWorkerRegistrar::BlockShutdown(nsIAsyncShutdownClient* aClient) {
1368 ProfileStopped();
1369 return NS_OK;
1372 NS_IMETHODIMP
1373 ServiceWorkerRegistrar::GetName(nsAString& aName) {
1374 aName = u"ServiceWorkerRegistrar: Flushing data"_ns;
1375 return NS_OK;
1378 NS_IMETHODIMP
1379 ServiceWorkerRegistrar::GetState(nsIPropertyBag** aBagOut) {
1380 nsCOMPtr<nsIWritablePropertyBag2> propertyBag =
1381 do_CreateInstance("@mozilla.org/hash-property-bag;1");
1383 MOZ_TRY(propertyBag->SetPropertyAsBool(u"shuttingDown"_ns, mShuttingDown));
1385 MOZ_TRY(propertyBag->SetPropertyAsBool(u"saveDataRunnableDispatched"_ns,
1386 mSaveDataRunnableDispatched));
1388 propertyBag.forget(aBagOut);
1390 return NS_OK;
1393 #define RELEASE_ASSERT_SUCCEEDED(rv, name) \
1394 do { \
1395 if (NS_FAILED(rv)) { \
1396 if (rv == NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS) { \
1397 if (auto* context = CycleCollectedJSContext::Get()) { \
1398 if (RefPtr<Exception> exn = context->GetPendingException()) { \
1399 MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s", \
1400 exn->GetMessageMoz().get()); \
1405 nsAutoCString errorName; \
1406 GetErrorName(rv, errorName); \
1407 MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s", errorName.get()); \
1409 } while (0)
1411 nsCOMPtr<nsIAsyncShutdownClient> ServiceWorkerRegistrar::GetShutdownPhase()
1412 const {
1413 nsresult rv;
1414 nsCOMPtr<nsIAsyncShutdownService> svc =
1415 do_GetService("@mozilla.org/async-shutdown-service;1", &rv);
1416 // If this fails, something is very wrong on the JS side (or we're out of
1417 // memory), and there's no point in continuing startup. Include as much
1418 // information as possible in the crash report.
1419 RELEASE_ASSERT_SUCCEEDED(rv, "async shutdown service");
1421 nsCOMPtr<nsIAsyncShutdownClient> client;
1422 rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
1423 RELEASE_ASSERT_SUCCEEDED(rv, "profileBeforeChange shutdown blocker");
1424 return client;
1427 #undef RELEASE_ASSERT_SUCCEEDED
1429 void ServiceWorkerRegistrar::Shutdown() {
1430 AssertIsOnBackgroundThread();
1431 MOZ_ASSERT(!mShuttingDown);
1433 mShuttingDown = true;
1434 MaybeScheduleShutdownCompleted();
1437 NS_IMETHODIMP
1438 ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
1439 const char16_t* aData) {
1440 MOZ_ASSERT(NS_IsMainThread());
1442 if (!strcmp(aTopic, "profile-after-change")) {
1443 nsCOMPtr<nsIObserverService> observerService =
1444 services::GetObserverService();
1445 observerService->RemoveObserver(this, "profile-after-change");
1447 // The profile is fully loaded, now we can proceed with the loading of data
1448 // from disk.
1449 ProfileStarted();
1451 return NS_OK;
1454 MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
1455 return NS_ERROR_UNEXPECTED;
1458 } // namespace mozilla::dom