tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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