tor-browser

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

ServiceWorkerRegistrationProxy.cpp (16640B)


      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 "ServiceWorkerRegistrationProxy.h"
      8 
      9 #include "ServiceWorkerManager.h"
     10 #include "ServiceWorkerRegistrationParent.h"
     11 #include "ServiceWorkerUnregisterCallback.h"
     12 #include "mozilla/SchedulerGroup.h"
     13 #include "mozilla/ScopeExit.h"
     14 #include "mozilla/dom/notification/NotificationUtils.h"
     15 #include "mozilla/ipc/BackgroundParent.h"
     16 #include "nsINotificationStorage.h"
     17 
     18 namespace mozilla::dom {
     19 
     20 using mozilla::ipc::AssertIsOnBackgroundThread;
     21 using namespace mozilla::dom::notification;
     22 
     23 class ServiceWorkerRegistrationProxy::DelayedUpdate final
     24    : public nsITimerCallback,
     25      public nsINamed {
     26  RefPtr<ServiceWorkerRegistrationProxy> mProxy;
     27  RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
     28  nsCOMPtr<nsITimer> mTimer;
     29  nsCString mNewestWorkerScriptUrl;
     30 
     31  ~DelayedUpdate() = default;
     32 
     33 public:
     34  NS_DECL_THREADSAFE_ISUPPORTS
     35  NS_DECL_NSITIMERCALLBACK
     36  NS_DECL_NSINAMED
     37 
     38  DelayedUpdate(RefPtr<ServiceWorkerRegistrationProxy>&& aProxy,
     39                RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise,
     40                nsCString&& aNewestWorkerScriptUrl, uint32_t delay);
     41 
     42  void ChainTo(RefPtr<ServiceWorkerRegistrationPromise::Private> aPromise);
     43 
     44  void Reject();
     45 
     46  void SetNewestWorkerScriptUrl(nsCString&& aNewestWorkerScriptUrl);
     47 };
     48 
     49 ServiceWorkerRegistrationProxy::~ServiceWorkerRegistrationProxy() {
     50  // Any thread
     51  MOZ_DIAGNOSTIC_ASSERT(!mActor);
     52  MOZ_DIAGNOSTIC_ASSERT(!mReg);
     53 }
     54 
     55 void ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread() {
     56  AssertIsOnBackgroundThread();
     57  if (!mActor) {
     58    return;
     59  }
     60  mActor->MaybeSendDelete();
     61 }
     62 
     63 void ServiceWorkerRegistrationProxy::UpdateStateOnBGThread(
     64    const ServiceWorkerRegistrationDescriptor& aDescriptor) {
     65  AssertIsOnBackgroundThread();
     66  if (!mActor) {
     67    return;
     68  }
     69  (void)mActor->SendUpdateState(aDescriptor.ToIPC());
     70 }
     71 
     72 void ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread() {
     73  AssertIsOnBackgroundThread();
     74  if (!mActor) {
     75    return;
     76  }
     77  (void)mActor->SendFireUpdateFound();
     78 }
     79 
     80 void ServiceWorkerRegistrationProxy::InitOnMainThread() {
     81  AssertIsOnMainThread();
     82 
     83  auto scopeExit = MakeScopeExit([&] { MaybeShutdownOnMainThread(); });
     84 
     85  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
     86  NS_ENSURE_TRUE_VOID(swm);
     87 
     88  RefPtr<ServiceWorkerRegistrationInfo> reg =
     89      swm->GetRegistration(mDescriptor.PrincipalInfo(), mDescriptor.Scope());
     90  NS_ENSURE_TRUE_VOID(reg);
     91 
     92  if (reg->Id() != mDescriptor.Id()) {
     93    // This registration has already been replaced by another one.
     94    return;
     95  }
     96 
     97  scopeExit.release();
     98 
     99  mReg = new nsMainThreadPtrHolder<ServiceWorkerRegistrationInfo>(
    100      "ServiceWorkerRegistrationProxy::mInfo", reg);
    101 
    102  mReg->AddInstance(this, mDescriptor);
    103 }
    104 
    105 void ServiceWorkerRegistrationProxy::MaybeShutdownOnMainThread() {
    106  AssertIsOnMainThread();
    107 
    108  if (mDelayedUpdate) {
    109    mDelayedUpdate->Reject();
    110    mDelayedUpdate = nullptr;
    111  }
    112  nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
    113      __func__, this, &ServiceWorkerRegistrationProxy::MaybeShutdownOnBGThread);
    114 
    115  MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
    116 }
    117 
    118 void ServiceWorkerRegistrationProxy::StopListeningOnMainThread() {
    119  AssertIsOnMainThread();
    120 
    121  if (!mReg) {
    122    return;
    123  }
    124 
    125  mReg->RemoveInstance(this);
    126  mReg = nullptr;
    127 }
    128 
    129 void ServiceWorkerRegistrationProxy::UpdateState(
    130    const ServiceWorkerRegistrationDescriptor& aDescriptor) {
    131  AssertIsOnMainThread();
    132 
    133  if (mDescriptor == aDescriptor) {
    134    return;
    135  }
    136  mDescriptor = aDescriptor;
    137 
    138  nsCOMPtr<nsIRunnable> r =
    139      NewRunnableMethod<ServiceWorkerRegistrationDescriptor>(
    140          __func__, this,
    141          &ServiceWorkerRegistrationProxy::UpdateStateOnBGThread, aDescriptor);
    142 
    143  MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
    144 }
    145 
    146 void ServiceWorkerRegistrationProxy::FireUpdateFound() {
    147  AssertIsOnMainThread();
    148 
    149  nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
    150      __func__, this,
    151      &ServiceWorkerRegistrationProxy::FireUpdateFoundOnBGThread);
    152 
    153  MOZ_ALWAYS_SUCCEEDS(mEventTarget->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
    154 }
    155 
    156 void ServiceWorkerRegistrationProxy::RegistrationCleared() {
    157  MaybeShutdownOnMainThread();
    158 }
    159 
    160 void ServiceWorkerRegistrationProxy::GetScope(nsAString& aScope) const {
    161  CopyUTF8toUTF16(mDescriptor.Scope(), aScope);
    162 }
    163 
    164 bool ServiceWorkerRegistrationProxy::MatchesDescriptor(
    165    const ServiceWorkerRegistrationDescriptor& aDescriptor) {
    166  AssertIsOnMainThread();
    167  return aDescriptor.Id() == mDescriptor.Id() &&
    168         aDescriptor.PrincipalInfo() == mDescriptor.PrincipalInfo() &&
    169         aDescriptor.Scope() == mDescriptor.Scope();
    170 }
    171 
    172 ServiceWorkerRegistrationProxy::ServiceWorkerRegistrationProxy(
    173    const ServiceWorkerRegistrationDescriptor& aDescriptor,
    174    const ClientInfo& aForClient)
    175    : mEventTarget(GetCurrentSerialEventTarget()),
    176      mDescriptor(aDescriptor),
    177      mListeningClientInfo(aForClient) {}
    178 
    179 void ServiceWorkerRegistrationProxy::Init(
    180    ServiceWorkerRegistrationParent* aActor) {
    181  AssertIsOnBackgroundThread();
    182  MOZ_DIAGNOSTIC_ASSERT(aActor);
    183  MOZ_DIAGNOSTIC_ASSERT(!mActor);
    184  MOZ_DIAGNOSTIC_ASSERT(mEventTarget);
    185 
    186  mActor = aActor;
    187 
    188  // Note, this must be done from a separate Init() method and not in
    189  // the constructor.  If done from the constructor the runnable can
    190  // execute, complete, and release its reference before the constructor
    191  // returns.
    192  nsCOMPtr<nsIRunnable> r =
    193      NewRunnableMethod("ServiceWorkerRegistrationProxy::Init", this,
    194                        &ServiceWorkerRegistrationProxy::InitOnMainThread);
    195  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    196 }
    197 
    198 void ServiceWorkerRegistrationProxy::RevokeActor(
    199    ServiceWorkerRegistrationParent* aActor) {
    200  AssertIsOnBackgroundThread();
    201  MOZ_DIAGNOSTIC_ASSERT(mActor);
    202  MOZ_DIAGNOSTIC_ASSERT(mActor == aActor);
    203  mActor = nullptr;
    204 
    205  nsCOMPtr<nsIRunnable> r = NewRunnableMethod(
    206      __func__, this,
    207      &ServiceWorkerRegistrationProxy::StopListeningOnMainThread);
    208  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    209 }
    210 
    211 RefPtr<GenericPromise> ServiceWorkerRegistrationProxy::Unregister() {
    212  AssertIsOnBackgroundThread();
    213 
    214  RefPtr<ServiceWorkerRegistrationProxy> self = this;
    215  RefPtr<GenericPromise::Private> promise =
    216      new GenericPromise::Private(__func__);
    217 
    218  nsCOMPtr<nsIRunnable> r =
    219      NS_NewRunnableFunction(__func__, [self, promise]() mutable {
    220        nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR;
    221        auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); });
    222 
    223        NS_ENSURE_TRUE_VOID(self->mReg);
    224 
    225        RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    226        NS_ENSURE_TRUE_VOID(swm);
    227 
    228        RefPtr<UnregisterCallback> cb = new UnregisterCallback(promise);
    229 
    230        rv = swm->Unregister(self->mReg->Principal(), cb,
    231                             NS_ConvertUTF8toUTF16(self->mReg->Scope()));
    232        NS_ENSURE_SUCCESS_VOID(rv);
    233 
    234        scopeExit.release();
    235      });
    236 
    237  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    238 
    239  return promise;
    240 }
    241 
    242 namespace {
    243 
    244 class UpdateCallback final : public ServiceWorkerUpdateFinishCallback {
    245  RefPtr<ServiceWorkerRegistrationPromise::Private> mPromise;
    246 
    247  ~UpdateCallback() = default;
    248 
    249 public:
    250  explicit UpdateCallback(
    251      RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise)
    252      : mPromise(std::move(aPromise)) {
    253    MOZ_DIAGNOSTIC_ASSERT(mPromise);
    254  }
    255 
    256  void UpdateSucceeded(ServiceWorkerRegistrationInfo* aInfo) override {
    257    mPromise->Resolve(aInfo->Descriptor(), __func__);
    258  }
    259 
    260  void UpdateFailed(ErrorResult& aResult) override {
    261    mPromise->Reject(CopyableErrorResult(aResult), __func__);
    262  }
    263 };
    264 
    265 }  // anonymous namespace
    266 
    267 NS_IMPL_ISUPPORTS(ServiceWorkerRegistrationProxy::DelayedUpdate,
    268                  nsITimerCallback, nsINamed)
    269 
    270 ServiceWorkerRegistrationProxy::DelayedUpdate::DelayedUpdate(
    271    RefPtr<ServiceWorkerRegistrationProxy>&& aProxy,
    272    RefPtr<ServiceWorkerRegistrationPromise::Private>&& aPromise,
    273    nsCString&& aNewestWorkerScriptUrl, uint32_t delay)
    274    : mProxy(std::move(aProxy)),
    275      mPromise(std::move(aPromise)),
    276      mNewestWorkerScriptUrl(std::move(aNewestWorkerScriptUrl)) {
    277  MOZ_DIAGNOSTIC_ASSERT(mProxy);
    278  MOZ_DIAGNOSTIC_ASSERT(mPromise);
    279  MOZ_ASSERT(!mNewestWorkerScriptUrl.IsEmpty());
    280  mProxy->mDelayedUpdate = this;
    281  Result<nsCOMPtr<nsITimer>, nsresult> result =
    282      NS_NewTimerWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
    283  mTimer = result.unwrapOr(nullptr);
    284  MOZ_DIAGNOSTIC_ASSERT(mTimer);
    285 }
    286 
    287 void ServiceWorkerRegistrationProxy::DelayedUpdate::ChainTo(
    288    RefPtr<ServiceWorkerRegistrationPromise::Private> aPromise) {
    289  AssertIsOnMainThread();
    290  MOZ_ASSERT(mProxy->mDelayedUpdate == this);
    291  MOZ_ASSERT(mPromise);
    292 
    293  mPromise->ChainTo(aPromise.forget(), __func__);
    294 }
    295 
    296 void ServiceWorkerRegistrationProxy::DelayedUpdate::Reject() {
    297  MOZ_DIAGNOSTIC_ASSERT(mPromise);
    298  if (mTimer) {
    299    mTimer->Cancel();
    300    mTimer = nullptr;
    301  }
    302  mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
    303 }
    304 
    305 void ServiceWorkerRegistrationProxy::DelayedUpdate::SetNewestWorkerScriptUrl(
    306    nsCString&& aNewestWorkerScriptUrl) {
    307  MOZ_ASSERT(NS_IsMainThread());
    308  mNewestWorkerScriptUrl = std::move(aNewestWorkerScriptUrl);
    309 }
    310 
    311 NS_IMETHODIMP
    312 ServiceWorkerRegistrationProxy::DelayedUpdate::Notify(nsITimer* aTimer) {
    313  // Already shutting down.
    314  if (mProxy->mDelayedUpdate != this) {
    315    return NS_OK;
    316  }
    317 
    318  auto scopeExit = MakeScopeExit(
    319      [&] { mPromise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); });
    320 
    321  NS_ENSURE_TRUE(mProxy->mReg, NS_ERROR_FAILURE);
    322 
    323  RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    324  NS_ENSURE_TRUE(swm, NS_ERROR_FAILURE);
    325 
    326  RefPtr<UpdateCallback> cb = new UpdateCallback(std::move(mPromise));
    327  swm->Update(mProxy->mListeningClientInfo, mProxy->mReg->Principal(),
    328              mProxy->mReg->Scope(), std::move(mNewestWorkerScriptUrl), cb);
    329 
    330  mTimer = nullptr;
    331  mProxy->mDelayedUpdate = nullptr;
    332 
    333  scopeExit.release();
    334  return NS_OK;
    335 }
    336 
    337 NS_IMETHODIMP
    338 ServiceWorkerRegistrationProxy::DelayedUpdate::GetName(nsACString& aName) {
    339  aName.AssignLiteral("ServiceWorkerRegistrationProxy::DelayedUpdate");
    340  return NS_OK;
    341 }
    342 
    343 RefPtr<ServiceWorkerRegistrationPromise> ServiceWorkerRegistrationProxy::Update(
    344    const nsACString& aNewestWorkerScriptUrl) {
    345  AssertIsOnBackgroundThread();
    346 
    347  RefPtr<ServiceWorkerRegistrationProxy> self = this;
    348  RefPtr<ServiceWorkerRegistrationPromise::Private> promise =
    349      new ServiceWorkerRegistrationPromise::Private(__func__);
    350 
    351  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    352      __func__,
    353      [self, promise,
    354       newestWorkerScriptUrl = nsCString(aNewestWorkerScriptUrl)]() mutable {
    355        auto scopeExit = MakeScopeExit(
    356            [&] { promise->Reject(NS_ERROR_DOM_INVALID_STATE_ERR, __func__); });
    357 
    358        // Get the delay value for the update
    359        NS_ENSURE_TRUE_VOID(self->mReg);
    360        uint32_t delay = self->mReg->GetUpdateDelay(false);
    361 
    362        // If the delay value does not equal to 0, create a timer and a timer
    363        // callback to perform the delayed update. Otherwise, update directly.
    364        if (delay) {
    365          if (self->mDelayedUpdate) {
    366            // NOTE: if we `ChainTo(),` there will ultimately be a single
    367            // update, and this update will resolve all promises that were
    368            // issued while the update's timer was ticking down.
    369            self->mDelayedUpdate->ChainTo(std::move(promise));
    370 
    371            // Use the "newest newest worker"'s script URL.
    372            self->mDelayedUpdate->SetNewestWorkerScriptUrl(
    373                std::move(newestWorkerScriptUrl));
    374          } else {
    375            RefPtr<ServiceWorkerRegistrationProxy::DelayedUpdate> du =
    376                new ServiceWorkerRegistrationProxy::DelayedUpdate(
    377                    std::move(self), std::move(promise),
    378                    std::move(newestWorkerScriptUrl), delay);
    379          }
    380        } else {
    381          RefPtr<ServiceWorkerManager> swm =
    382              ServiceWorkerManager::GetInstance();
    383          NS_ENSURE_TRUE_VOID(swm);
    384 
    385          RefPtr<UpdateCallback> cb = new UpdateCallback(std::move(promise));
    386          swm->Update(self->mListeningClientInfo, self->mReg->Principal(),
    387                      self->mReg->Scope(), std::move(newestWorkerScriptUrl),
    388                      cb);
    389        }
    390        scopeExit.release();
    391      });
    392 
    393  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    394 
    395  return promise;
    396 }
    397 
    398 RefPtr<GenericPromise>
    399 ServiceWorkerRegistrationProxy::SetNavigationPreloadEnabled(
    400    const bool& aEnabled) {
    401  AssertIsOnBackgroundThread();
    402 
    403  RefPtr<ServiceWorkerRegistrationProxy> self = this;
    404  RefPtr<GenericPromise::Private> promise =
    405      new GenericPromise::Private(__func__);
    406 
    407  nsCOMPtr<nsIRunnable> r =
    408      NS_NewRunnableFunction(__func__, [aEnabled, self, promise]() mutable {
    409        nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR;
    410        auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); });
    411 
    412        NS_ENSURE_TRUE_VOID(self->mReg);
    413        NS_ENSURE_TRUE_VOID(self->mReg->GetActive());
    414 
    415        auto reg = self->mReg;
    416        reg->SetNavigationPreloadEnabled(aEnabled);
    417 
    418        RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    419        NS_ENSURE_TRUE_VOID(swm);
    420        swm->StoreRegistration(reg->Principal(), reg);
    421 
    422        scopeExit.release();
    423 
    424        promise->Resolve(true, __func__);
    425      });
    426 
    427  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    428 
    429  return promise;
    430 }
    431 
    432 RefPtr<GenericPromise>
    433 ServiceWorkerRegistrationProxy::SetNavigationPreloadHeader(
    434    const nsACString& aHeader) {
    435  AssertIsOnBackgroundThread();
    436 
    437  RefPtr<ServiceWorkerRegistrationProxy> self = this;
    438  RefPtr<GenericPromise::Private> promise =
    439      new GenericPromise::Private(__func__);
    440 
    441  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    442      __func__, [aHeader = nsCString(aHeader), self, promise]() mutable {
    443        nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR;
    444        auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); });
    445 
    446        NS_ENSURE_TRUE_VOID(self->mReg);
    447        NS_ENSURE_TRUE_VOID(self->mReg->GetActive());
    448 
    449        auto reg = self->mReg;
    450        reg->SetNavigationPreloadHeader(aHeader);
    451 
    452        RefPtr<ServiceWorkerManager> swm = ServiceWorkerManager::GetInstance();
    453        NS_ENSURE_TRUE_VOID(swm);
    454        swm->StoreRegistration(reg->Principal(), reg);
    455 
    456        scopeExit.release();
    457 
    458        promise->Resolve(true, __func__);
    459      });
    460 
    461  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    462 
    463  return promise;
    464 }
    465 
    466 RefPtr<NavigationPreloadStatePromise>
    467 ServiceWorkerRegistrationProxy::GetNavigationPreloadState() {
    468  AssertIsOnBackgroundThread();
    469 
    470  RefPtr<ServiceWorkerRegistrationProxy> self = this;
    471  RefPtr<NavigationPreloadStatePromise::Private> promise =
    472      new NavigationPreloadStatePromise::Private(__func__);
    473 
    474  nsCOMPtr<nsIRunnable> r =
    475      NS_NewRunnableFunction(__func__, [self, promise]() mutable {
    476        nsresult rv = NS_ERROR_DOM_INVALID_STATE_ERR;
    477        auto scopeExit = MakeScopeExit([&] { promise->Reject(rv, __func__); });
    478 
    479        NS_ENSURE_TRUE_VOID(self->mReg);
    480        scopeExit.release();
    481 
    482        promise->Resolve(self->mReg->GetNavigationPreloadState(), __func__);
    483      });
    484 
    485  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    486 
    487  return promise;
    488 }
    489 
    490 RefPtr<NotificationsPromise> ServiceWorkerRegistrationProxy::GetNotifications(
    491    const nsAString& aTag) {
    492  AssertIsOnBackgroundThread();
    493 
    494  RefPtr<ServiceWorkerRegistrationProxy> self = this;
    495  return InvokeAsync(
    496      GetMainThreadSerialEventTarget(), __func__,
    497      [self, tag = nsString(aTag)]() {
    498        Result<nsCOMPtr<nsIPrincipal>, nsresult> principalResult =
    499            self->mListeningClientInfo.GetPrincipal();
    500        if (principalResult.isErr()) {
    501          return NotificationsPromise::CreateAndReject(
    502              NS_ERROR_DOM_INVALID_STATE_ERR, __func__);
    503        }
    504 
    505        nsCOMPtr<nsIPrincipal> principal = principalResult.unwrap();
    506        nsString origin;
    507        GetOrigin(principal, origin);
    508 
    509        RefPtr<NotificationsPromise> promise = GetStoredNotificationsForScope(
    510            principal, self->mDescriptor.Scope(), tag);
    511        return promise;
    512      });
    513 }
    514 
    515 }  // namespace mozilla::dom