ServiceWorkerUnregisterJob.cpp (6332B)
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/. */ 6 7 #include "ServiceWorkerUnregisterJob.h" 8 9 #include "ServiceWorkerManager.h" 10 #include "mozilla/dom/CookieStoreSubscriptionService.h" 11 #include "mozilla/dom/notification/NotificationUtils.h" 12 #include "nsIAlertsService.h" 13 #include "nsIPushService.h" 14 #include "nsServiceManagerUtils.h" 15 #include "nsThreadUtils.h" 16 17 using namespace mozilla::dom::notification; 18 19 namespace mozilla::dom { 20 21 class ServiceWorkerUnregisterJob::PushUnsubscribeCallback final 22 : public nsIUnsubscribeResultCallback { 23 public: 24 NS_DECL_ISUPPORTS 25 26 already_AddRefed<GenericPromise> Promise() { 27 return mPromiseHolder.Ensure(__func__); 28 } 29 30 NS_IMETHOD 31 OnUnsubscribe(nsresult aStatus, bool success) override { 32 // Warn if unsubscribing fails, but don't prevent the worker from 33 // unregistering. 34 (void)NS_WARN_IF(NS_FAILED(aStatus)); 35 mPromiseHolder.Resolve(success, __func__); 36 return NS_OK; 37 } 38 39 private: 40 virtual ~PushUnsubscribeCallback() { 41 // We may be shutting down prematurely without getting the result, so make 42 // sure to settle the promise. 43 mPromiseHolder.RejectIfExists(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); 44 }; 45 46 MozPromiseHolder<GenericPromise> mPromiseHolder; 47 }; 48 49 NS_IMPL_ISUPPORTS(ServiceWorkerUnregisterJob::PushUnsubscribeCallback, 50 nsIUnsubscribeResultCallback) 51 52 ServiceWorkerUnregisterJob::ServiceWorkerUnregisterJob(nsIPrincipal* aPrincipal, 53 const nsACString& aScope) 54 : ServiceWorkerJob(Type::Unregister, aPrincipal, aScope, ""_ns), 55 mResult(false) {} 56 57 bool ServiceWorkerUnregisterJob::GetResult() const { 58 MOZ_ASSERT(NS_IsMainThread()); 59 return mResult; 60 } 61 62 ServiceWorkerUnregisterJob::~ServiceWorkerUnregisterJob() = default; 63 64 already_AddRefed<GenericPromise> 65 ServiceWorkerUnregisterJob::ClearNotifications() { 66 RefPtr<GenericPromise::Private> resultPromise = 67 new GenericPromise::Private(__func__); 68 69 nsCOMPtr<nsIAlertsService> alertsService = 70 do_GetService("@mozilla.org/alerts-service;1"); 71 72 nsAutoCString origin; 73 nsresult rv = mPrincipal->GetOrigin(origin); 74 if (!alertsService || NS_FAILED(rv)) { 75 resultPromise->Reject(rv, __func__); 76 return resultPromise.forget(); 77 } 78 79 RefPtr<NotificationsPromise> promise = 80 GetStoredNotificationsForScope(mPrincipal, mScope, u""_ns); 81 82 promise->Then( 83 GetCurrentSerialEventTarget(), __func__, 84 [resultPromise, 85 alertsService](const CopyableTArray<IPCNotification>& aNotifications) { 86 for (const IPCNotification& notification : aNotifications) { 87 // CloseAlert will emit alertfinished which will synchronously remove 88 // each notification also from the DB. (The DB removal doesn't happen 89 // synchronously but its task queue guarantees the order.) 90 alertsService->CloseAlert(notification.id(), false); 91 } 92 resultPromise->Resolve(true, __func__); 93 }, 94 [resultPromise](nsresult rv) { resultPromise->Reject(rv, __func__); }); 95 96 return resultPromise.forget(); 97 } 98 99 already_AddRefed<GenericPromise> 100 ServiceWorkerUnregisterJob::ClearPushSubscriptions() { 101 nsresult rv = NS_OK; 102 nsCOMPtr<nsIPushService> pushService = 103 do_GetService("@mozilla.org/push/Service;1", &rv); 104 if (NS_FAILED(rv)) { 105 return GenericPromise::CreateAndReject(rv, __func__).forget(); 106 } 107 108 nsCOMPtr<PushUnsubscribeCallback> unsubscribeCallback = 109 new PushUnsubscribeCallback(); 110 rv = pushService->Unsubscribe(NS_ConvertUTF8toUTF16(mScope), mPrincipal, 111 unsubscribeCallback); 112 if (NS_FAILED(rv)) { 113 return GenericPromise::CreateAndReject(rv, __func__).forget(); 114 } 115 return unsubscribeCallback->Promise(); 116 } 117 118 void ServiceWorkerUnregisterJob::AsyncExecute() { 119 MOZ_ASSERT(NS_IsMainThread()); 120 121 if (Canceled()) { 122 Finish(NS_ERROR_DOM_ABORT_ERR); 123 return; 124 } 125 126 CookieStoreSubscriptionService::ServiceWorkerUnregistered(mPrincipal, mScope); 127 128 nsTArray<RefPtr<GenericPromise>> promises{ClearNotifications(), 129 ClearPushSubscriptions()}; 130 131 GenericPromise::AllSettled(GetMainThreadSerialEventTarget(), promises) 132 ->Then(GetMainThreadSerialEventTarget(), __func__, 133 [self = RefPtr(this)]( 134 GenericPromise::AllSettledPromiseType::ResolveOrRejectValue&& 135 aValue) { self->Unregister(); }); 136 } 137 138 void ServiceWorkerUnregisterJob::Unregister() { 139 MOZ_ASSERT(NS_IsMainThread()); 140 141 RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance(); 142 if (Canceled() || !swm) { 143 Finish(NS_ERROR_DOM_ABORT_ERR); 144 return; 145 } 146 147 // Step 1 of the Unregister algorithm requires checking that the 148 // client origin matches the scope's origin. We perform this in 149 // registration->update() method directly since we don't have that 150 // client information available here. 151 152 // "Let registration be the result of running [[Get Registration]] 153 // algorithm passing scope as the argument." 154 RefPtr<ServiceWorkerRegistrationInfo> registration = 155 swm->GetRegistration(mPrincipal, mScope); 156 if (!registration) { 157 // "If registration is null, then, resolve promise with false." 158 Finish(NS_OK); 159 return; 160 } 161 162 // Note, we send the message to remove the registration from disk now. This is 163 // necessary to ensure the registration is removed if the controlled 164 // clients are closed by shutting down the browser. 165 swm->MaybeSendUnregister(mPrincipal, mScope); 166 167 swm->EvictFromBFCache(registration); 168 169 // "Remove scope to registration map[job's scope url]." 170 swm->RemoveRegistration(registration); 171 MOZ_ASSERT(registration->IsUnregistered()); 172 173 // "Resolve promise with true" 174 mResult = true; 175 InvokeResultCallbacks(NS_OK); 176 177 // "Invoke Try Clear Registration with registration" 178 if (!registration->IsControllingClients()) { 179 if (registration->IsIdle()) { 180 registration->Clear(); 181 } else { 182 registration->ClearWhenIdle(); 183 } 184 } 185 186 Finish(NS_OK); 187 } 188 189 } // namespace mozilla::dom