Bug 1893067 - Add actions key to glean urlbar metrics. r=mak,urlbar-reviewers
[gecko.git] / dom / serviceworkers / ServiceWorkerRegistrar.cpp
blob923c217b789bf27179cdf924e96f6997d8791db6
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/StaticPrefs_dom.h"
13 #include "nsIEventTarget.h"
14 #include "nsIInputStream.h"
15 #include "nsILineInputStream.h"
16 #include "nsIObserverService.h"
17 #include "nsIOutputStream.h"
18 #include "nsISafeOutputStream.h"
19 #include "nsIServiceWorkerManager.h"
20 #include "nsIURI.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 nsCOMPtr<nsIURI> url;
70 nsresult rv = NS_NewURI(getter_AddRefs(url), aURL);
71 if (NS_WARN_IF(NS_FAILED(rv))) {
72 return rv;
75 OriginAttributes attrs;
76 nsCOMPtr<nsIPrincipal> principal =
77 BasePrincipal::CreateContentPrincipal(url, attrs);
78 if (!principal) {
79 return NS_ERROR_NULL_POINTER;
82 rv = principal->GetOriginNoSuffix(aOrigin);
83 if (NS_WARN_IF(NS_FAILED(rv))) {
84 return rv;
87 rv = principal->GetBaseDomain(aBaseDomain);
88 if (NS_WARN_IF(NS_FAILED(rv))) {
89 return rv;
92 return NS_OK;
95 nsresult ReadLine(nsILineInputStream* aStream, nsACString& aValue) {
96 bool hasMoreLines;
97 nsresult rv = aStream->ReadLine(aValue, &hasMoreLines);
98 if (NS_WARN_IF(NS_FAILED(rv))) {
99 return rv;
102 if (NS_WARN_IF(!hasMoreLines)) {
103 return NS_ERROR_FAILURE;
106 return NS_OK;
109 nsresult CreatePrincipalInfo(nsILineInputStream* aStream,
110 ServiceWorkerRegistrationData* aEntry,
111 bool aSkipSpec = false) {
112 nsAutoCString suffix;
113 nsresult rv = ReadLine(aStream, suffix);
114 if (NS_WARN_IF(NS_FAILED(rv))) {
115 return rv;
118 OriginAttributes attrs;
119 if (!attrs.PopulateFromSuffix(suffix)) {
120 return NS_ERROR_INVALID_ARG;
123 if (aSkipSpec) {
124 nsAutoCString unused;
125 nsresult rv = ReadLine(aStream, unused);
126 if (NS_WARN_IF(NS_FAILED(rv))) {
127 return rv;
131 rv = ReadLine(aStream, aEntry->scope());
132 if (NS_WARN_IF(NS_FAILED(rv))) {
133 return rv;
136 nsCString origin;
137 nsCString baseDomain;
138 rv = GetOriginAndBaseDomain(aEntry->scope(), origin, baseDomain);
139 if (NS_WARN_IF(NS_FAILED(rv))) {
140 return rv;
143 aEntry->principal() = mozilla::ipc::ContentPrincipalInfo(
144 attrs, origin, aEntry->scope(), Nothing(), baseDomain);
146 return NS_OK;
149 const IPCNavigationPreloadState gDefaultNavigationPreloadState(false,
150 "true"_ns);
152 } // namespace
154 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar, nsIObserver, nsIAsyncShutdownBlocker)
156 void ServiceWorkerRegistrar::Initialize() {
157 MOZ_ASSERT(!gServiceWorkerRegistrar);
159 if (!XRE_IsParentProcess()) {
160 return;
163 gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
164 ClearOnShutdown(&gServiceWorkerRegistrar);
166 nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
167 if (obs) {
168 DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
169 "profile-after-change", false);
170 MOZ_ASSERT(NS_SUCCEEDED(rv));
174 /* static */
175 already_AddRefed<ServiceWorkerRegistrar> ServiceWorkerRegistrar::Get() {
176 MOZ_ASSERT(XRE_IsParentProcess());
178 MOZ_ASSERT(gServiceWorkerRegistrar);
179 RefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
180 return service.forget();
183 ServiceWorkerRegistrar::ServiceWorkerRegistrar()
184 : mMonitor("ServiceWorkerRegistrar.mMonitor"),
185 mDataLoaded(false),
186 mDataGeneration(kInvalidGeneration),
187 mFileGeneration(kInvalidGeneration),
188 mRetryCount(0),
189 mShuttingDown(false),
190 mSaveDataRunnableDispatched(false) {
191 MOZ_ASSERT(NS_IsMainThread());
194 ServiceWorkerRegistrar::~ServiceWorkerRegistrar() {
195 MOZ_ASSERT(!mSaveDataRunnableDispatched);
198 void ServiceWorkerRegistrar::GetRegistrations(
199 nsTArray<ServiceWorkerRegistrationData>& aValues) {
200 MOZ_ASSERT(NS_IsMainThread());
201 MOZ_ASSERT(aValues.IsEmpty());
203 MonitorAutoLock lock(mMonitor);
205 // If we don't have the profile directory, profile is not started yet (and
206 // probably we are in a utest).
207 if (!mProfileDir) {
208 return;
211 // We care just about the first execution because this can be blocked by
212 // loading data from disk.
213 static bool firstTime = true;
214 TimeStamp startTime;
216 if (firstTime) {
217 startTime = TimeStamp::NowLoRes();
220 // Waiting for data loaded.
221 mMonitor.AssertCurrentThreadOwns();
222 while (!mDataLoaded) {
223 mMonitor.Wait();
226 aValues.AppendElements(mData);
228 MaybeResetGeneration();
229 MOZ_DIAGNOSTIC_ASSERT(mDataGeneration != kInvalidGeneration);
230 MOZ_DIAGNOSTIC_ASSERT(mFileGeneration != kInvalidGeneration);
232 if (firstTime) {
233 firstTime = false;
234 Telemetry::AccumulateTimeDelta(
235 Telemetry::SERVICE_WORKER_REGISTRATION_LOADING, startTime);
239 namespace {
241 bool Equivalent(const ServiceWorkerRegistrationData& aLeft,
242 const ServiceWorkerRegistrationData& aRight) {
243 MOZ_ASSERT(aLeft.principal().type() ==
244 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
245 MOZ_ASSERT(aRight.principal().type() ==
246 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
248 const auto& leftPrincipal = aLeft.principal().get_ContentPrincipalInfo();
249 const auto& rightPrincipal = aRight.principal().get_ContentPrincipalInfo();
251 // Only compare the attributes, not the spec part of the principal.
252 // The scope comparison above already covers the origin and codebase
253 // principals include the full path in their spec which is not what
254 // we want here.
255 return aLeft.scope() == aRight.scope() &&
256 leftPrincipal.attrs() == rightPrincipal.attrs();
259 } // anonymous namespace
261 void ServiceWorkerRegistrar::RegisterServiceWorker(
262 const ServiceWorkerRegistrationData& aData) {
263 AssertIsOnBackgroundThread();
265 if (mShuttingDown) {
266 NS_WARNING("Failed to register a serviceWorker during shutting down.");
267 return;
271 MonitorAutoLock lock(mMonitor);
272 MOZ_ASSERT(mDataLoaded);
273 RegisterServiceWorkerInternal(aData);
276 MaybeScheduleSaveData();
277 StorageActivityService::SendActivity(aData.principal());
280 void ServiceWorkerRegistrar::UnregisterServiceWorker(
281 const PrincipalInfo& aPrincipalInfo, const nsACString& aScope) {
282 AssertIsOnBackgroundThread();
284 if (mShuttingDown) {
285 NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
286 return;
289 bool deleted = false;
292 MonitorAutoLock lock(mMonitor);
293 MOZ_ASSERT(mDataLoaded);
295 ServiceWorkerRegistrationData tmp;
296 tmp.principal() = aPrincipalInfo;
297 tmp.scope() = aScope;
299 for (uint32_t i = 0; i < mData.Length(); ++i) {
300 if (Equivalent(tmp, mData[i])) {
301 gServiceWorkersRegistered--;
302 if (mData[i].currentWorkerHandlesFetch()) {
303 gServiceWorkersRegisteredFetch--;
305 // Update Telemetry
306 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_REGISTRATIONS,
307 u"All"_ns, gServiceWorkersRegistered);
308 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_REGISTRATIONS,
309 u"Fetch"_ns, gServiceWorkersRegisteredFetch);
310 LOG(("Unregister ServiceWorker: %u, fetch %u\n",
311 gServiceWorkersRegistered, gServiceWorkersRegisteredFetch));
313 mData.RemoveElementAt(i);
314 mDataGeneration = GetNextGeneration();
315 deleted = true;
316 break;
321 if (deleted) {
322 MaybeScheduleSaveData();
323 StorageActivityService::SendActivity(aPrincipalInfo);
327 void ServiceWorkerRegistrar::RemoveAll() {
328 AssertIsOnBackgroundThread();
330 if (mShuttingDown) {
331 NS_WARNING("Failed to remove all the serviceWorkers during shutting down.");
332 return;
335 bool deleted = false;
337 nsTArray<ServiceWorkerRegistrationData> data;
339 MonitorAutoLock lock(mMonitor);
340 MOZ_ASSERT(mDataLoaded);
342 // Let's take a copy in order to inform StorageActivityService.
343 data = mData.Clone();
345 deleted = !mData.IsEmpty();
346 mData.Clear();
348 mDataGeneration = GetNextGeneration();
351 if (!deleted) {
352 return;
355 MaybeScheduleSaveData();
357 for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
358 StorageActivityService::SendActivity(data[i].principal());
362 void ServiceWorkerRegistrar::LoadData() {
363 MOZ_ASSERT(!NS_IsMainThread());
364 #ifdef DEBUG
366 MonitorAutoLock lock(mMonitor);
367 MOZ_ASSERT(!mDataLoaded);
369 #endif
371 nsresult rv = ReadData();
373 if (NS_WARN_IF(NS_FAILED(rv))) {
374 DeleteData();
375 // Also if the reading failed we have to notify what is waiting for data.
378 MonitorAutoLock lock(mMonitor);
379 MOZ_ASSERT(!mDataLoaded);
380 mDataLoaded = true;
381 mMonitor.Notify();
384 bool ServiceWorkerRegistrar::ReloadDataForTest() {
385 if (NS_WARN_IF(!StaticPrefs::dom_serviceWorkers_testing_enabled())) {
386 return false;
389 MOZ_ASSERT(NS_IsMainThread());
390 MonitorAutoLock lock(mMonitor);
391 mData.Clear();
392 mDataLoaded = false;
394 nsCOMPtr<nsIEventTarget> target =
395 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
396 MOZ_ASSERT(target, "Must have stream transport service");
398 nsCOMPtr<nsIRunnable> runnable =
399 NewRunnableMethod("dom::ServiceWorkerRegistrar::LoadData", this,
400 &ServiceWorkerRegistrar::LoadData);
401 nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
402 if (NS_FAILED(rv)) {
403 NS_WARNING("Failed to dispatch the LoadDataRunnable.");
404 return false;
407 mMonitor.AssertCurrentThreadOwns();
408 while (!mDataLoaded) {
409 mMonitor.Wait();
412 return mDataLoaded;
415 nsresult ServiceWorkerRegistrar::ReadData() {
416 // We cannot assert about the correct thread because normally this method
417 // runs on a IO thread, but in gTests we call it from the main-thread.
419 nsCOMPtr<nsIFile> file;
422 MonitorAutoLock lock(mMonitor);
424 if (!mProfileDir) {
425 return NS_ERROR_FAILURE;
428 nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
429 if (NS_WARN_IF(NS_FAILED(rv))) {
430 return rv;
434 nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
435 if (NS_WARN_IF(NS_FAILED(rv))) {
436 return rv;
439 bool exists;
440 rv = file->Exists(&exists);
441 if (NS_WARN_IF(NS_FAILED(rv))) {
442 return rv;
445 if (!exists) {
446 return NS_OK;
449 nsCOMPtr<nsIInputStream> stream;
450 rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
451 if (NS_WARN_IF(NS_FAILED(rv))) {
452 return rv;
455 nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
456 MOZ_ASSERT(lineInputStream);
458 nsAutoCString version;
459 bool hasMoreLines;
460 rv = lineInputStream->ReadLine(version, &hasMoreLines);
461 if (NS_WARN_IF(NS_FAILED(rv))) {
462 return rv;
465 if (!IsSupportedVersion(version)) {
466 nsContentUtils::LogMessageToConsole(
467 nsPrintfCString("Unsupported service worker registrar version: %s",
468 version.get())
469 .get());
470 return NS_ERROR_FAILURE;
473 nsTArray<ServiceWorkerRegistrationData> tmpData;
475 bool overwrite = false;
476 bool dedupe = false;
477 while (hasMoreLines) {
478 ServiceWorkerRegistrationData* entry = tmpData.AppendElement();
480 #define GET_LINE(x) \
481 rv = lineInputStream->ReadLine(x, &hasMoreLines); \
482 if (NS_WARN_IF(NS_FAILED(rv))) { \
483 return rv; \
485 if (NS_WARN_IF(!hasMoreLines)) { \
486 return NS_ERROR_FAILURE; \
489 nsAutoCString line;
490 if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
491 rv = CreatePrincipalInfo(lineInputStream, entry);
492 if (NS_WARN_IF(NS_FAILED(rv))) {
493 return rv;
496 GET_LINE(entry->currentWorkerURL());
498 nsAutoCString fetchFlag;
499 GET_LINE(fetchFlag);
500 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
501 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
502 return NS_ERROR_INVALID_ARG;
504 entry->currentWorkerHandlesFetch() =
505 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
507 nsAutoCString cacheName;
508 GET_LINE(cacheName);
509 CopyUTF8toUTF16(cacheName, entry->cacheName());
511 nsAutoCString updateViaCache;
512 GET_LINE(updateViaCache);
513 entry->updateViaCache() = updateViaCache.ToInteger(&rv, 16);
514 if (NS_WARN_IF(NS_FAILED(rv))) {
515 return rv;
517 if (entry->updateViaCache() >
518 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE) {
519 return NS_ERROR_INVALID_ARG;
522 nsAutoCString installedTimeStr;
523 GET_LINE(installedTimeStr);
524 int64_t installedTime = installedTimeStr.ToInteger64(&rv);
525 if (NS_WARN_IF(NS_FAILED(rv))) {
526 return rv;
528 entry->currentWorkerInstalledTime() = installedTime;
530 nsAutoCString activatedTimeStr;
531 GET_LINE(activatedTimeStr);
532 int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
533 if (NS_WARN_IF(NS_FAILED(rv))) {
534 return rv;
536 entry->currentWorkerActivatedTime() = activatedTime;
538 nsAutoCString lastUpdateTimeStr;
539 GET_LINE(lastUpdateTimeStr);
540 int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
541 if (NS_WARN_IF(NS_FAILED(rv))) {
542 return rv;
544 entry->lastUpdateTime() = lastUpdateTime;
546 nsAutoCString navigationPreloadEnabledStr;
547 GET_LINE(navigationPreloadEnabledStr);
548 bool navigationPreloadEnabled =
549 navigationPreloadEnabledStr.ToInteger(&rv);
550 if (NS_WARN_IF(NS_FAILED(rv))) {
551 return rv;
553 entry->navigationPreloadState().enabled() = navigationPreloadEnabled;
555 GET_LINE(entry->navigationPreloadState().headerValue());
556 } else if (version.EqualsLiteral("8")) {
557 rv = CreatePrincipalInfo(lineInputStream, entry);
558 if (NS_WARN_IF(NS_FAILED(rv))) {
559 return rv;
562 GET_LINE(entry->currentWorkerURL());
564 nsAutoCString fetchFlag;
565 GET_LINE(fetchFlag);
566 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
567 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
568 return NS_ERROR_INVALID_ARG;
570 entry->currentWorkerHandlesFetch() =
571 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
573 nsAutoCString cacheName;
574 GET_LINE(cacheName);
575 CopyUTF8toUTF16(cacheName, entry->cacheName());
577 nsAutoCString updateViaCache;
578 GET_LINE(updateViaCache);
579 entry->updateViaCache() = updateViaCache.ToInteger(&rv, 16);
580 if (NS_WARN_IF(NS_FAILED(rv))) {
581 return rv;
583 if (entry->updateViaCache() >
584 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE) {
585 return NS_ERROR_INVALID_ARG;
588 nsAutoCString installedTimeStr;
589 GET_LINE(installedTimeStr);
590 int64_t installedTime = installedTimeStr.ToInteger64(&rv);
591 if (NS_WARN_IF(NS_FAILED(rv))) {
592 return rv;
594 entry->currentWorkerInstalledTime() = installedTime;
596 nsAutoCString activatedTimeStr;
597 GET_LINE(activatedTimeStr);
598 int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
599 if (NS_WARN_IF(NS_FAILED(rv))) {
600 return rv;
602 entry->currentWorkerActivatedTime() = activatedTime;
604 nsAutoCString lastUpdateTimeStr;
605 GET_LINE(lastUpdateTimeStr);
606 int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
607 if (NS_WARN_IF(NS_FAILED(rv))) {
608 return rv;
610 entry->lastUpdateTime() = lastUpdateTime;
612 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
613 } else if (version.EqualsLiteral("7")) {
614 rv = CreatePrincipalInfo(lineInputStream, entry);
615 if (NS_WARN_IF(NS_FAILED(rv))) {
616 return rv;
619 GET_LINE(entry->currentWorkerURL());
621 nsAutoCString fetchFlag;
622 GET_LINE(fetchFlag);
623 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
624 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
625 return NS_ERROR_INVALID_ARG;
627 entry->currentWorkerHandlesFetch() =
628 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
630 nsAutoCString cacheName;
631 GET_LINE(cacheName);
632 CopyUTF8toUTF16(cacheName, entry->cacheName());
634 nsAutoCString loadFlags;
635 GET_LINE(loadFlags);
636 entry->updateViaCache() =
637 loadFlags.ToInteger(&rv, 16) == nsIRequest::LOAD_NORMAL
638 ? nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL
639 : nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
641 if (NS_WARN_IF(NS_FAILED(rv))) {
642 return rv;
645 nsAutoCString installedTimeStr;
646 GET_LINE(installedTimeStr);
647 int64_t installedTime = installedTimeStr.ToInteger64(&rv);
648 if (NS_WARN_IF(NS_FAILED(rv))) {
649 return rv;
651 entry->currentWorkerInstalledTime() = installedTime;
653 nsAutoCString activatedTimeStr;
654 GET_LINE(activatedTimeStr);
655 int64_t activatedTime = activatedTimeStr.ToInteger64(&rv);
656 if (NS_WARN_IF(NS_FAILED(rv))) {
657 return rv;
659 entry->currentWorkerActivatedTime() = activatedTime;
661 nsAutoCString lastUpdateTimeStr;
662 GET_LINE(lastUpdateTimeStr);
663 int64_t lastUpdateTime = lastUpdateTimeStr.ToInteger64(&rv);
664 if (NS_WARN_IF(NS_FAILED(rv))) {
665 return rv;
667 entry->lastUpdateTime() = lastUpdateTime;
669 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
670 } else if (version.EqualsLiteral("6")) {
671 rv = CreatePrincipalInfo(lineInputStream, entry);
672 if (NS_WARN_IF(NS_FAILED(rv))) {
673 return rv;
676 GET_LINE(entry->currentWorkerURL());
678 nsAutoCString fetchFlag;
679 GET_LINE(fetchFlag);
680 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
681 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
682 return NS_ERROR_INVALID_ARG;
684 entry->currentWorkerHandlesFetch() =
685 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
687 nsAutoCString cacheName;
688 GET_LINE(cacheName);
689 CopyUTF8toUTF16(cacheName, entry->cacheName());
691 nsAutoCString loadFlags;
692 GET_LINE(loadFlags);
693 entry->updateViaCache() =
694 loadFlags.ToInteger(&rv, 16) == nsIRequest::LOAD_NORMAL
695 ? nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL
696 : nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
698 if (NS_WARN_IF(NS_FAILED(rv))) {
699 return rv;
702 entry->currentWorkerInstalledTime() = 0;
703 entry->currentWorkerActivatedTime() = 0;
704 entry->lastUpdateTime() = 0;
706 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
707 } else if (version.EqualsLiteral("5")) {
708 overwrite = true;
709 dedupe = true;
711 rv = CreatePrincipalInfo(lineInputStream, entry);
712 if (NS_WARN_IF(NS_FAILED(rv))) {
713 return rv;
716 GET_LINE(entry->currentWorkerURL());
718 nsAutoCString fetchFlag;
719 GET_LINE(fetchFlag);
720 if (!fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE) &&
721 !fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_FALSE)) {
722 return NS_ERROR_INVALID_ARG;
724 entry->currentWorkerHandlesFetch() =
725 fetchFlag.EqualsLiteral(SERVICEWORKERREGISTRAR_TRUE);
727 nsAutoCString cacheName;
728 GET_LINE(cacheName);
729 CopyUTF8toUTF16(cacheName, entry->cacheName());
731 entry->updateViaCache() =
732 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
734 entry->currentWorkerInstalledTime() = 0;
735 entry->currentWorkerActivatedTime() = 0;
736 entry->lastUpdateTime() = 0;
738 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
739 } else if (version.EqualsLiteral("4")) {
740 overwrite = true;
741 dedupe = true;
743 rv = CreatePrincipalInfo(lineInputStream, entry);
744 if (NS_WARN_IF(NS_FAILED(rv))) {
745 return rv;
748 GET_LINE(entry->currentWorkerURL());
750 // default handlesFetch flag to Enabled
751 entry->currentWorkerHandlesFetch() = true;
753 nsAutoCString cacheName;
754 GET_LINE(cacheName);
755 CopyUTF8toUTF16(cacheName, entry->cacheName());
757 entry->updateViaCache() =
758 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
760 entry->currentWorkerInstalledTime() = 0;
761 entry->currentWorkerActivatedTime() = 0;
762 entry->lastUpdateTime() = 0;
764 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
765 } else if (version.EqualsLiteral("3")) {
766 overwrite = true;
767 dedupe = true;
769 rv = CreatePrincipalInfo(lineInputStream, entry, true);
770 if (NS_WARN_IF(NS_FAILED(rv))) {
771 return rv;
774 GET_LINE(entry->currentWorkerURL());
776 // default handlesFetch flag to Enabled
777 entry->currentWorkerHandlesFetch() = true;
779 nsAutoCString cacheName;
780 GET_LINE(cacheName);
781 CopyUTF8toUTF16(cacheName, entry->cacheName());
783 entry->updateViaCache() =
784 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
786 entry->currentWorkerInstalledTime() = 0;
787 entry->currentWorkerActivatedTime() = 0;
788 entry->lastUpdateTime() = 0;
790 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
791 } else if (version.EqualsLiteral("2")) {
792 overwrite = true;
793 dedupe = true;
795 rv = CreatePrincipalInfo(lineInputStream, entry, true);
796 if (NS_WARN_IF(NS_FAILED(rv))) {
797 return rv;
800 // scriptSpec is no more used in latest version.
801 nsAutoCString unused;
802 GET_LINE(unused);
804 GET_LINE(entry->currentWorkerURL());
806 // default handlesFetch flag to Enabled
807 entry->currentWorkerHandlesFetch() = true;
809 nsAutoCString cacheName;
810 GET_LINE(cacheName);
811 CopyUTF8toUTF16(cacheName, entry->cacheName());
813 // waitingCacheName is no more used in latest version.
814 GET_LINE(unused);
816 entry->updateViaCache() =
817 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS;
819 entry->currentWorkerInstalledTime() = 0;
820 entry->currentWorkerActivatedTime() = 0;
821 entry->lastUpdateTime() = 0;
823 entry->navigationPreloadState() = gDefaultNavigationPreloadState;
824 } else {
825 MOZ_ASSERT_UNREACHABLE("Should never get here!");
828 #undef GET_LINE
830 rv = lineInputStream->ReadLine(line, &hasMoreLines);
831 if (NS_WARN_IF(NS_FAILED(rv))) {
832 return rv;
835 if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
836 return NS_ERROR_FAILURE;
840 stream->Close();
842 // We currently only call this at startup where we block the main thread
843 // preventing further operation until it completes, however take the lock
844 // in case that changes
847 MonitorAutoLock lock(mMonitor);
848 // Copy data over to mData.
849 for (uint32_t i = 0; i < tmpData.Length(); ++i) {
850 // Older versions could sometimes write out empty, useless entries.
851 // Prune those here.
852 if (!ServiceWorkerRegistrationDataIsValid(tmpData[i])) {
853 continue;
856 bool match = false;
857 if (dedupe) {
858 MOZ_ASSERT(overwrite);
859 // If this is an old profile, then we might need to deduplicate. In
860 // theory this can be removed in the future (Bug 1248449)
861 for (uint32_t j = 0; j < mData.Length(); ++j) {
862 // Use same comparison as RegisterServiceWorker. Scope contains
863 // basic origin information. Combine with any principal attributes.
864 if (Equivalent(tmpData[i], mData[j])) {
865 // Last match wins, just like legacy loading used to do in
866 // the ServiceWorkerManager.
867 mData[j] = tmpData[i];
868 // Dupe found, so overwrite file with reduced list.
869 match = true;
870 break;
873 } else {
874 #ifdef DEBUG
875 // Otherwise assert no duplications in debug builds.
876 for (uint32_t j = 0; j < mData.Length(); ++j) {
877 MOZ_ASSERT(!Equivalent(tmpData[i], mData[j]));
879 #endif
881 if (!match) {
882 mData.AppendElement(tmpData[i]);
886 // Overwrite previous version.
887 // Cannot call SaveData directly because gtest uses main-thread.
889 // XXX NOTE: if we could be accessed multi-threaded here, we would need to
890 // find a way to lock around access to mData. Since we can't, suppress the
891 // thread-safety warnings.
892 MOZ_PUSH_IGNORE_THREAD_SAFETY
893 if (overwrite && NS_FAILED(WriteData(mData))) {
894 NS_WARNING("Failed to write data for the ServiceWorker Registations.");
895 DeleteData();
897 MOZ_POP_THREAD_SAFETY
899 return NS_OK;
902 void ServiceWorkerRegistrar::DeleteData() {
903 // We cannot assert about the correct thread because normally this method
904 // runs on a IO thread, but in gTests we call it from the main-thread.
906 nsCOMPtr<nsIFile> file;
909 MonitorAutoLock lock(mMonitor);
910 mData.Clear();
912 if (!mProfileDir) {
913 return;
916 nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
917 if (NS_WARN_IF(NS_FAILED(rv))) {
918 return;
922 nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
923 if (NS_WARN_IF(NS_FAILED(rv))) {
924 return;
927 rv = file->Remove(false);
928 if (rv == NS_ERROR_FILE_NOT_FOUND) {
929 return;
932 if (NS_WARN_IF(NS_FAILED(rv))) {
933 return;
937 void ServiceWorkerRegistrar::RegisterServiceWorkerInternal(
938 const ServiceWorkerRegistrationData& aData) {
939 bool found = false;
940 for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
941 if (Equivalent(aData, mData[i])) {
942 found = true;
943 if (mData[i].currentWorkerHandlesFetch()) {
944 // Decrement here if we found it, in case the new registration no
945 // longer handles Fetch. If it continues to handle fetch, we'll
946 // bump it back later.
947 gServiceWorkersRegisteredFetch--;
949 mData[i] = aData;
950 break;
954 if (!found) {
955 MOZ_ASSERT(ServiceWorkerRegistrationDataIsValid(aData));
956 mData.AppendElement(aData);
957 // We didn't find an entry to update, so we have 1 more
958 gServiceWorkersRegistered++;
960 // Handles bumping both for new registrations and updates
961 if (aData.currentWorkerHandlesFetch()) {
962 gServiceWorkersRegisteredFetch++;
964 // Update Telemetry
965 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_REGISTRATIONS,
966 u"All"_ns, gServiceWorkersRegistered);
967 Telemetry::ScalarSet(Telemetry::ScalarID::SERVICEWORKER_REGISTRATIONS,
968 u"Fetch"_ns, gServiceWorkersRegisteredFetch);
969 LOG(("Register: %u, fetch %u\n", gServiceWorkersRegistered,
970 gServiceWorkersRegisteredFetch));
972 mDataGeneration = GetNextGeneration();
975 class ServiceWorkerRegistrarSaveDataRunnable final : public Runnable {
976 nsCOMPtr<nsIEventTarget> mEventTarget;
977 const nsTArray<ServiceWorkerRegistrationData> mData;
978 const uint32_t mGeneration;
980 public:
981 ServiceWorkerRegistrarSaveDataRunnable(
982 nsTArray<ServiceWorkerRegistrationData>&& aData, uint32_t aGeneration)
983 : Runnable("dom::ServiceWorkerRegistrarSaveDataRunnable"),
984 mEventTarget(GetCurrentSerialEventTarget()),
985 mData(std::move(aData)),
986 mGeneration(aGeneration) {
987 AssertIsOnBackgroundThread();
988 MOZ_DIAGNOSTIC_ASSERT(mGeneration != kInvalidGeneration);
991 NS_IMETHOD
992 Run() override {
993 RefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
994 MOZ_ASSERT(service);
996 uint32_t fileGeneration = kInvalidGeneration;
998 if (NS_SUCCEEDED(service->SaveData(mData))) {
999 fileGeneration = mGeneration;
1002 RefPtr<Runnable> runnable = NewRunnableMethod<uint32_t>(
1003 "ServiceWorkerRegistrar::DataSaved", service,
1004 &ServiceWorkerRegistrar::DataSaved, fileGeneration);
1005 MOZ_ALWAYS_SUCCEEDS(
1006 mEventTarget->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL));
1008 return NS_OK;
1012 void ServiceWorkerRegistrar::MaybeScheduleSaveData() {
1013 AssertIsOnBackgroundThread();
1014 MOZ_ASSERT(!mShuttingDown);
1016 if (mShuttingDown || mSaveDataRunnableDispatched ||
1017 mDataGeneration <= mFileGeneration) {
1018 return;
1021 nsCOMPtr<nsIEventTarget> target =
1022 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
1023 MOZ_ASSERT(target, "Must have stream transport service");
1025 uint32_t generation = kInvalidGeneration;
1026 nsTArray<ServiceWorkerRegistrationData> data;
1029 MonitorAutoLock lock(mMonitor);
1030 generation = mDataGeneration;
1031 data.AppendElements(mData);
1034 RefPtr<Runnable> runnable =
1035 new ServiceWorkerRegistrarSaveDataRunnable(std::move(data), generation);
1036 nsresult rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1037 NS_ENSURE_SUCCESS_VOID(rv);
1039 mSaveDataRunnableDispatched = true;
1042 void ServiceWorkerRegistrar::ShutdownCompleted() {
1043 MOZ_ASSERT(NS_IsMainThread());
1045 DebugOnly<nsresult> rv = GetShutdownPhase()->RemoveBlocker(this);
1046 MOZ_ASSERT(NS_SUCCEEDED(rv));
1049 nsresult ServiceWorkerRegistrar::SaveData(
1050 const nsTArray<ServiceWorkerRegistrationData>& aData) {
1051 MOZ_ASSERT(!NS_IsMainThread());
1053 nsresult rv = WriteData(aData);
1054 if (NS_FAILED(rv)) {
1055 NS_WARNING("Failed to write data for the ServiceWorker Registations.");
1056 // Don't touch the file or in-memory state. Writing files can
1057 // sometimes fail due to virus scanning, etc. We should just leave
1058 // things as is so the next save operation can pick up any changes
1059 // without losing data.
1061 return rv;
1064 void ServiceWorkerRegistrar::DataSaved(uint32_t aFileGeneration) {
1065 AssertIsOnBackgroundThread();
1066 MOZ_ASSERT(mSaveDataRunnableDispatched);
1068 mSaveDataRunnableDispatched = false;
1070 // Check for shutdown before possibly triggering any more saves
1071 // runnables.
1072 MaybeScheduleShutdownCompleted();
1073 if (mShuttingDown) {
1074 return;
1077 // If we got a valid generation, then the save was successful.
1078 if (aFileGeneration != kInvalidGeneration) {
1079 // Update the file generation. We also check to see if we
1080 // can reset the generation back to zero if the file and data
1081 // are now in sync. This allows us to avoid dealing with wrap
1082 // around of the generation count.
1083 mFileGeneration = aFileGeneration;
1084 MaybeResetGeneration();
1086 // Successful write resets the retry count.
1087 mRetryCount = 0;
1089 // Possibly schedule another save operation if more data
1090 // has come in while processing this one.
1091 MaybeScheduleSaveData();
1093 return;
1096 // Otherwise, the save failed since the generation is invalid. We
1097 // want to retry the save, but only a limited number of times.
1098 static const uint32_t kMaxRetryCount = 2;
1099 if (mRetryCount >= kMaxRetryCount) {
1100 return;
1103 mRetryCount += 1;
1104 MaybeScheduleSaveData();
1107 void ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted() {
1108 AssertIsOnBackgroundThread();
1110 if (mSaveDataRunnableDispatched || !mShuttingDown) {
1111 return;
1114 RefPtr<Runnable> runnable =
1115 NewRunnableMethod("dom::ServiceWorkerRegistrar::ShutdownCompleted", this,
1116 &ServiceWorkerRegistrar::ShutdownCompleted);
1117 MOZ_ALWAYS_SUCCEEDS(NS_DispatchToMainThread(runnable.forget()));
1120 uint32_t ServiceWorkerRegistrar::GetNextGeneration() {
1121 uint32_t ret = mDataGeneration + 1;
1122 if (ret == kInvalidGeneration) {
1123 ret += 1;
1125 return ret;
1128 void ServiceWorkerRegistrar::MaybeResetGeneration() {
1129 if (mDataGeneration != mFileGeneration) {
1130 return;
1132 mDataGeneration = mFileGeneration = 0;
1135 bool ServiceWorkerRegistrar::IsSupportedVersion(
1136 const nsACString& aVersion) const {
1137 uint32_t numVersions = ArrayLength(gSupportedRegistrarVersions);
1138 for (uint32_t i = 0; i < numVersions; i++) {
1139 if (aVersion.EqualsASCII(gSupportedRegistrarVersions[i])) {
1140 return true;
1143 return false;
1146 nsresult ServiceWorkerRegistrar::WriteData(
1147 const nsTArray<ServiceWorkerRegistrationData>& aData) {
1148 // We cannot assert about the correct thread because normally this method
1149 // runs on a IO thread, but in gTests we call it from the main-thread.
1151 nsCOMPtr<nsIFile> file;
1154 MonitorAutoLock lock(mMonitor);
1156 if (!mProfileDir) {
1157 return NS_ERROR_FAILURE;
1160 nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
1161 if (NS_WARN_IF(NS_FAILED(rv))) {
1162 return rv;
1166 nsresult rv = file->Append(nsLiteralString(SERVICEWORKERREGISTRAR_FILE));
1167 if (NS_WARN_IF(NS_FAILED(rv))) {
1168 return rv;
1171 nsCOMPtr<nsIOutputStream> stream;
1172 rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
1173 if (NS_WARN_IF(NS_FAILED(rv))) {
1174 return rv;
1177 nsAutoCString buffer;
1178 buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
1179 buffer.Append('\n');
1181 uint32_t count;
1182 rv = stream->Write(buffer.Data(), buffer.Length(), &count);
1183 if (NS_WARN_IF(NS_FAILED(rv))) {
1184 return rv;
1187 if (count != buffer.Length()) {
1188 return NS_ERROR_UNEXPECTED;
1191 for (uint32_t i = 0, len = aData.Length(); i < len; ++i) {
1192 // We have an assertion further up the stack, but as a last
1193 // resort avoid writing out broken entries here.
1194 if (!ServiceWorkerRegistrationDataIsValid(aData[i])) {
1195 continue;
1198 const mozilla::ipc::PrincipalInfo& info = aData[i].principal();
1200 MOZ_ASSERT(info.type() ==
1201 mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
1203 const mozilla::ipc::ContentPrincipalInfo& cInfo =
1204 info.get_ContentPrincipalInfo();
1206 nsAutoCString suffix;
1207 cInfo.attrs().CreateSuffix(suffix);
1209 buffer.Truncate();
1210 buffer.Append(suffix.get());
1211 buffer.Append('\n');
1213 buffer.Append(aData[i].scope());
1214 buffer.Append('\n');
1216 buffer.Append(aData[i].currentWorkerURL());
1217 buffer.Append('\n');
1219 buffer.Append(aData[i].currentWorkerHandlesFetch()
1220 ? SERVICEWORKERREGISTRAR_TRUE
1221 : SERVICEWORKERREGISTRAR_FALSE);
1222 buffer.Append('\n');
1224 buffer.Append(NS_ConvertUTF16toUTF8(aData[i].cacheName()));
1225 buffer.Append('\n');
1227 buffer.AppendInt(aData[i].updateViaCache(), 16);
1228 buffer.Append('\n');
1229 MOZ_DIAGNOSTIC_ASSERT(
1230 aData[i].updateViaCache() ==
1231 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_IMPORTS ||
1232 aData[i].updateViaCache() ==
1233 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_ALL ||
1234 aData[i].updateViaCache() ==
1235 nsIServiceWorkerRegistrationInfo::UPDATE_VIA_CACHE_NONE);
1237 static_assert(nsIRequest::LOAD_NORMAL == 0,
1238 "LOAD_NORMAL matches serialized value.");
1239 static_assert(nsIRequest::VALIDATE_ALWAYS == (1 << 11),
1240 "VALIDATE_ALWAYS matches serialized value");
1242 buffer.AppendInt(aData[i].currentWorkerInstalledTime());
1243 buffer.Append('\n');
1245 buffer.AppendInt(aData[i].currentWorkerActivatedTime());
1246 buffer.Append('\n');
1248 buffer.AppendInt(aData[i].lastUpdateTime());
1249 buffer.Append('\n');
1251 buffer.AppendInt(
1252 static_cast<int32_t>(aData[i].navigationPreloadState().enabled()));
1253 buffer.Append('\n');
1255 buffer.Append(aData[i].navigationPreloadState().headerValue());
1256 buffer.Append('\n');
1258 buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
1259 buffer.Append('\n');
1261 rv = stream->Write(buffer.Data(), buffer.Length(), &count);
1262 if (NS_WARN_IF(NS_FAILED(rv))) {
1263 return rv;
1266 if (count != buffer.Length()) {
1267 return NS_ERROR_UNEXPECTED;
1271 nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
1272 MOZ_ASSERT(safeStream);
1274 rv = safeStream->Finish();
1275 if (NS_WARN_IF(NS_FAILED(rv))) {
1276 return rv;
1279 return NS_OK;
1282 void ServiceWorkerRegistrar::ProfileStarted() {
1283 MOZ_ASSERT(NS_IsMainThread());
1285 MonitorAutoLock lock(mMonitor);
1286 MOZ_DIAGNOSTIC_ASSERT(!mProfileDir);
1288 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1289 getter_AddRefs(mProfileDir));
1290 if (NS_WARN_IF(NS_FAILED(rv))) {
1291 return;
1294 nsAutoString blockerName;
1295 MOZ_ALWAYS_SUCCEEDS(GetName(blockerName));
1297 rv = GetShutdownPhase()->AddBlocker(
1298 this, NS_LITERAL_STRING_FROM_CSTRING(__FILE__), __LINE__, blockerName);
1299 if (NS_WARN_IF(NS_FAILED(rv))) {
1300 return;
1303 nsCOMPtr<nsIEventTarget> target =
1304 do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
1305 MOZ_ASSERT(target, "Must have stream transport service");
1307 nsCOMPtr<nsIRunnable> runnable =
1308 NewRunnableMethod("dom::ServiceWorkerRegistrar::LoadData", this,
1309 &ServiceWorkerRegistrar::LoadData);
1310 rv = target->Dispatch(runnable.forget(), NS_DISPATCH_NORMAL);
1311 if (NS_FAILED(rv)) {
1312 NS_WARNING("Failed to dispatch the LoadDataRunnable.");
1316 void ServiceWorkerRegistrar::ProfileStopped() {
1317 MOZ_ASSERT(NS_IsMainThread());
1319 MonitorAutoLock lock(mMonitor);
1321 if (!mProfileDir) {
1322 nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
1323 getter_AddRefs(mProfileDir));
1324 if (NS_WARN_IF(NS_FAILED(rv))) {
1325 // If we do not have a profile directory, we are somehow screwed.
1326 MOZ_DIAGNOSTIC_ASSERT(
1327 false,
1328 "NS_GetSpecialDirectory for NS_APP_USER_PROFILE_50_DIR failed!");
1332 // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
1333 // issued by the ServiceWorkerManagerService, so the appropriate place to
1334 // trigger shutdown is on that thread.
1336 // However, it's quite possible that the PBackground thread was not brought
1337 // into existence for xpcshell tests. We don't cause it to be created
1338 // ourselves for any reason, for example.
1340 // In this scenario, we know that:
1341 // - We will receive exactly one call to ourself from BlockShutdown() and
1342 // BlockShutdown() will be called (at most) once.
1343 // - The only way our Shutdown() method gets called is via
1344 // BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
1345 // invoked, which only happens if we get to that send below here that we
1346 // can't get to.
1347 // - All Shutdown() does is set mShuttingDown=true (essential for
1348 // invariants) and invoke MaybeScheduleShutdownCompleted().
1349 // - Since there is no PBackground thread, mSaveDataRunnableDispatched must
1350 // be false because only MaybeScheduleSaveData() set it and it only runs
1351 // on the background thread, so it cannot have run. And so we would
1352 // expect MaybeScheduleShutdownCompleted() to schedule an invocation of
1353 // ShutdownCompleted on the main thread.
1354 PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
1355 if (mProfileDir && child) {
1356 if (child->SendShutdownServiceWorkerRegistrar()) {
1357 // Normal shutdown sequence has been initiated, go home.
1358 return;
1360 // If we get here, the PBackground thread has probably gone nuts and we
1361 // want to know it.
1362 MOZ_DIAGNOSTIC_ASSERT(
1363 false, "Unable to send the ShutdownServiceWorkerRegistrar message.");
1366 // On any error it's appropriate to set mShuttingDown=true (as Shutdown
1367 // would do) and directly invoke ShutdownCompleted() (as Shutdown would
1368 // indirectly do via MaybeScheduleShutdownCompleted) in order to unblock
1369 // shutdown.
1370 mShuttingDown = true;
1371 ShutdownCompleted();
1374 // Async shutdown blocker methods
1376 NS_IMETHODIMP
1377 ServiceWorkerRegistrar::BlockShutdown(nsIAsyncShutdownClient* aClient) {
1378 ProfileStopped();
1379 return NS_OK;
1382 NS_IMETHODIMP
1383 ServiceWorkerRegistrar::GetName(nsAString& aName) {
1384 aName = u"ServiceWorkerRegistrar: Flushing data"_ns;
1385 return NS_OK;
1388 NS_IMETHODIMP
1389 ServiceWorkerRegistrar::GetState(nsIPropertyBag** aBagOut) {
1390 nsCOMPtr<nsIWritablePropertyBag2> propertyBag =
1391 do_CreateInstance("@mozilla.org/hash-property-bag;1");
1393 MOZ_TRY(propertyBag->SetPropertyAsBool(u"shuttingDown"_ns, mShuttingDown));
1395 MOZ_TRY(propertyBag->SetPropertyAsBool(u"saveDataRunnableDispatched"_ns,
1396 mSaveDataRunnableDispatched));
1398 propertyBag.forget(aBagOut);
1400 return NS_OK;
1403 #define RELEASE_ASSERT_SUCCEEDED(rv, name) \
1404 do { \
1405 if (NS_FAILED(rv)) { \
1406 if ((rv) == NS_ERROR_XPC_JAVASCRIPT_ERROR_WITH_DETAILS) { \
1407 if (auto* context = CycleCollectedJSContext::Get()) { \
1408 if (RefPtr<Exception> exn = context->GetPendingException()) { \
1409 MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s", \
1410 exn->GetMessageMoz().get()); \
1415 nsAutoCString errorName; \
1416 GetErrorName(rv, errorName); \
1417 MOZ_CRASH_UNSAFE_PRINTF("Failed to get " name ": %s", errorName.get()); \
1419 } while (0)
1421 nsCOMPtr<nsIAsyncShutdownClient> ServiceWorkerRegistrar::GetShutdownPhase()
1422 const {
1423 nsresult rv;
1424 nsCOMPtr<nsIAsyncShutdownService> svc =
1425 do_GetService("@mozilla.org/async-shutdown-service;1", &rv);
1426 // If this fails, something is very wrong on the JS side (or we're out of
1427 // memory), and there's no point in continuing startup. Include as much
1428 // information as possible in the crash report.
1429 RELEASE_ASSERT_SUCCEEDED(rv, "async shutdown service");
1431 nsCOMPtr<nsIAsyncShutdownClient> client;
1432 rv = svc->GetProfileBeforeChange(getter_AddRefs(client));
1433 RELEASE_ASSERT_SUCCEEDED(rv, "profileBeforeChange shutdown blocker");
1434 return client;
1437 #undef RELEASE_ASSERT_SUCCEEDED
1439 void ServiceWorkerRegistrar::Shutdown() {
1440 AssertIsOnBackgroundThread();
1441 MOZ_ASSERT(!mShuttingDown);
1443 mShuttingDown = true;
1444 MaybeScheduleShutdownCompleted();
1447 NS_IMETHODIMP
1448 ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
1449 const char16_t* aData) {
1450 MOZ_ASSERT(NS_IsMainThread());
1452 if (!strcmp(aTopic, "profile-after-change")) {
1453 nsCOMPtr<nsIObserverService> observerService =
1454 services::GetObserverService();
1455 observerService->RemoveObserver(this, "profile-after-change");
1457 // The profile is fully loaded, now we can proceed with the loading of data
1458 // from disk.
1459 ProfileStarted();
1461 return NS_OK;
1464 MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
1465 return NS_ERROR_UNEXPECTED;
1468 } // namespace mozilla::dom