123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881 |
- /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
- /* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
- #include "ServiceWorkerRegistrar.h"
- #include "mozilla/dom/ServiceWorkerRegistrarTypes.h"
- #include "nsIEventTarget.h"
- #include "nsIInputStream.h"
- #include "nsILineInputStream.h"
- #include "nsIObserverService.h"
- #include "nsIOutputStream.h"
- #include "nsISafeOutputStream.h"
- #include "MainThreadUtils.h"
- #include "mozilla/ClearOnShutdown.h"
- #include "mozilla/ipc/BackgroundChild.h"
- #include "mozilla/ipc/BackgroundParent.h"
- #include "mozilla/ipc/PBackgroundChild.h"
- #include "mozilla/ModuleUtils.h"
- #include "mozilla/Services.h"
- #include "mozilla/StaticPtr.h"
- #include "nsAppDirectoryServiceDefs.h"
- #include "nsContentUtils.h"
- #include "nsDirectoryServiceUtils.h"
- #include "nsNetCID.h"
- #include "nsNetUtil.h"
- #include "nsServiceManagerUtils.h"
- #include "nsThreadUtils.h"
- #include "nsXULAppAPI.h"
- using namespace mozilla::ipc;
- namespace mozilla {
- namespace dom {
- namespace {
- static const char* gSupportedRegistrarVersions[] = {
- SERVICEWORKERREGISTRAR_VERSION,
- "3",
- "2"
- };
- StaticRefPtr<ServiceWorkerRegistrar> gServiceWorkerRegistrar;
- } // namespace
- NS_IMPL_ISUPPORTS(ServiceWorkerRegistrar,
- nsIObserver)
- void
- ServiceWorkerRegistrar::Initialize()
- {
- MOZ_ASSERT(!gServiceWorkerRegistrar);
- if (!XRE_IsParentProcess()) {
- return;
- }
- gServiceWorkerRegistrar = new ServiceWorkerRegistrar();
- ClearOnShutdown(&gServiceWorkerRegistrar);
- nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
- if (obs) {
- DebugOnly<nsresult> rv = obs->AddObserver(gServiceWorkerRegistrar,
- "profile-after-change", false);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- rv = obs->AddObserver(gServiceWorkerRegistrar, "profile-before-change",
- false);
- MOZ_ASSERT(NS_SUCCEEDED(rv));
- }
- }
- /* static */ already_AddRefed<ServiceWorkerRegistrar>
- ServiceWorkerRegistrar::Get()
- {
- MOZ_ASSERT(XRE_IsParentProcess());
- MOZ_ASSERT(gServiceWorkerRegistrar);
- RefPtr<ServiceWorkerRegistrar> service = gServiceWorkerRegistrar.get();
- return service.forget();
- }
- ServiceWorkerRegistrar::ServiceWorkerRegistrar()
- : mMonitor("ServiceWorkerRegistrar.mMonitor")
- , mDataLoaded(false)
- , mShuttingDown(false)
- , mShutdownCompleteFlag(nullptr)
- , mRunnableCounter(0)
- {
- MOZ_ASSERT(NS_IsMainThread());
- }
- ServiceWorkerRegistrar::~ServiceWorkerRegistrar()
- {
- MOZ_ASSERT(!mRunnableCounter);
- }
- void
- ServiceWorkerRegistrar::GetRegistrations(
- nsTArray<ServiceWorkerRegistrationData>& aValues)
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(aValues.IsEmpty());
- MonitorAutoLock lock(mMonitor);
- // If we don't have the profile directory, profile is not started yet (and
- // probably we are in a utest).
- if (!mProfileDir) {
- return;
- }
- // We care just about the first execution because this can be blocked by
- // loading data from disk.
- static bool firstTime = true;
- TimeStamp startTime;
- if (firstTime) {
- startTime = TimeStamp::NowLoRes();
- }
- // Waiting for data loaded.
- mMonitor.AssertCurrentThreadOwns();
- while (!mDataLoaded) {
- mMonitor.Wait();
- }
- aValues.AppendElements(mData);
- if (firstTime) {
- firstTime = false;
- }
- }
- namespace {
- bool Equivalent(const ServiceWorkerRegistrationData& aLeft,
- const ServiceWorkerRegistrationData& aRight)
- {
- MOZ_ASSERT(aLeft.principal().type() ==
- mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
- MOZ_ASSERT(aRight.principal().type() ==
- mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
- const auto& leftPrincipal = aLeft.principal().get_ContentPrincipalInfo();
- const auto& rightPrincipal = aRight.principal().get_ContentPrincipalInfo();
- // Only compare the attributes, not the spec part of the principal.
- // The scope comparison above already covers the origin and codebase
- // principals include the full path in their spec which is not what
- // we want here.
- return aLeft.scope() == aRight.scope() &&
- leftPrincipal.attrs() == rightPrincipal.attrs();
- }
- } // anonymous namespace
- void
- ServiceWorkerRegistrar::RegisterServiceWorker(
- const ServiceWorkerRegistrationData& aData)
- {
- AssertIsOnBackgroundThread();
- if (mShuttingDown) {
- NS_WARNING("Failed to register a serviceWorker during shutting down.");
- return;
- }
- {
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mDataLoaded);
- RegisterServiceWorkerInternal(aData);
- }
- ScheduleSaveData();
- }
- void
- ServiceWorkerRegistrar::UnregisterServiceWorker(
- const PrincipalInfo& aPrincipalInfo,
- const nsACString& aScope)
- {
- AssertIsOnBackgroundThread();
- if (mShuttingDown) {
- NS_WARNING("Failed to unregister a serviceWorker during shutting down.");
- return;
- }
- bool deleted = false;
- {
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mDataLoaded);
- ServiceWorkerRegistrationData tmp;
- tmp.principal() = aPrincipalInfo;
- tmp.scope() = aScope;
- for (uint32_t i = 0; i < mData.Length(); ++i) {
- if (Equivalent(tmp, mData[i])) {
- mData.RemoveElementAt(i);
- deleted = true;
- break;
- }
- }
- }
- if (deleted) {
- ScheduleSaveData();
- }
- }
- void
- ServiceWorkerRegistrar::RemoveAll()
- {
- AssertIsOnBackgroundThread();
- if (mShuttingDown) {
- NS_WARNING("Failed to remove all the serviceWorkers during shutting down.");
- return;
- }
- bool deleted = false;
- {
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(mDataLoaded);
- deleted = !mData.IsEmpty();
- mData.Clear();
- }
- if (deleted) {
- ScheduleSaveData();
- }
- }
- void
- ServiceWorkerRegistrar::LoadData()
- {
- MOZ_ASSERT(!NS_IsMainThread());
- MOZ_ASSERT(!mDataLoaded);
- nsresult rv = ReadData();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- DeleteData();
- // Also if the reading failed we have to notify what is waiting for data.
- }
- MonitorAutoLock lock(mMonitor);
- MOZ_ASSERT(!mDataLoaded);
- mDataLoaded = true;
- mMonitor.Notify();
- }
- nsresult
- ServiceWorkerRegistrar::ReadData()
- {
- // We cannot assert about the correct thread because normally this method
- // runs on a IO thread, but in gTests we call it from the main-thread.
- nsCOMPtr<nsIFile> file;
- {
- MonitorAutoLock lock(mMonitor);
- if (!mProfileDir) {
- return NS_ERROR_FAILURE;
- }
- nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- bool exists;
- rv = file->Exists(&exists);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!exists) {
- return NS_OK;
- }
- nsCOMPtr<nsIInputStream> stream;
- rv = NS_NewLocalFileInputStream(getter_AddRefs(stream), file);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsCOMPtr<nsILineInputStream> lineInputStream = do_QueryInterface(stream);
- MOZ_ASSERT(lineInputStream);
- nsAutoCString version;
- bool hasMoreLines;
- rv = lineInputStream->ReadLine(version, &hasMoreLines);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!IsSupportedVersion(version)) {
- nsContentUtils::LogMessageToConsole(nsPrintfCString(
- "Unsupported service worker registrar version: %s", version.get()).get());
- return NS_ERROR_FAILURE;
- }
- nsTArray<ServiceWorkerRegistrationData> tmpData;
- bool overwrite = false;
- bool dedupe = false;
- while (hasMoreLines) {
- ServiceWorkerRegistrationData* entry = tmpData.AppendElement();
- #define GET_LINE(x) \
- rv = lineInputStream->ReadLine(x, &hasMoreLines); \
- if (NS_WARN_IF(NS_FAILED(rv))) { \
- return rv; \
- } \
- if (NS_WARN_IF(!hasMoreLines)) { \
- return NS_ERROR_FAILURE; \
- }
- nsAutoCString line;
- nsAutoCString unused;
- if (version.EqualsLiteral(SERVICEWORKERREGISTRAR_VERSION)) {
- nsAutoCString suffix;
- GET_LINE(suffix);
- PrincipalOriginAttributes attrs;
- if (!attrs.PopulateFromSuffix(suffix)) {
- return NS_ERROR_INVALID_ARG;
- }
- GET_LINE(entry->scope());
- entry->principal() =
- mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
- GET_LINE(entry->currentWorkerURL());
- nsAutoCString cacheName;
- GET_LINE(cacheName);
- CopyUTF8toUTF16(cacheName, entry->cacheName());
- } else if (version.EqualsLiteral("3")) {
- overwrite = true;
- dedupe = true;
- nsAutoCString suffix;
- GET_LINE(suffix);
- PrincipalOriginAttributes attrs;
- if (!attrs.PopulateFromSuffix(suffix)) {
- return NS_ERROR_INVALID_ARG;
- }
- // principal spec is no longer used; we use scope directly instead
- GET_LINE(unused);
- GET_LINE(entry->scope());
- entry->principal() =
- mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
- GET_LINE(entry->currentWorkerURL());
- nsAutoCString cacheName;
- GET_LINE(cacheName);
- CopyUTF8toUTF16(cacheName, entry->cacheName());
- } else if (version.EqualsLiteral("2")) {
- overwrite = true;
- dedupe = true;
- nsAutoCString suffix;
- GET_LINE(suffix);
- PrincipalOriginAttributes attrs;
- if (!attrs.PopulateFromSuffix(suffix)) {
- return NS_ERROR_INVALID_ARG;
- }
- // principal spec is no longer used; we use scope directly instead
- GET_LINE(unused);
- GET_LINE(entry->scope());
- entry->principal() =
- mozilla::ipc::ContentPrincipalInfo(attrs, void_t(), entry->scope());
- // scriptSpec is no more used in latest version.
- GET_LINE(unused);
- GET_LINE(entry->currentWorkerURL());
- nsAutoCString cacheName;
- GET_LINE(cacheName);
- CopyUTF8toUTF16(cacheName, entry->cacheName());
- // waitingCacheName is no more used in latest version.
- GET_LINE(unused);
- } else {
- MOZ_ASSERT_UNREACHABLE("Should never get here!");
- }
- #undef GET_LINE
- rv = lineInputStream->ReadLine(line, &hasMoreLines);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (!line.EqualsLiteral(SERVICEWORKERREGISTRAR_TERMINATOR)) {
- return NS_ERROR_FAILURE;
- }
- }
- stream->Close();
- // Copy data over to mData.
- for (uint32_t i = 0; i < tmpData.Length(); ++i) {
- bool match = false;
- if (dedupe) {
- MOZ_ASSERT(overwrite);
- // If this is an old profile, then we might need to deduplicate. In
- // theory this can be removed in the future (Bug 1248449)
- for (uint32_t j = 0; j < mData.Length(); ++j) {
- // Use same comparison as RegisterServiceWorker. Scope contains
- // basic origin information. Combine with any principal attributes.
- if (Equivalent(tmpData[i], mData[j])) {
- // Last match wins, just like legacy loading used to do in
- // the ServiceWorkerManager.
- mData[j] = tmpData[i];
- // Dupe found, so overwrite file with reduced list.
- match = true;
- break;
- }
- }
- } else {
- #ifdef DEBUG
- // Otherwise assert no duplications in debug builds.
- for (uint32_t j = 0; j < mData.Length(); ++j) {
- MOZ_ASSERT(!Equivalent(tmpData[i], mData[j]));
- }
- #endif
- }
- if (!match) {
- mData.AppendElement(tmpData[i]);
- }
- }
- // Overwrite previous version.
- // Cannot call SaveData directly because gtest uses main-thread.
- if (overwrite && NS_FAILED(WriteData())) {
- NS_WARNING("Failed to write data for the ServiceWorker Registations.");
- DeleteData();
- }
- return NS_OK;
- }
- void
- ServiceWorkerRegistrar::DeleteData()
- {
- // We cannot assert about the correct thread because normally this method
- // runs on a IO thread, but in gTests we call it from the main-thread.
- nsCOMPtr<nsIFile> file;
- {
- MonitorAutoLock lock(mMonitor);
- mData.Clear();
- if (!mProfileDir) {
- return;
- }
- nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- }
- nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- rv = file->Remove(false);
- if (rv == NS_ERROR_FILE_NOT_FOUND) {
- return;
- }
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- }
- void
- ServiceWorkerRegistrar::RegisterServiceWorkerInternal(const ServiceWorkerRegistrationData& aData)
- {
- bool found = false;
- for (uint32_t i = 0, len = mData.Length(); i < len; ++i) {
- if (Equivalent(aData, mData[i])) {
- mData[i] = aData;
- found = true;
- break;
- }
- }
- if (!found) {
- mData.AppendElement(aData);
- }
- }
- class ServiceWorkerRegistrarSaveDataRunnable final : public Runnable
- {
- public:
- ServiceWorkerRegistrarSaveDataRunnable()
- : mThread(do_GetCurrentThread())
- {
- AssertIsOnBackgroundThread();
- }
- NS_IMETHOD
- Run() override
- {
- RefPtr<ServiceWorkerRegistrar> service = ServiceWorkerRegistrar::Get();
- MOZ_ASSERT(service);
- service->SaveData();
- RefPtr<Runnable> runnable =
- NewRunnableMethod(service, &ServiceWorkerRegistrar::DataSaved);
- nsresult rv = mThread->Dispatch(runnable, NS_DISPATCH_NORMAL);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- private:
- nsCOMPtr<nsIThread> mThread;
- };
- void
- ServiceWorkerRegistrar::ScheduleSaveData()
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(!mShuttingDown);
- nsCOMPtr<nsIEventTarget> target =
- do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
- MOZ_ASSERT(target, "Must have stream transport service");
- RefPtr<Runnable> runnable =
- new ServiceWorkerRegistrarSaveDataRunnable();
- nsresult rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- ++mRunnableCounter;
- }
- void
- ServiceWorkerRegistrar::ShutdownCompleted()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MOZ_ASSERT(mShutdownCompleteFlag && !*mShutdownCompleteFlag);
- *mShutdownCompleteFlag = true;
- }
- void
- ServiceWorkerRegistrar::SaveData()
- {
- MOZ_ASSERT(!NS_IsMainThread());
- nsresult rv = WriteData();
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to write data for the ServiceWorker Registations.");
- DeleteData();
- }
- }
- void
- ServiceWorkerRegistrar::DataSaved()
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(mRunnableCounter);
- --mRunnableCounter;
- MaybeScheduleShutdownCompleted();
- }
- void
- ServiceWorkerRegistrar::MaybeScheduleShutdownCompleted()
- {
- AssertIsOnBackgroundThread();
- if (mRunnableCounter || !mShuttingDown) {
- return;
- }
- RefPtr<Runnable> runnable =
- NewRunnableMethod(this, &ServiceWorkerRegistrar::ShutdownCompleted);
- nsresult rv = NS_DispatchToMainThread(runnable);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- }
- bool
- ServiceWorkerRegistrar::IsSupportedVersion(const nsACString& aVersion) const
- {
- uint32_t numVersions = ArrayLength(gSupportedRegistrarVersions);
- for (uint32_t i = 0; i < numVersions; i++) {
- if (aVersion.EqualsASCII(gSupportedRegistrarVersions[i])) {
- return true;
- }
- }
- return false;
- }
- nsresult
- ServiceWorkerRegistrar::WriteData()
- {
- // We cannot assert about the correct thread because normally this method
- // runs on a IO thread, but in gTests we call it from the main-thread.
- nsCOMPtr<nsIFile> file;
- {
- MonitorAutoLock lock(mMonitor);
- if (!mProfileDir) {
- return NS_ERROR_FAILURE;
- }
- nsresult rv = mProfileDir->Clone(getter_AddRefs(file));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- }
- nsresult rv = file->Append(NS_LITERAL_STRING(SERVICEWORKERREGISTRAR_FILE));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- // We need a lock to take a snapshot of the data.
- nsTArray<ServiceWorkerRegistrationData> data;
- {
- MonitorAutoLock lock(mMonitor);
- data = mData;
- }
- nsCOMPtr<nsIOutputStream> stream;
- rv = NS_NewSafeLocalFileOutputStream(getter_AddRefs(stream), file);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- nsAutoCString buffer;
- buffer.AppendLiteral(SERVICEWORKERREGISTRAR_VERSION);
- buffer.Append('\n');
- uint32_t count;
- rv = stream->Write(buffer.Data(), buffer.Length(), &count);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (count != buffer.Length()) {
- return NS_ERROR_UNEXPECTED;
- }
- for (uint32_t i = 0, len = data.Length(); i < len; ++i) {
- const mozilla::ipc::PrincipalInfo& info = data[i].principal();
- MOZ_ASSERT(info.type() == mozilla::ipc::PrincipalInfo::TContentPrincipalInfo);
- const mozilla::ipc::ContentPrincipalInfo& cInfo =
- info.get_ContentPrincipalInfo();
- nsAutoCString suffix;
- cInfo.attrs().CreateSuffix(suffix);
- buffer.Truncate();
- buffer.Append(suffix.get());
- buffer.Append('\n');
- buffer.Append(data[i].scope());
- buffer.Append('\n');
- buffer.Append(data[i].currentWorkerURL());
- buffer.Append('\n');
- buffer.Append(NS_ConvertUTF16toUTF8(data[i].cacheName()));
- buffer.Append('\n');
- buffer.AppendLiteral(SERVICEWORKERREGISTRAR_TERMINATOR);
- buffer.Append('\n');
- rv = stream->Write(buffer.Data(), buffer.Length(), &count);
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- if (count != buffer.Length()) {
- return NS_ERROR_UNEXPECTED;
- }
- }
- nsCOMPtr<nsISafeOutputStream> safeStream = do_QueryInterface(stream);
- MOZ_ASSERT(safeStream);
- rv = safeStream->Finish();
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return rv;
- }
- return NS_OK;
- }
- void
- ServiceWorkerRegistrar::ProfileStarted()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MonitorAutoLock lock(mMonitor);
- MOZ_DIAGNOSTIC_ASSERT(!mProfileDir);
- nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
- getter_AddRefs(mProfileDir));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- nsCOMPtr<nsIEventTarget> target =
- do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID);
- MOZ_ASSERT(target, "Must have stream transport service");
- nsCOMPtr<nsIRunnable> runnable =
- NewRunnableMethod(this, &ServiceWorkerRegistrar::LoadData);
- rv = target->Dispatch(runnable, NS_DISPATCH_NORMAL);
- if (NS_FAILED(rv)) {
- NS_WARNING("Failed to dispatch the LoadDataRunnable.");
- }
- }
- void
- ServiceWorkerRegistrar::ProfileStopped()
- {
- MOZ_ASSERT(NS_IsMainThread());
- MonitorAutoLock lock(mMonitor);
- if (!mProfileDir) {
- nsresult rv = NS_GetSpecialDirectory(NS_APP_USER_PROFILE_50_DIR,
- getter_AddRefs(mProfileDir));
- if (NS_WARN_IF(NS_FAILED(rv))) {
- return;
- }
- }
- // We must set the pointer before potentially entering the fast-path shutdown
- // below.
- bool completed = false;
- mShutdownCompleteFlag = &completed;
- PBackgroundChild* child = BackgroundChild::GetForCurrentThread();
- if (!child) {
- // Mutations to the ServiceWorkerRegistrar happen on the PBackground thread,
- // issued by the ServiceWorkerManagerService, so the appropriate place to
- // trigger shutdown is on that thread.
- //
- // However, it's quite possible that the PBackground thread was not brought
- // into existence for xpcshell tests. We don't cause it to be created
- // ourselves for any reason, for example.
- //
- // In this scenario, we know that:
- // - We will receive exactly one call to ourself from BlockShutdown() and
- // BlockShutdown() will be called (at most) once.
- // - The only way our Shutdown() method gets called is via
- // BackgroundParentImpl::RecvShutdownServiceWorkerRegistrar() being
- // invoked, which only happens if we get to that send below here that we
- // can't get to.
- // - All Shutdown() does is set mShuttingDown=true (essential for
- // invariants) and invoke MaybeScheduleShutdownCompleted().
- // - Since there is no PBackground thread, mRunnableCounter must be 0
- // because only ScheduleSaveData() increments it and it only runs on the
- // background thread, so it cannot have run. And so we would expect
- // MaybeScheduleShutdownCompleted() to schedule an invocation of
- // ShutdownCompleted on the main thread.
- //
- // So it's appropriate for us to set mShuttingDown=true (as Shutdown would
- // do) and directly invoke ShutdownCompleted() (as Shutdown would indirectly
- // do via MaybeScheduleShutdownCompleted).
- mShuttingDown = true;
- ShutdownCompleted();
- return;
- }
- child->SendShutdownServiceWorkerRegistrar();
- nsCOMPtr<nsIThread> thread(do_GetCurrentThread());
- while (true) {
- MOZ_ALWAYS_TRUE(NS_ProcessNextEvent(thread));
- if (completed) {
- break;
- }
- }
- }
- void
- ServiceWorkerRegistrar::Shutdown()
- {
- AssertIsOnBackgroundThread();
- MOZ_ASSERT(!mShuttingDown);
- mShuttingDown = true;
- MaybeScheduleShutdownCompleted();
- }
- NS_IMETHODIMP
- ServiceWorkerRegistrar::Observe(nsISupports* aSubject, const char* aTopic,
- const char16_t* aData)
- {
- MOZ_ASSERT(NS_IsMainThread());
- if (!strcmp(aTopic, "profile-after-change")) {
- nsCOMPtr<nsIObserverService> observerService =
- services::GetObserverService();
- observerService->RemoveObserver(this, "profile-after-change");
- // The profile is fully loaded, now we can proceed with the loading of data
- // from disk.
- ProfileStarted();
- return NS_OK;
- }
- if (!strcmp(aTopic, "profile-before-change")) {
- // Hygiene; gServiceWorkerRegistrar should still be keeping a reference
- // alive well past this phase of shutdown, but it's bad form to drop your
- // last potentially owning reference and then make a call that requires you
- // to still be alive, especially when you spin a nested event loop.
- RefPtr<ServiceWorkerRegistrar> kungFuDeathGrip(this);
- nsCOMPtr<nsIObserverService> observerService =
- services::GetObserverService();
- observerService->RemoveObserver(this, "profile-before-change");
- // Shutting down, let's sync the data.
- ProfileStopped();
- return NS_OK;
- }
- MOZ_ASSERT(false, "ServiceWorkerRegistrar got unexpected topic!");
- return NS_ERROR_UNEXPECTED;
- }
- } // namespace dom
- } // namespace mozilla
|