/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* 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 "ServiceWorkerUnregisterJob.h" #include "mozilla/Unused.h" #include "nsIPushService.h" #include "nsServiceManagerUtils.h" #include "nsThreadUtils.h" #include "ServiceWorkerManager.h" namespace mozilla::dom { class ServiceWorkerUnregisterJob::PushUnsubscribeCallback final : public nsIUnsubscribeResultCallback { public: NS_DECL_ISUPPORTS explicit PushUnsubscribeCallback(ServiceWorkerUnregisterJob* aJob) : mJob(aJob) { MOZ_ASSERT(NS_IsMainThread()); } NS_IMETHOD OnUnsubscribe(nsresult aStatus, bool) override { // Warn if unsubscribing fails, but don't prevent the worker from // unregistering. Unused << NS_WARN_IF(NS_FAILED(aStatus)); mJob->Unregister(); return NS_OK; } private: ~PushUnsubscribeCallback() = default; RefPtr mJob; }; NS_IMPL_ISUPPORTS(ServiceWorkerUnregisterJob::PushUnsubscribeCallback, nsIUnsubscribeResultCallback) ServiceWorkerUnregisterJob::ServiceWorkerUnregisterJob(nsIPrincipal* aPrincipal, const nsACString& aScope) : ServiceWorkerJob(Type::Unregister, aPrincipal, aScope, ""_ns), mResult(false) {} bool ServiceWorkerUnregisterJob::GetResult() const { MOZ_ASSERT(NS_IsMainThread()); return mResult; } ServiceWorkerUnregisterJob::~ServiceWorkerUnregisterJob() = default; void ServiceWorkerUnregisterJob::AsyncExecute() { MOZ_ASSERT(NS_IsMainThread()); if (Canceled()) { Finish(NS_ERROR_DOM_ABORT_ERR); return; } // Push API, section 5: "When a service worker registration is unregistered, // any associated push subscription must be deactivated." To ensure the // service worker registration isn't cleared as we're unregistering, we // unsubscribe first. nsCOMPtr pushService = do_GetService("@mozilla.org/push/Service;1"); if (NS_WARN_IF(!pushService)) { Unregister(); return; } nsCOMPtr unsubscribeCallback = new PushUnsubscribeCallback(this); nsresult rv = pushService->Unsubscribe(NS_ConvertUTF8toUTF16(mScope), mPrincipal, unsubscribeCallback); if (NS_WARN_IF(NS_FAILED(rv))) { Unregister(); } } void ServiceWorkerUnregisterJob::Unregister() { MOZ_ASSERT(NS_IsMainThread()); RefPtr swm = ServiceWorkerManager::GetInstance(); if (Canceled() || !swm) { Finish(NS_ERROR_DOM_ABORT_ERR); return; } // Step 1 of the Unregister algorithm requires checking that the // client origin matches the scope's origin. We perform this in // registration->update() method directly since we don't have that // client information available here. // "Let registration be the result of running [[Get Registration]] // algorithm passing scope as the argument." RefPtr registration = swm->GetRegistration(mPrincipal, mScope); if (!registration) { // "If registration is null, then, resolve promise with false." Finish(NS_OK); return; } // Note, we send the message to remove the registration from disk now. This is // necessary to ensure the registration is removed if the controlled // clients are closed by shutting down the browser. swm->MaybeSendUnregister(mPrincipal, mScope); swm->EvictFromBFCache(registration); // "Remove scope to registration map[job's scope url]." swm->RemoveRegistration(registration); MOZ_ASSERT(registration->IsUnregistered()); // "Resolve promise with true" mResult = true; InvokeResultCallbacks(NS_OK); // "Invoke Try Clear Registration with registration" if (!registration->IsControllingClients()) { if (registration->IsIdle()) { registration->Clear(); } else { registration->ClearWhenIdle(); } } Finish(NS_OK); } } // namespace mozilla::dom