tor-browser

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

ServiceWorkerOp.cpp (70181B)


      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 "ServiceWorkerOp.h"
      8 
      9 #include <utility>
     10 
     11 #include "ServiceWorkerCloneData.h"
     12 #include "ServiceWorkerOpPromise.h"
     13 #include "ServiceWorkerShutdownState.h"
     14 #include "js/Exception.h"  // JS::ExceptionStack, JS::StealPendingExceptionStack
     15 #include "jsapi.h"
     16 #include "mozilla/Assertions.h"
     17 #include "mozilla/CycleCollectedJSContext.h"
     18 #include "mozilla/DebugOnly.h"
     19 #include "mozilla/ErrorResult.h"
     20 #include "mozilla/OwningNonNull.h"
     21 #include "mozilla/SchedulerGroup.h"
     22 #include "mozilla/ScopeExit.h"
     23 #include "mozilla/dom/BindingDeclarations.h"
     24 #include "mozilla/dom/Client.h"
     25 #include "mozilla/dom/CookieStore.h"
     26 #include "mozilla/dom/ExtendableCookieChangeEvent.h"
     27 #include "mozilla/dom/ExtendableMessageEventBinding.h"
     28 #include "mozilla/dom/FetchEventBinding.h"
     29 #include "mozilla/dom/FetchEventOpProxyChild.h"
     30 #include "mozilla/dom/InternalHeaders.h"
     31 #include "mozilla/dom/InternalRequest.h"
     32 #include "mozilla/dom/InternalResponse.h"
     33 #include "mozilla/dom/Notification.h"
     34 #include "mozilla/dom/NotificationEvent.h"
     35 #include "mozilla/dom/NotificationEventBinding.h"
     36 #include "mozilla/dom/PerformanceStorage.h"
     37 #include "mozilla/dom/PerformanceTiming.h"
     38 #include "mozilla/dom/PushEventBinding.h"
     39 #include "mozilla/dom/PushSubscriptionChangeEvent.h"
     40 #include "mozilla/dom/PushSubscriptionChangeEventBinding.h"
     41 #include "mozilla/dom/RemoteWorkerChild.h"
     42 #include "mozilla/dom/RemoteWorkerNonLifeCycleOpControllerChild.h"
     43 #include "mozilla/dom/RemoteWorkerService.h"
     44 #include "mozilla/dom/Request.h"
     45 #include "mozilla/dom/Response.h"
     46 #include "mozilla/dom/RootedDictionary.h"
     47 #include "mozilla/dom/SafeRefPtr.h"
     48 #include "mozilla/dom/ServiceWorker.h"
     49 #include "mozilla/dom/ServiceWorkerBinding.h"
     50 #include "mozilla/dom/ServiceWorkerGlobalScopeBinding.h"
     51 #include "mozilla/dom/WorkerCommon.h"
     52 #include "mozilla/dom/WorkerRef.h"
     53 #include "mozilla/dom/WorkerScope.h"
     54 #include "mozilla/extensions/ExtensionBrowser.h"
     55 #include "mozilla/ipc/IPCStreamUtils.h"
     56 #include "nsCOMPtr.h"
     57 #include "nsContentUtils.h"
     58 #include "nsDebug.h"
     59 #include "nsError.h"
     60 #include "nsINamed.h"
     61 #include "nsIPushErrorReporter.h"
     62 #include "nsISupportsImpl.h"
     63 #include "nsITimer.h"
     64 #include "nsIURI.h"
     65 #include "nsServiceManagerUtils.h"
     66 #include "nsTArray.h"
     67 #include "nsThreadUtils.h"
     68 
     69 namespace mozilla::dom {
     70 
     71 using remoteworker::Canceled;
     72 using remoteworker::Killed;
     73 using remoteworker::Pending;
     74 using remoteworker::Running;
     75 
     76 namespace {
     77 
     78 class ExtendableEventKeepAliveHandler final
     79    : public ExtendableEvent::ExtensionsHandler,
     80      public PromiseNativeHandler {
     81 public:
     82  NS_DECL_ISUPPORTS
     83 
     84  static RefPtr<ExtendableEventKeepAliveHandler> Create(
     85      RefPtr<ExtendableEventCallback> aCallback) {
     86    MOZ_ASSERT(IsCurrentThreadRunningWorker());
     87 
     88    RefPtr<ExtendableEventKeepAliveHandler> self =
     89        new ExtendableEventKeepAliveHandler(std::move(aCallback));
     90 
     91    self->mWorkerRef = StrongWorkerRef::Create(
     92        GetCurrentThreadWorkerPrivate(), "ExtendableEventKeepAliveHandler",
     93        [self]() { self->Cleanup(); });
     94 
     95    if (NS_WARN_IF(!self->mWorkerRef)) {
     96      return nullptr;
     97    }
     98 
     99    return self;
    100  }
    101 
    102  /**
    103   * ExtendableEvent::ExtensionsHandler interface
    104   */
    105  bool WaitOnPromise(Promise& aPromise) override {
    106    if (!mAcceptingPromises) {
    107      MOZ_ASSERT(!GetDispatchFlag());
    108      MOZ_ASSERT(!mSelfRef, "We shouldn't be holding a self reference!");
    109      return false;
    110    }
    111 
    112    if (!mSelfRef) {
    113      MOZ_ASSERT(!mPendingPromisesCount);
    114      mSelfRef = this;
    115    }
    116 
    117    ++mPendingPromisesCount;
    118    aPromise.AppendNativeHandler(this);
    119 
    120    return true;
    121  }
    122 
    123  /**
    124   * PromiseNativeHandler interface
    125   */
    126  void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
    127                        ErrorResult& aRv) override {
    128    RemovePromise(Resolved);
    129  }
    130 
    131  void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
    132                        ErrorResult& aRv) override {
    133    RemovePromise(Rejected);
    134  }
    135 
    136  void MaybeDone() {
    137    MOZ_ASSERT(IsCurrentThreadRunningWorker());
    138    MOZ_ASSERT(!GetDispatchFlag());
    139 
    140    if (mPendingPromisesCount) {
    141      return;
    142    }
    143 
    144    if (mCallback) {
    145      mCallback->FinishedWithResult(mRejected ? Rejected : Resolved);
    146      mCallback = nullptr;
    147    }
    148 
    149    Cleanup();
    150  }
    151 
    152 private:
    153  /**
    154   * This class is useful for the case where pending microtasks will continue
    155   * extending the event, which means that the event is not "done." For example:
    156   *
    157   * // `e` is an ExtendableEvent, `p` is a Promise
    158   * e.waitUntil(p);
    159   * p.then(() => e.waitUntil(otherPromise));
    160   */
    161  class MaybeDoneRunner : public MicroTaskRunnable {
    162   public:
    163    explicit MaybeDoneRunner(RefPtr<ExtendableEventKeepAliveHandler> aHandler)
    164        : mHandler(std::move(aHandler)) {}
    165 
    166    void Run(AutoSlowOperation& /* unused */) override {
    167      mHandler->MaybeDone();
    168    }
    169 
    170   private:
    171    RefPtr<ExtendableEventKeepAliveHandler> mHandler;
    172  };
    173 
    174  explicit ExtendableEventKeepAliveHandler(
    175      RefPtr<ExtendableEventCallback> aCallback)
    176      : mCallback(std::move(aCallback)) {}
    177 
    178  ~ExtendableEventKeepAliveHandler() { Cleanup(); }
    179 
    180  void Cleanup() {
    181    MOZ_ASSERT(IsCurrentThreadRunningWorker());
    182 
    183    if (mCallback) {
    184      mCallback->FinishedWithResult(Rejected);
    185    }
    186 
    187    mSelfRef = nullptr;
    188    mWorkerRef = nullptr;
    189    mCallback = nullptr;
    190    mAcceptingPromises = false;
    191  }
    192 
    193  void RemovePromise(ExtendableEventResult aResult) {
    194    MOZ_ASSERT(IsCurrentThreadRunningWorker());
    195    MOZ_DIAGNOSTIC_ASSERT(mPendingPromisesCount > 0);
    196 
    197    // NOTE: mSelfRef can be nullptr here if MaybeCleanup() was just called
    198    // before a promise settled. This can happen, for example, if the worker
    199    // thread is being terminated for running too long, browser shutdown, etc.
    200 
    201    mRejected |= (aResult == Rejected);
    202 
    203    --mPendingPromisesCount;
    204    if (mPendingPromisesCount || GetDispatchFlag()) {
    205      return;
    206    }
    207 
    208    CycleCollectedJSContext* cx = CycleCollectedJSContext::Get();
    209    MOZ_ASSERT(cx);
    210 
    211    RefPtr<MaybeDoneRunner> r = new MaybeDoneRunner(this);
    212    cx->DispatchToMicroTask(r.forget());
    213  }
    214 
    215  /**
    216   * We start holding a self reference when the first extension promise is
    217   * added, and this reference is released when the last promise settles or
    218   * when the worker is shutting down.
    219   *
    220   * This is needed in the case that we're waiting indefinitely on a to-be-GC'ed
    221   * promise that's no longer reachable and will never be settled.
    222   */
    223  RefPtr<ExtendableEventKeepAliveHandler> mSelfRef;
    224 
    225  RefPtr<StrongWorkerRef> mWorkerRef;
    226 
    227  RefPtr<ExtendableEventCallback> mCallback;
    228 
    229  uint32_t mPendingPromisesCount = 0;
    230 
    231  bool mRejected = false;
    232  bool mAcceptingPromises = true;
    233 };
    234 
    235 NS_IMPL_ISUPPORTS0(ExtendableEventKeepAliveHandler)
    236 
    237 nsresult DispatchExtendableEventOnWorkerScope(
    238    JSContext* aCx, WorkerGlobalScope* aWorkerScope, ExtendableEvent* aEvent,
    239    RefPtr<ExtendableEventCallback> aCallback) {
    240  MOZ_ASSERT(aCx);
    241  MOZ_ASSERT(aWorkerScope);
    242  MOZ_ASSERT(aEvent);
    243 
    244  nsCOMPtr<nsIGlobalObject> globalObject = aWorkerScope;
    245  WidgetEvent* internalEvent = aEvent->WidgetEventPtr();
    246 
    247  RefPtr<ExtendableEventKeepAliveHandler> keepAliveHandler =
    248      ExtendableEventKeepAliveHandler::Create(std::move(aCallback));
    249  if (NS_WARN_IF(!keepAliveHandler)) {
    250    return NS_ERROR_FAILURE;
    251  }
    252 
    253  // This must be always set *before* dispatching the event, otherwise
    254  // waitUntil() calls will fail.
    255  aEvent->SetKeepAliveHandler(keepAliveHandler);
    256 
    257  ErrorResult result;
    258  aWorkerScope->DispatchEvent(*aEvent, result);
    259  if (NS_WARN_IF(result.Failed())) {
    260    result.SuppressException();
    261    return NS_ERROR_FAILURE;
    262  }
    263 
    264  keepAliveHandler->MaybeDone();
    265 
    266  // We don't block the event when getting an exception but still report the
    267  // error message. NOTE: this will not stop the event.
    268  if (internalEvent->mFlags.mExceptionWasRaised) {
    269    return NS_ERROR_XPC_JS_THREW_EXCEPTION;
    270  }
    271 
    272  return NS_OK;
    273 }
    274 
    275 bool DispatchFailed(nsresult aStatus) {
    276  return NS_FAILED(aStatus) && aStatus != NS_ERROR_XPC_JS_THREW_EXCEPTION;
    277 }
    278 
    279 }  // anonymous namespace
    280 
    281 class ServiceWorkerOp::ServiceWorkerOpRunnable final
    282    : public WorkerDebuggeeRunnable {
    283 public:
    284  NS_DECL_ISUPPORTS_INHERITED
    285 
    286  ServiceWorkerOpRunnable(RefPtr<ServiceWorkerOp> aOwner,
    287                          WorkerPrivate* aWorkerPrivate)
    288      : WorkerDebuggeeRunnable("ServiceWorkerOpRunnable"),
    289        mOwner(std::move(aOwner)) {
    290    MOZ_ASSERT(mOwner);
    291    MOZ_ASSERT(aWorkerPrivate);
    292  }
    293 
    294 private:
    295  ~ServiceWorkerOpRunnable() = default;
    296 
    297  bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    298    MOZ_ASSERT(aWorkerPrivate);
    299    aWorkerPrivate->AssertIsOnWorkerThread();
    300    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    301    MOZ_ASSERT(mOwner);
    302 
    303    // GlobalScope could be nullptr here that OOM issue causes GlobalScope
    304    // creation fail.
    305    if (!aWorkerPrivate->GlobalScope() ||
    306        aWorkerPrivate->GlobalScope()->IsDying()) {
    307      (void)Cancel();
    308      return true;
    309    }
    310 
    311    bool rv = mOwner->Exec(aCx, aWorkerPrivate);
    312    (void)NS_WARN_IF(!rv);
    313    mOwner = nullptr;
    314 
    315    return rv;
    316  }
    317 
    318  // Silent PreDispatch and PostDispatch, since ServiceWorkerOpRunnable can be
    319  // from the main thread or from the worker thread.
    320  bool PreDispatch(WorkerPrivate* WorkerPrivate) override { return true; }
    321  void PostDispatch(WorkerPrivate* WorkerPrivate,
    322                    bool aDispatchResult) override {}
    323 
    324  nsresult Cancel() override {
    325    MOZ_ASSERT(mOwner);
    326 
    327    mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
    328    mOwner = nullptr;
    329 
    330    return NS_OK;
    331  }
    332 
    333  RefPtr<ServiceWorkerOp> mOwner;
    334 };
    335 
    336 NS_IMPL_ISUPPORTS_INHERITED0(ServiceWorkerOp::ServiceWorkerOpRunnable,
    337                             WorkerThreadRunnable)
    338 
    339 bool ServiceWorkerOp::MaybeStart(RemoteWorkerChild* aOwner,
    340                                 RemoteWorkerState& aState) {
    341  MOZ_ASSERT(!mStarted);
    342  MOZ_ASSERT(aOwner);
    343  MOZ_ASSERT(aOwner->GetActorEventTarget()->IsOnCurrentThread());
    344 
    345  auto launcherData = aOwner->mLauncherData.Access();
    346 
    347  if (NS_WARN_IF(!aOwner->CanSend())) {
    348    RejectAll(NS_ERROR_DOM_ABORT_ERR);
    349    mStarted = true;
    350    return true;
    351  }
    352 
    353  // Allow termination to happen while the Service Worker is initializing.
    354  if (aState.is<Pending>() && !IsTerminationOp()) {
    355    return false;
    356  }
    357 
    358  if (NS_WARN_IF(aState.is<Canceled>()) || NS_WARN_IF(aState.is<Killed>())) {
    359    RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
    360    mStarted = true;
    361    return true;
    362  }
    363 
    364  MOZ_ASSERT(aState.is<Running>() || IsTerminationOp());
    365 
    366  RefPtr<ServiceWorkerOp> self = this;
    367 
    368  if (IsTerminationOp()) {
    369    aOwner->GetTerminationPromise()->Then(
    370        GetCurrentSerialEventTarget(), __func__,
    371        [self](
    372            const GenericNonExclusivePromise::ResolveOrRejectValue& aResult) {
    373          MaybeReportServiceWorkerShutdownProgress(self->mArgs, true);
    374 
    375          MOZ_ASSERT(!self->mPromiseHolder.IsEmpty());
    376 
    377          if (NS_WARN_IF(aResult.IsReject())) {
    378            self->mPromiseHolder.Reject(aResult.RejectValue(), __func__);
    379            return;
    380          }
    381 
    382          self->mPromiseHolder.Resolve(NS_OK, __func__);
    383        });
    384  }
    385 
    386  // NewRunnableMethod doesn't work here because the template does not appear to
    387  // be able to deal with the owner argument having storage as a RefPtr but
    388  // with the method taking a RefPtr&.
    389  RefPtr<RemoteWorkerChild> owner = aOwner;
    390  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    391      __func__, [self = std::move(self), owner = std::move(owner)]() mutable {
    392        self->StartOnMainThread(owner);
    393      });
    394 
    395  mStarted = true;
    396 
    397  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    398  return true;
    399 }
    400 
    401 void ServiceWorkerOp::StartOnMainThread(RefPtr<RemoteWorkerChild>& aOwner) {
    402  AssertIsOnMainThread();
    403  MaybeReportServiceWorkerShutdownProgress(mArgs);
    404 
    405  {
    406    auto lock = aOwner->mState.Lock();
    407 
    408    if (NS_WARN_IF(!lock->is<Running>() && !IsTerminationOp())) {
    409      RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
    410      return;
    411    }
    412  }
    413 
    414  if (IsTerminationOp()) {
    415    aOwner->CloseWorkerOnMainThread();
    416  } else {
    417    auto lock = aOwner->mState.Lock();
    418    MOZ_ASSERT(lock->is<Running>());
    419 
    420    RefPtr<WorkerThreadRunnable> workerRunnable =
    421        GetRunnable(lock->as<Running>().mWorkerPrivate);
    422 
    423    if (NS_WARN_IF(
    424            !workerRunnable->Dispatch(lock->as<Running>().mWorkerPrivate))) {
    425      RejectAll(NS_ERROR_FAILURE);
    426    }
    427  }
    428 }
    429 
    430 void ServiceWorkerOp::Start(RemoteWorkerNonLifeCycleOpControllerChild* aOwner,
    431                            RemoteWorkerState& aState) {
    432  MOZ_ASSERT(!mStarted);
    433  MOZ_ASSERT(aOwner);
    434 
    435  if (NS_WARN_IF(!aOwner->CanSend())) {
    436    RejectAll(NS_ERROR_DOM_ABORT_ERR);
    437    mStarted = true;
    438    return;
    439  }
    440 
    441  // NonLifeCycle related operations would never start at Pending state.
    442  MOZ_ASSERT(!aState.is<Pending>());
    443 
    444  if (NS_WARN_IF(aState.is<Canceled>()) || NS_WARN_IF(aState.is<Killed>())) {
    445    RejectAll(NS_ERROR_DOM_INVALID_STATE_ERR);
    446    mStarted = true;
    447    return;
    448  }
    449 
    450  MOZ_ASSERT(aState.is<Running>());
    451 
    452  WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    453 
    454  MOZ_ASSERT_DEBUG_OR_FUZZING(workerPrivate);
    455 
    456  RefPtr<WorkerThreadRunnable> workerRunnable = GetRunnable(workerPrivate);
    457 
    458  if (NS_WARN_IF(!workerRunnable->Dispatch(workerPrivate))) {
    459    RejectAll(NS_ERROR_FAILURE);
    460  }
    461 
    462  mStarted = true;
    463 }
    464 
    465 void ServiceWorkerOp::Cancel() { RejectAll(NS_ERROR_DOM_ABORT_ERR); }
    466 
    467 ServiceWorkerOp::ServiceWorkerOp(
    468    ServiceWorkerOpArgs&& aArgs,
    469    std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
    470    : mArgs(std::move(aArgs)) {
    471  // Can be on the Worker Launcher thread for Terminate operations or on the
    472  // Worker thread for other operations
    473  RefPtr<ServiceWorkerOpPromise> promise = mPromiseHolder.Ensure(__func__);
    474 
    475  promise->Then(
    476      GetCurrentSerialEventTarget(), __func__,
    477      [callback = std::move(aCallback)](
    478          ServiceWorkerOpPromise::ResolveOrRejectValue&& aResult) mutable {
    479        if (NS_WARN_IF(aResult.IsReject())) {
    480          MOZ_ASSERT(NS_FAILED(aResult.RejectValue()));
    481          callback(aResult.RejectValue());
    482          return;
    483        }
    484 
    485        callback(aResult.ResolveValue());
    486      });
    487 }
    488 
    489 ServiceWorkerOp::~ServiceWorkerOp() {
    490  (void)NS_WARN_IF(!mPromiseHolder.IsEmpty());
    491  mPromiseHolder.RejectIfExists(NS_ERROR_DOM_ABORT_ERR, __func__);
    492 }
    493 
    494 bool ServiceWorkerOp::Started() const {
    495  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
    496  return mStarted;
    497 }
    498 
    499 bool ServiceWorkerOp::IsTerminationOp() const {
    500  return mArgs.type() ==
    501         ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs;
    502 }
    503 
    504 RefPtr<WorkerThreadRunnable> ServiceWorkerOp::GetRunnable(
    505    WorkerPrivate* aWorkerPrivate) {
    506  MOZ_ASSERT(aWorkerPrivate);
    507 
    508  return new ServiceWorkerOpRunnable(this, aWorkerPrivate);
    509 }
    510 
    511 void ServiceWorkerOp::RejectAll(nsresult aStatus) {
    512  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
    513  mPromiseHolder.Reject(aStatus, __func__);
    514 }
    515 
    516 class CheckScriptEvaluationOp final : public ServiceWorkerOp {
    517  using ServiceWorkerOp::ServiceWorkerOp;
    518 
    519 public:
    520  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CheckScriptEvaluationOp, override)
    521 
    522 private:
    523  ~CheckScriptEvaluationOp() = default;
    524 
    525  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    526    MOZ_ASSERT(aWorkerPrivate);
    527    aWorkerPrivate->AssertIsOnWorkerThread();
    528    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    529    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
    530 
    531    ServiceWorkerCheckScriptEvaluationOpResult result;
    532    result.workerScriptExecutedSuccessfully() =
    533        aWorkerPrivate->WorkerScriptExecutedSuccessfully();
    534    result.fetchHandlerWasAdded() = aWorkerPrivate->FetchHandlerWasAdded();
    535 
    536    mPromiseHolder.Resolve(result, __func__);
    537 
    538    return true;
    539  }
    540 };
    541 
    542 class TerminateServiceWorkerOp final : public ServiceWorkerOp {
    543  using ServiceWorkerOp::ServiceWorkerOp;
    544 
    545 public:
    546  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(TerminateServiceWorkerOp, override)
    547 
    548 private:
    549  ~TerminateServiceWorkerOp() = default;
    550 
    551  bool Exec(JSContext*, WorkerPrivate*) override {
    552    MOZ_ASSERT_UNREACHABLE(
    553        "Worker termination should be handled in "
    554        "`ServiceWorkerOp::MaybeStart()`");
    555 
    556    return false;
    557  }
    558 };
    559 
    560 class UpdateServiceWorkerStateOp final : public ServiceWorkerOp {
    561  using ServiceWorkerOp::ServiceWorkerOp;
    562 
    563 public:
    564  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateServiceWorkerStateOp, override);
    565 
    566 private:
    567  class UpdateStateOpRunnable final : public WorkerControlRunnable {
    568   public:
    569    NS_DECL_ISUPPORTS_INHERITED
    570 
    571    UpdateStateOpRunnable(RefPtr<UpdateServiceWorkerStateOp> aOwner,
    572                          WorkerPrivate* aWorkerPrivate)
    573        : WorkerControlRunnable("UpdateStateOpRunnable"),
    574          mOwner(std::move(aOwner)) {
    575      MOZ_ASSERT(mOwner);
    576      MOZ_ASSERT(aWorkerPrivate);
    577      aWorkerPrivate->AssertIsOnWorkerThread();
    578    }
    579 
    580    virtual bool PreDispatch(WorkerPrivate* aWorkerPrivate) override {
    581      aWorkerPrivate->AssertIsOnWorkerThread();
    582      return true;
    583    }
    584 
    585    virtual void PostDispatch(WorkerPrivate* aWorkerPrivate,
    586                              bool aDispatchResult) override {
    587      aWorkerPrivate->AssertIsOnWorkerThread();
    588    }
    589 
    590   private:
    591    ~UpdateStateOpRunnable() = default;
    592 
    593    bool WorkerRun(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    594      MOZ_ASSERT(aWorkerPrivate);
    595      aWorkerPrivate->AssertIsOnWorkerThread();
    596      MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    597 
    598      if (mOwner) {
    599        (void)mOwner->Exec(aCx, aWorkerPrivate);
    600        mOwner = nullptr;
    601      }
    602 
    603      return true;
    604    }
    605 
    606    nsresult Cancel() override {
    607      MOZ_ASSERT(mOwner);
    608 
    609      mOwner->RejectAll(NS_ERROR_DOM_ABORT_ERR);
    610      mOwner = nullptr;
    611 
    612      return NS_OK;
    613    }
    614 
    615    RefPtr<UpdateServiceWorkerStateOp> mOwner;
    616  };
    617 
    618  ~UpdateServiceWorkerStateOp() = default;
    619 
    620  RefPtr<WorkerThreadRunnable> GetRunnable(
    621      WorkerPrivate* aWorkerPrivate) override {
    622    MOZ_ASSERT(aWorkerPrivate);
    623    aWorkerPrivate->IsOnWorkerThread();
    624    MOZ_ASSERT(mArgs.type() ==
    625               ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs);
    626 
    627    return new UpdateStateOpRunnable(this, aWorkerPrivate);
    628  }
    629 
    630  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    631    MOZ_ASSERT(aWorkerPrivate);
    632    aWorkerPrivate->AssertIsOnWorkerThread();
    633    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    634    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
    635 
    636    ServiceWorkerState state =
    637        mArgs.get_ServiceWorkerUpdateStateOpArgs().state();
    638    aWorkerPrivate->UpdateServiceWorkerState(state);
    639 
    640    mPromiseHolder.Resolve(NS_OK, __func__);
    641 
    642    return true;
    643  }
    644 };
    645 
    646 NS_IMPL_ISUPPORTS_INHERITED0(UpdateServiceWorkerStateOp::UpdateStateOpRunnable,
    647                             WorkerControlRunnable)
    648 
    649 void ExtendableEventOp::FinishedWithResult(ExtendableEventResult aResult) {
    650  MOZ_ASSERT(IsCurrentThreadRunningWorker());
    651  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
    652 
    653  mPromiseHolder.Resolve(aResult == Resolved ? NS_OK : NS_ERROR_FAILURE,
    654                         __func__);
    655 }
    656 
    657 class LifeCycleEventOp final : public ExtendableEventOp {
    658  using ExtendableEventOp::ExtendableEventOp;
    659 
    660 public:
    661  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(LifeCycleEventOp, override)
    662 
    663 private:
    664  ~LifeCycleEventOp() = default;
    665 
    666  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    667    MOZ_ASSERT(aWorkerPrivate);
    668    aWorkerPrivate->AssertIsOnWorkerThread();
    669    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    670    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
    671 
    672    RefPtr<ExtendableEvent> event;
    673    RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
    674 
    675    const nsString& eventName =
    676        mArgs.get_ServiceWorkerLifeCycleEventOpArgs().eventName();
    677 
    678    if (eventName.EqualsASCII("install") || eventName.EqualsASCII("activate")) {
    679      ExtendableEventInit init;
    680      init.mBubbles = false;
    681      init.mCancelable = false;
    682      event = ExtendableEvent::Constructor(target, eventName, init);
    683    } else {
    684      MOZ_CRASH("Unexpected lifecycle event");
    685    }
    686 
    687    event->SetTrusted(true);
    688 
    689    nsresult rv = DispatchExtendableEventOnWorkerScope(
    690        aCx, aWorkerPrivate->GlobalScope(), event, this);
    691 
    692    if (NS_WARN_IF(DispatchFailed(rv))) {
    693      RejectAll(rv);
    694    }
    695 
    696    return !DispatchFailed(rv);
    697  }
    698 };
    699 
    700 class CookieChangeEventOp final : public ExtendableEventOp {
    701  using ExtendableEventOp::ExtendableEventOp;
    702 
    703 public:
    704  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CookieChangeEventOp, override)
    705 
    706 private:
    707  ~CookieChangeEventOp() = default;
    708 
    709  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    710    MOZ_ASSERT(aWorkerPrivate);
    711    aWorkerPrivate->AssertIsOnWorkerThread();
    712    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    713    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
    714 
    715    const ServiceWorkerCookieChangeEventOpArgs& args =
    716        mArgs.get_ServiceWorkerCookieChangeEventOpArgs();
    717 
    718    CookieListItem item;
    719    CookieStore::CookieStructToItem(args.cookie(), &item);
    720 
    721    GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
    722    nsCOMPtr<EventTarget> eventTarget =
    723        do_QueryInterface(globalObj.GetAsSupports());
    724    MOZ_ASSERT(eventTarget);
    725 
    726    RefPtr<ExtendableCookieChangeEvent> event;
    727 
    728    if (args.deleted()) {
    729      item.mValue.Reset();
    730      event = ExtendableCookieChangeEvent::CreateForDeletedCookie(eventTarget,
    731                                                                  item);
    732    } else {
    733      event = ExtendableCookieChangeEvent::CreateForChangedCookie(eventTarget,
    734                                                                  item);
    735    }
    736 
    737    MOZ_ASSERT(event);
    738 
    739    nsresult rv = DispatchExtendableEventOnWorkerScope(
    740        aCx, aWorkerPrivate->GlobalScope(), event, this);
    741 
    742    if (NS_WARN_IF(NS_FAILED(rv))) {
    743      return false;
    744    }
    745 
    746    return true;
    747  }
    748 };
    749 
    750 /**
    751 * PushEventOp
    752 */
    753 class PushEventOp final : public ExtendableEventOp {
    754  using ExtendableEventOp::ExtendableEventOp;
    755 
    756 public:
    757  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushEventOp, override)
    758 
    759 private:
    760  ~PushEventOp() = default;
    761 
    762  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    763    MOZ_ASSERT(aWorkerPrivate);
    764    aWorkerPrivate->AssertIsOnWorkerThread();
    765    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    766    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
    767 
    768    ErrorResult result;
    769 
    770    auto scopeExit = MakeScopeExit([&] {
    771      MOZ_ASSERT(result.Failed());
    772 
    773      RejectAll(result.StealNSResult());
    774      ReportError(aWorkerPrivate);
    775    });
    776 
    777    const ServiceWorkerPushEventOpArgs& args =
    778        mArgs.get_ServiceWorkerPushEventOpArgs();
    779 
    780    RootedDictionary<PushEventInit> pushEventInit(aCx);
    781 
    782    if (args.data().type() != OptionalPushData::Tvoid_t) {
    783      const auto& bytes = args.data().get_ArrayOfuint8_t();
    784      JSObject* data = Uint8Array::Create(aCx, bytes, result);
    785 
    786      // The ScopeExit above will deal with the exceptions (through
    787      // StealNSResult).
    788      result.WouldReportJSException();
    789      if (result.Failed()) {
    790        return false;
    791      }
    792 
    793      DebugOnly<bool> inited =
    794          pushEventInit.mData.Construct().SetAsArrayBufferView().Init(data);
    795      MOZ_ASSERT(inited);
    796    }
    797 
    798    pushEventInit.mBubbles = false;
    799    pushEventInit.mCancelable = false;
    800 
    801    GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
    802    RefPtr<PushEvent> pushEvent =
    803        PushEvent::Constructor(globalObj, u"push"_ns, pushEventInit, result);
    804 
    805    if (NS_WARN_IF(result.Failed())) {
    806      return false;
    807    }
    808 
    809    pushEvent->SetTrusted(true);
    810 
    811    scopeExit.release();
    812 
    813    nsresult rv = DispatchExtendableEventOnWorkerScope(
    814        aCx, aWorkerPrivate->GlobalScope(), pushEvent, this);
    815 
    816    if (NS_FAILED(rv)) {
    817      if (NS_WARN_IF(DispatchFailed(rv))) {
    818        RejectAll(rv);
    819      }
    820 
    821      // We don't cancel WorkerPrivate when catching an exception.
    822      ReportError(aWorkerPrivate,
    823                  nsIPushErrorReporter::DELIVERY_UNCAUGHT_EXCEPTION);
    824    }
    825 
    826    return !DispatchFailed(rv);
    827  }
    828 
    829  void FinishedWithResult(ExtendableEventResult aResult) override {
    830    MOZ_ASSERT(IsCurrentThreadRunningWorker());
    831 
    832    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
    833 
    834    if (aResult == Rejected) {
    835      ReportError(workerPrivate,
    836                  nsIPushErrorReporter::DELIVERY_UNHANDLED_REJECTION);
    837    }
    838 
    839    ExtendableEventOp::FinishedWithResult(aResult);
    840  }
    841 
    842  void ReportError(
    843      WorkerPrivate* aWorkerPrivate,
    844      uint16_t aError = nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) {
    845    MOZ_ASSERT(aWorkerPrivate);
    846    aWorkerPrivate->AssertIsOnWorkerThread();
    847    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    848 
    849    if (NS_WARN_IF(aError > nsIPushErrorReporter::DELIVERY_INTERNAL_ERROR) ||
    850        mArgs.get_ServiceWorkerPushEventOpArgs().messageId().IsEmpty()) {
    851      return;
    852    }
    853 
    854    nsString messageId = mArgs.get_ServiceWorkerPushEventOpArgs().messageId();
    855    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    856        __func__, [messageId = std::move(messageId), error = aError] {
    857          nsCOMPtr<nsIPushErrorReporter> reporter =
    858              do_GetService("@mozilla.org/push/Service;1");
    859 
    860          if (reporter) {
    861            nsresult rv = reporter->ReportDeliveryError(messageId, error);
    862            (void)NS_WARN_IF(NS_FAILED(rv));
    863          }
    864        });
    865 
    866    MOZ_ALWAYS_SUCCEEDS(aWorkerPrivate->DispatchToMainThread(r.forget()));
    867  }
    868 };
    869 
    870 class PushSubscriptionChangeEventOp final : public ExtendableEventOp {
    871  using ExtendableEventOp::ExtendableEventOp;
    872 
    873 public:
    874  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(PushSubscriptionChangeEventOp, override)
    875 
    876 private:
    877  ~PushSubscriptionChangeEventOp() = default;
    878 
    879  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
    880    MOZ_ASSERT(aWorkerPrivate);
    881    aWorkerPrivate->AssertIsOnWorkerThread();
    882    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    883    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
    884 
    885    RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
    886 
    887    ServiceWorkerPushSubscriptionChangeEventOpArgs& args =
    888        mArgs.get_ServiceWorkerPushSubscriptionChangeEventOpArgs();
    889 
    890    PushSubscriptionChangeEventInit init;
    891    init.mBubbles = false;
    892    init.mCancelable = false;
    893 
    894    if (args.oldSubscription()) {
    895      PushSubscriptionData oldSubscriptionData =
    896          args.oldSubscription().extract();
    897      RefPtr<PushSubscription> oldSubscription = new PushSubscription(
    898          target->GetParentObject(), oldSubscriptionData.endpoint(), u""_ns,
    899          Nullable<EpochTimeStamp>(),
    900          std::move(oldSubscriptionData.rawP256dhKey()),
    901          std::move(oldSubscriptionData.authSecret()),
    902          std::move(oldSubscriptionData.appServerKey()));
    903      init.mOldSubscription = oldSubscription.forget();
    904    }
    905 
    906    RefPtr<PushSubscriptionChangeEvent> event =
    907        PushSubscriptionChangeEvent::Constructor(
    908            target, u"pushsubscriptionchange"_ns, init);
    909    event->SetTrusted(true);
    910 
    911    nsresult rv = DispatchExtendableEventOnWorkerScope(
    912        aCx, aWorkerPrivate->GlobalScope(), event, this);
    913 
    914    if (NS_WARN_IF(DispatchFailed(rv))) {
    915      RejectAll(rv);
    916    }
    917 
    918    return !DispatchFailed(rv);
    919  }
    920 };
    921 
    922 class NotificationEventOp : public ExtendableEventOp,
    923                            public nsITimerCallback,
    924                            public nsINamed {
    925  using ExtendableEventOp::ExtendableEventOp;
    926 
    927 public:
    928  NS_DECL_THREADSAFE_ISUPPORTS
    929 
    930 private:
    931  ~NotificationEventOp() {
    932    MOZ_DIAGNOSTIC_ASSERT(!mTimer);
    933    MOZ_DIAGNOSTIC_ASSERT(!mWorkerRef);
    934  }
    935 
    936  void ClearWindowAllowed(WorkerPrivate* aWorkerPrivate) {
    937    MOZ_ASSERT(aWorkerPrivate);
    938    aWorkerPrivate->AssertIsOnWorkerThread();
    939    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
    940 
    941    if (!mTimer) {
    942      return;
    943    }
    944 
    945    // This might be executed after the global was unrooted, in which case
    946    // GlobalScope() will return null. Making the check here just to be safe.
    947    WorkerGlobalScope* globalScope = aWorkerPrivate->GlobalScope();
    948    if (!globalScope) {
    949      return;
    950    }
    951 
    952    globalScope->ConsumeWindowInteraction();
    953    mTimer->Cancel();
    954    mTimer = nullptr;
    955 
    956    mWorkerRef = nullptr;
    957  }
    958 
    959  void StartClearWindowTimer(WorkerPrivate* aWorkerPrivate) {
    960    MOZ_ASSERT(aWorkerPrivate);
    961    aWorkerPrivate->AssertIsOnWorkerThread();
    962    MOZ_ASSERT(!mTimer);
    963 
    964    nsresult rv;
    965    nsCOMPtr<nsITimer> timer =
    966        NS_NewTimer(aWorkerPrivate->ControlEventTarget());
    967    if (NS_WARN_IF(!timer)) {
    968      return;
    969    }
    970 
    971    MOZ_ASSERT(!mWorkerRef);
    972    RefPtr<NotificationEventOp> self = this;
    973    mWorkerRef = StrongWorkerRef::Create(
    974        aWorkerPrivate, "NotificationEventOp", [self = std::move(self)] {
    975          // We could try to hold the worker alive until the timer fires, but
    976          // other APIs are not likely to work in this partially shutdown state.
    977          // We might as well let the worker thread exit.
    978          self->ClearWindowAllowed(self->mWorkerRef->Private());
    979        });
    980 
    981    if (!mWorkerRef) {
    982      return;
    983    }
    984 
    985    aWorkerPrivate->GlobalScope()->AllowWindowInteraction();
    986    timer.swap(mTimer);
    987 
    988    // We swap first and then initialize the timer so that even if initializing
    989    // fails, we still clean the interaction count correctly.
    990    uint32_t delay =
    991        StaticPrefs::dom_webnotifications_disable_open_click_delay();
    992    rv = mTimer->InitWithCallback(this, delay, nsITimer::TYPE_ONE_SHOT);
    993 
    994    if (NS_WARN_IF(NS_FAILED(rv))) {
    995      ClearWindowAllowed(aWorkerPrivate);
    996      return;
    997    }
    998  }
    999 
   1000  // ExtendableEventOp interface
   1001  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
   1002    MOZ_ASSERT(aWorkerPrivate);
   1003    aWorkerPrivate->AssertIsOnWorkerThread();
   1004    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
   1005    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1006 
   1007    RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
   1008 
   1009    ServiceWorkerNotificationEventOpArgs& args =
   1010        mArgs.get_ServiceWorkerNotificationEventOpArgs();
   1011    bool isClick = args.type() ==
   1012                   ServiceWorkerNotificationEventOpArgs::
   1013                       TServiceWorkerNotificationClickEventOpArgs;
   1014    const IPCNotification& notification =
   1015        args.type() == ServiceWorkerNotificationEventOpArgs::
   1016                           TServiceWorkerNotificationClickEventOpArgs
   1017            ? args.get_ServiceWorkerNotificationClickEventOpArgs()
   1018                  .notification()
   1019            : args.get_ServiceWorkerNotificationCloseEventOpArgs()
   1020                  .notification();
   1021 
   1022    auto result = Notification::ConstructFromIPC(
   1023        aWorkerPrivate->GlobalScope(), notification,
   1024        NS_ConvertUTF8toUTF16(aWorkerPrivate->ServiceWorkerScope()));
   1025 
   1026    if (NS_WARN_IF(result.isErr())) {
   1027      return false;
   1028    }
   1029 
   1030    NotificationEventInit init;
   1031    init.mNotification = result.unwrap();
   1032    init.mBubbles = false;
   1033    init.mCancelable = false;
   1034    if (isClick) {
   1035      init.mAction =
   1036          args.get_ServiceWorkerNotificationClickEventOpArgs().action();
   1037    }
   1038 
   1039    RefPtr<NotificationEvent> notificationEvent =
   1040        NotificationEvent::Constructor(
   1041            target, isClick ? u"notificationclick"_ns : u"notificationclose"_ns,
   1042            init);
   1043 
   1044    notificationEvent->SetTrusted(true);
   1045 
   1046    if (isClick) {
   1047      StartClearWindowTimer(aWorkerPrivate);
   1048    }
   1049 
   1050    nsresult rv = DispatchExtendableEventOnWorkerScope(
   1051        aCx, aWorkerPrivate->GlobalScope(), notificationEvent, this);
   1052 
   1053    if (NS_WARN_IF(DispatchFailed(rv))) {
   1054      // This will reject mPromiseHolder.
   1055      FinishedWithResult(Rejected);
   1056    }
   1057 
   1058    return !DispatchFailed(rv);
   1059  }
   1060 
   1061  void FinishedWithResult(ExtendableEventResult aResult) override {
   1062    MOZ_ASSERT(IsCurrentThreadRunningWorker());
   1063 
   1064    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   1065    MOZ_ASSERT(workerPrivate);
   1066 
   1067    ClearWindowAllowed(workerPrivate);
   1068 
   1069    ExtendableEventOp::FinishedWithResult(aResult);
   1070  }
   1071 
   1072  // nsITimerCallback interface
   1073  NS_IMETHOD Notify(nsITimer* aTimer) override {
   1074    MOZ_DIAGNOSTIC_ASSERT(mTimer == aTimer);
   1075    WorkerPrivate* workerPrivate = GetCurrentThreadWorkerPrivate();
   1076    ClearWindowAllowed(workerPrivate);
   1077    return NS_OK;
   1078  }
   1079 
   1080  // nsINamed interface
   1081  NS_IMETHOD GetName(nsACString& aName) override {
   1082    aName.AssignLiteral("NotificationEventOp");
   1083    return NS_OK;
   1084  }
   1085 
   1086  nsCOMPtr<nsITimer> mTimer;
   1087  RefPtr<StrongWorkerRef> mWorkerRef;
   1088 };
   1089 
   1090 NS_IMPL_ISUPPORTS(NotificationEventOp, nsITimerCallback, nsINamed)
   1091 
   1092 class MessageEventOp final : public ExtendableEventOp {
   1093  using ExtendableEventOp::ExtendableEventOp;
   1094 
   1095 public:
   1096  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(MessageEventOp, override)
   1097 
   1098  MessageEventOp(ServiceWorkerOpArgs&& aArgs,
   1099                 std::function<void(const ServiceWorkerOpResult&)>&& aCallback)
   1100      : ExtendableEventOp(std::move(aArgs), std::move(aCallback)),
   1101        mData(new ServiceWorkerCloneData()) {
   1102    mData->CopyFromClonedMessageData(
   1103        mArgs.get_ServiceWorkerMessageEventOpArgs().clonedData());
   1104  }
   1105 
   1106 private:
   1107  ~MessageEventOp() = default;
   1108 
   1109  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
   1110    MOZ_ASSERT(aWorkerPrivate);
   1111    aWorkerPrivate->AssertIsOnWorkerThread();
   1112    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
   1113    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1114 
   1115    JS::Rooted<JS::Value> messageData(aCx);
   1116    nsCOMPtr<nsIGlobalObject> sgo = aWorkerPrivate->GlobalScope();
   1117    ErrorResult rv;
   1118    if (!mData->IsErrorMessageData()) {
   1119      mData->Read(aCx, &messageData, rv);
   1120    }
   1121 
   1122    // If mData is an error message data, then it means that it failed to
   1123    // serialize on the caller side because it contains a shared memory object.
   1124    // If deserialization fails, we will fire a messageerror event.
   1125    const bool deserializationFailed =
   1126        rv.Failed() || mData->IsErrorMessageData();
   1127 
   1128    Sequence<OwningNonNull<MessagePort>> ports;
   1129    if (!mData->TakeTransferredPortsAsSequence(ports)) {
   1130      RejectAll(NS_ERROR_FAILURE);
   1131      rv.SuppressException();
   1132      return false;
   1133    }
   1134 
   1135    RootedDictionary<ExtendableMessageEventInit> init(aCx);
   1136 
   1137    init.mBubbles = false;
   1138    init.mCancelable = false;
   1139 
   1140    // On a messageerror event, we disregard ports:
   1141    // https://w3c.github.io/ServiceWorker/#service-worker-postmessage
   1142    if (!deserializationFailed) {
   1143      init.mData = messageData;
   1144      init.mPorts = std::move(ports);
   1145    }
   1146 
   1147    PostMessageSource& ipcSource =
   1148        mArgs.get_ServiceWorkerMessageEventOpArgs().source();
   1149    nsCString originSource;
   1150    switch (ipcSource.type()) {
   1151      case PostMessageSource::TClientInfoAndState:
   1152        originSource = ipcSource.get_ClientInfoAndState().info().url();
   1153        init.mSource.SetValue().SetAsClient() =
   1154            new Client(sgo, ipcSource.get_ClientInfoAndState());
   1155        break;
   1156      case PostMessageSource::TIPCServiceWorkerDescriptor:
   1157        originSource = ipcSource.get_IPCServiceWorkerDescriptor().scriptURL();
   1158        init.mSource.SetValue().SetAsServiceWorker() = ServiceWorker::Create(
   1159            sgo, ServiceWorkerDescriptor(
   1160                     ipcSource.get_IPCServiceWorkerDescriptor()));
   1161        break;
   1162      default:
   1163        MOZ_ASSERT_UNREACHABLE("Unexpected source type");
   1164        return false;
   1165    }
   1166 
   1167    nsCOMPtr<nsIURI> url;
   1168    nsresult result = NS_NewURI(getter_AddRefs(url), originSource);
   1169    if (NS_WARN_IF(NS_FAILED(result))) {
   1170      RejectAll(result);
   1171      rv.SuppressException();
   1172      return false;
   1173    }
   1174 
   1175    OriginAttributes attrs;
   1176    nsCOMPtr<nsIPrincipal> principal =
   1177        BasePrincipal::CreateContentPrincipal(url, attrs);
   1178    if (!principal) {
   1179      return false;
   1180    }
   1181 
   1182    nsCString origin;
   1183    result = principal->GetOriginNoSuffix(origin);
   1184    if (NS_WARN_IF(NS_FAILED(result))) {
   1185      RejectAll(result);
   1186      rv.SuppressException();
   1187      return false;
   1188    }
   1189 
   1190    CopyUTF8toUTF16(origin, init.mOrigin);
   1191 
   1192    rv.SuppressException();
   1193    RefPtr<EventTarget> target = aWorkerPrivate->GlobalScope();
   1194    RefPtr<ExtendableMessageEvent> extendableEvent =
   1195        ExtendableMessageEvent::Constructor(
   1196            target, deserializationFailed ? u"messageerror"_ns : u"message"_ns,
   1197            init);
   1198 
   1199    extendableEvent->SetTrusted(true);
   1200 
   1201    nsresult rv2 = DispatchExtendableEventOnWorkerScope(
   1202        aCx, aWorkerPrivate->GlobalScope(), extendableEvent, this);
   1203 
   1204    if (NS_WARN_IF(DispatchFailed(rv2))) {
   1205      RejectAll(rv2);
   1206    }
   1207 
   1208    return !DispatchFailed(rv2);
   1209  }
   1210 
   1211  RefPtr<ServiceWorkerCloneData> mData;
   1212 };
   1213 
   1214 class UpdateIsOnContentBlockingAllowListOp final : public ExtendableEventOp {
   1215  using ExtendableEventOp::ExtendableEventOp;
   1216 
   1217 public:
   1218  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(UpdateIsOnContentBlockingAllowListOp,
   1219                                        override);
   1220 
   1221 private:
   1222  ~UpdateIsOnContentBlockingAllowListOp() = default;
   1223 
   1224  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
   1225    MOZ_ASSERT(aWorkerPrivate);
   1226    aWorkerPrivate->AssertIsOnWorkerThread();
   1227    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
   1228    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1229 
   1230    bool onContentBlockingAllowList =
   1231        mArgs.get_ServiceWorkerUpdateIsOnContentBlockingAllowListOpArgs()
   1232            .onContentBlockingAllowList();
   1233    aWorkerPrivate->UpdateIsOnContentBlockingAllowList(
   1234        onContentBlockingAllowList);
   1235 
   1236    return true;
   1237  }
   1238 };
   1239 
   1240 /**
   1241 * Used for ScopeExit-style network request cancelation in
   1242 * `ResolvedCallback()` (e.g. if `FetchEvent::RespondWith()` is resolved with
   1243 * a non-JS object).
   1244 */
   1245 class MOZ_STACK_CLASS FetchEventOp::AutoCancel {
   1246 public:
   1247  explicit AutoCancel(FetchEventOp* aOwner)
   1248      : mOwner(aOwner),
   1249        mLine(0),
   1250        mColumn(0),
   1251        mMessageName("InterceptionFailedWithURL"_ns) {
   1252    MOZ_ASSERT(IsCurrentThreadRunningWorker());
   1253    MOZ_ASSERT(mOwner);
   1254 
   1255    nsAutoString requestURL;
   1256    mOwner->GetRequestURL(requestURL);
   1257    mParams.AppendElement(requestURL);
   1258  }
   1259 
   1260  ~AutoCancel() {
   1261    if (mOwner) {
   1262      if (mSourceSpec.IsEmpty()) {
   1263        mOwner->AsyncLog(mMessageName, std::move(mParams));
   1264      } else {
   1265        mOwner->AsyncLog(mSourceSpec, mLine, mColumn, mMessageName,
   1266                         std::move(mParams));
   1267      }
   1268 
   1269      MOZ_ASSERT(!mOwner->mRespondWithPromiseHolder.IsEmpty());
   1270      mOwner->mHandled->MaybeRejectWithNetworkError("AutoCancel"_ns);
   1271      mOwner->mRespondWithPromiseHolder.Reject(
   1272          CancelInterceptionArgs(
   1273              NS_ERROR_INTERCEPTION_FAILED,
   1274              FetchEventTimeStamps(mOwner->mFetchHandlerStart,
   1275                                   mOwner->mFetchHandlerFinish)),
   1276          __func__);
   1277    }
   1278  }
   1279 
   1280  // This function steals the error message from a ErrorResult.
   1281  void SetCancelErrorResult(JSContext* aCx, ErrorResult& aRv) {
   1282    MOZ_DIAGNOSTIC_ASSERT(aRv.Failed());
   1283    MOZ_DIAGNOSTIC_ASSERT(!JS_IsExceptionPending(aCx));
   1284 
   1285    // Storing the error as exception in the JSContext.
   1286    if (!aRv.MaybeSetPendingException(aCx)) {
   1287      return;
   1288    }
   1289 
   1290    MOZ_ASSERT(!aRv.Failed());
   1291 
   1292    // Let's take the pending exception.
   1293    JS::ExceptionStack exnStack(aCx);
   1294    if (!JS::StealPendingExceptionStack(aCx, &exnStack)) {
   1295      return;
   1296    }
   1297 
   1298    // Converting the exception in a JS::ErrorReportBuilder.
   1299    JS::ErrorReportBuilder report(aCx);
   1300    if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) {
   1301      JS_ClearPendingException(aCx);
   1302      return;
   1303    }
   1304 
   1305    MOZ_ASSERT(mOwner);
   1306    MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
   1307    MOZ_ASSERT(mParams.Length() == 1);
   1308 
   1309    // Let's store the error message here.
   1310    mMessageName.Assign(report.toStringResult().c_str());
   1311    mParams.Clear();
   1312  }
   1313 
   1314  template <typename... Params>
   1315  void SetCancelMessage(const nsACString& aMessageName, Params&&... aParams) {
   1316    MOZ_ASSERT(mOwner);
   1317    MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
   1318    MOZ_ASSERT(mParams.Length() == 1);
   1319    mMessageName = aMessageName;
   1320    mParams.Clear();
   1321    StringArrayAppender::Append(mParams, sizeof...(Params),
   1322                                std::forward<Params>(aParams)...);
   1323  }
   1324 
   1325  template <typename... Params>
   1326  void SetCancelMessageAndLocation(const nsACString& aSourceSpec,
   1327                                   uint32_t aLine, uint32_t aColumn,
   1328                                   const nsACString& aMessageName,
   1329                                   Params&&... aParams) {
   1330    MOZ_ASSERT(mOwner);
   1331    MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL"));
   1332    MOZ_ASSERT(mParams.Length() == 1);
   1333 
   1334    mSourceSpec = aSourceSpec;
   1335    mLine = aLine;
   1336    mColumn = aColumn;
   1337 
   1338    mMessageName = aMessageName;
   1339    mParams.Clear();
   1340    StringArrayAppender::Append(mParams, sizeof...(Params),
   1341                                std::forward<Params>(aParams)...);
   1342  }
   1343 
   1344  void Reset() { mOwner = nullptr; }
   1345 
   1346 private:
   1347  FetchEventOp* MOZ_NON_OWNING_REF mOwner;
   1348  nsCString mSourceSpec;
   1349  uint32_t mLine;
   1350  uint32_t mColumn;
   1351  nsCString mMessageName;
   1352  nsTArray<nsString> mParams;
   1353 };
   1354 
   1355 NS_IMPL_ISUPPORTS0(FetchEventOp)
   1356 
   1357 void FetchEventOp::SetActor(RefPtr<FetchEventOpProxyChild> aActor) {
   1358  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
   1359  MOZ_ASSERT(!Started());
   1360  MOZ_ASSERT(!mActor);
   1361 
   1362  mActor = std::move(aActor);
   1363 }
   1364 
   1365 void FetchEventOp::RevokeActor(FetchEventOpProxyChild* aActor) {
   1366  MOZ_ASSERT(aActor);
   1367  MOZ_ASSERT_IF(mActor, mActor == aActor);
   1368 
   1369  mActor = nullptr;
   1370 }
   1371 
   1372 RefPtr<FetchEventRespondWithPromise> FetchEventOp::GetRespondWithPromise() {
   1373  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
   1374  MOZ_ASSERT(!Started());
   1375  MOZ_ASSERT(mRespondWithPromiseHolder.IsEmpty());
   1376 
   1377  return mRespondWithPromiseHolder.Ensure(__func__);
   1378 }
   1379 
   1380 void FetchEventOp::RespondWithCalledAt(const nsCString& aRespondWithScriptSpec,
   1381                                       uint32_t aRespondWithLineNumber,
   1382                                       uint32_t aRespondWithColumnNumber) {
   1383  MOZ_ASSERT(IsCurrentThreadRunningWorker());
   1384  MOZ_ASSERT(!mRespondWithClosure);
   1385 
   1386  mRespondWithClosure.emplace(aRespondWithScriptSpec, aRespondWithLineNumber,
   1387                              aRespondWithColumnNumber);
   1388 }
   1389 
   1390 void FetchEventOp::ReportCanceled(const nsCString& aPreventDefaultScriptSpec,
   1391                                  uint32_t aPreventDefaultLineNumber,
   1392                                  uint32_t aPreventDefaultColumnNumber) {
   1393  MOZ_ASSERT(IsCurrentThreadRunningWorker());
   1394  MOZ_ASSERT(mActor);
   1395  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1396 
   1397  nsString requestURL;
   1398  GetRequestURL(requestURL);
   1399 
   1400  AsyncLog(aPreventDefaultScriptSpec, aPreventDefaultLineNumber,
   1401           aPreventDefaultColumnNumber, "InterceptionCanceledWithURL"_ns,
   1402           {std::move(requestURL)});
   1403 }
   1404 
   1405 FetchEventOp::~FetchEventOp() {
   1406  mRespondWithPromiseHolder.RejectIfExists(
   1407      CancelInterceptionArgs(
   1408          NS_ERROR_DOM_ABORT_ERR,
   1409          FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish)),
   1410      __func__);
   1411 }
   1412 
   1413 void FetchEventOp::RejectAll(nsresult aStatus) {
   1414  MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
   1415  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1416 
   1417  if (mFetchHandlerStart.IsNull()) {
   1418    mFetchHandlerStart = TimeStamp::Now();
   1419  }
   1420  if (mFetchHandlerFinish.IsNull()) {
   1421    mFetchHandlerFinish = TimeStamp::Now();
   1422  }
   1423 
   1424  mRespondWithPromiseHolder.Reject(
   1425      CancelInterceptionArgs(
   1426          aStatus,
   1427          FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish)),
   1428      __func__);
   1429  mPromiseHolder.Reject(aStatus, __func__);
   1430 }
   1431 
   1432 void FetchEventOp::FinishedWithResult(ExtendableEventResult aResult) {
   1433  MOZ_ASSERT(IsCurrentThreadRunningWorker());
   1434  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1435  MOZ_ASSERT(!mResult);
   1436 
   1437  mResult.emplace(aResult);
   1438 
   1439  /**
   1440   * This should only return early if neither waitUntil() nor respondWith()
   1441   * are called. The early return is so that mRespondWithPromiseHolder has a
   1442   * chance to settle before mPromiseHolder does.
   1443   */
   1444  if (!mPostDispatchChecksDone) {
   1445    return;
   1446  }
   1447 
   1448  MaybeFinished();
   1449 }
   1450 
   1451 void FetchEventOp::MaybeFinished() {
   1452  MOZ_ASSERT(IsCurrentThreadRunningWorker());
   1453  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1454 
   1455  if (mResult) {
   1456    // It's possible that mRespondWithPromiseHolder wasn't settled. That happens
   1457    // if the worker was terminated before the respondWith promise settled.
   1458 
   1459    mHandled = nullptr;
   1460    mPreloadResponse = nullptr;
   1461    mPreloadResponseAvailablePromiseRequestHolder.DisconnectIfExists();
   1462    mPreloadResponseTimingPromiseRequestHolder.DisconnectIfExists();
   1463    mPreloadResponseEndPromiseRequestHolder.DisconnectIfExists();
   1464 
   1465    ServiceWorkerFetchEventOpResult result(
   1466        mResult.value() == Resolved ? NS_OK : NS_ERROR_FAILURE);
   1467 
   1468    mPromiseHolder.Resolve(result, __func__);
   1469  }
   1470 }
   1471 
   1472 bool FetchEventOp::Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) {
   1473  aWorkerPrivate->AssertIsOnWorkerThread();
   1474  MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
   1475  MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
   1476  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1477 
   1478  nsresult rv = DispatchFetchEvent(aCx, aWorkerPrivate);
   1479 
   1480  if (NS_WARN_IF(NS_FAILED(rv))) {
   1481    RejectAll(rv);
   1482  }
   1483 
   1484  return NS_SUCCEEDED(rv);
   1485 }
   1486 
   1487 void FetchEventOp::AsyncLog(const nsCString& aMessageName,
   1488                            nsTArray<nsString> aParams) {
   1489  MOZ_ASSERT(mActor);
   1490  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1491  MOZ_ASSERT(mRespondWithClosure);
   1492 
   1493  const FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
   1494 
   1495  AsyncLog(closure.respondWithScriptSpec(), closure.respondWithLineNumber(),
   1496           closure.respondWithColumnNumber(), aMessageName, std::move(aParams));
   1497 }
   1498 
   1499 void FetchEventOp::AsyncLog(const nsCString& aScriptSpec, uint32_t aLineNumber,
   1500                            uint32_t aColumnNumber,
   1501                            const nsCString& aMessageName,
   1502                            nsTArray<nsString> aParams) {
   1503  MOZ_ASSERT(mActor);
   1504  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1505 
   1506  // Capture `this` because FetchEventOpProxyChild (mActor) is not thread
   1507  // safe, so an AddRef from RefPtr<FetchEventOpProxyChild>'s constructor will
   1508  // assert.
   1509  RefPtr<FetchEventOp> self = this;
   1510 
   1511  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
   1512      __func__, [self = std::move(self), spec = aScriptSpec, line = aLineNumber,
   1513                 column = aColumnNumber, messageName = aMessageName,
   1514                 params = std::move(aParams)] {
   1515        if (NS_WARN_IF(!self->mActor)) {
   1516          return;
   1517        }
   1518 
   1519        (void)self->mActor->SendAsyncLog(spec, line, column, messageName,
   1520                                         params);
   1521      });
   1522 
   1523  MOZ_ALWAYS_SUCCEEDS(
   1524      RemoteWorkerService::Thread()->Dispatch(r.forget(), NS_DISPATCH_NORMAL));
   1525 }
   1526 
   1527 void FetchEventOp::GetRequestURL(nsAString& aOutRequestURL) {
   1528  nsTArray<nsCString>& urls =
   1529      mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs()
   1530          .common()
   1531          .internalRequest()
   1532          .urlList();
   1533  MOZ_ASSERT(!urls.IsEmpty());
   1534 
   1535  CopyUTF8toUTF16(urls.LastElement(), aOutRequestURL);
   1536 }
   1537 
   1538 void FetchEventOp::ResolvedCallback(JSContext* aCx,
   1539                                    JS::Handle<JS::Value> aValue,
   1540                                    ErrorResult& aRv) {
   1541  MOZ_ASSERT(IsCurrentThreadRunningWorker());
   1542  MOZ_ASSERT(mRespondWithClosure);
   1543  MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
   1544  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1545 
   1546  mFetchHandlerFinish = TimeStamp::Now();
   1547 
   1548  nsAutoString requestURL;
   1549  GetRequestURL(requestURL);
   1550 
   1551  AutoCancel autoCancel(this);
   1552 
   1553  if (!aValue.isObject()) {
   1554    NS_WARNING(
   1555        "FetchEvent::RespondWith was passed a promise resolved to a "
   1556        "non-Object "
   1557        "value");
   1558 
   1559    nsCString sourceSpec;
   1560    uint32_t line = 0;
   1561    uint32_t column = 0;
   1562    nsString valueString;
   1563    nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
   1564                                       valueString);
   1565 
   1566    autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
   1567                                           "InterceptedNonResponseWithURL"_ns,
   1568                                           requestURL, valueString);
   1569    return;
   1570  }
   1571 
   1572  RefPtr<Response> response;
   1573  nsresult rv = UNWRAP_OBJECT(Response, &aValue.toObject(), response);
   1574  if (NS_FAILED(rv)) {
   1575    nsCString sourceSpec;
   1576    uint32_t line = 0;
   1577    uint32_t column = 0;
   1578    nsString valueString;
   1579    nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
   1580                                       valueString);
   1581 
   1582    autoCancel.SetCancelMessageAndLocation(sourceSpec, line, column,
   1583                                           "InterceptedNonResponseWithURL"_ns,
   1584                                           requestURL, valueString);
   1585    return;
   1586  }
   1587 
   1588  WorkerPrivate* worker = GetCurrentThreadWorkerPrivate();
   1589  MOZ_ASSERT(worker);
   1590  worker->AssertIsOnWorkerThread();
   1591 
   1592  // Section "HTTP Fetch", step 3.3:
   1593  //  If one of the following conditions is true, return a network error:
   1594  //    * response's type is "error".
   1595  //    * request's mode is not "no-cors" and response's type is "opaque".
   1596  //    * request's redirect mode is not "manual" and response's type is
   1597  //      "opaqueredirect".
   1598  //    * request's redirect mode is not "follow" and response's url list
   1599  //      has more than one item.
   1600 
   1601  if (response->Type() == ResponseType::Error) {
   1602    autoCancel.SetCancelMessage("InterceptedErrorResponseWithURL"_ns,
   1603                                requestURL);
   1604    return;
   1605  }
   1606 
   1607  const ParentToChildServiceWorkerFetchEventOpArgs& args =
   1608      mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs();
   1609  const RequestMode requestMode = args.common().internalRequest().requestMode();
   1610 
   1611  if (response->Type() == ResponseType::Opaque &&
   1612      requestMode != RequestMode::No_cors) {
   1613    NS_ConvertASCIItoUTF16 modeString(GetEnumString(requestMode));
   1614 
   1615    nsAutoString requestURL;
   1616    GetRequestURL(requestURL);
   1617 
   1618    autoCancel.SetCancelMessage("BadOpaqueInterceptionRequestModeWithURL"_ns,
   1619                                requestURL, modeString);
   1620    return;
   1621  }
   1622 
   1623  const RequestRedirect requestRedirectMode =
   1624      args.common().internalRequest().requestRedirect();
   1625 
   1626  if (requestRedirectMode != RequestRedirect::Manual &&
   1627      response->Type() == ResponseType::Opaqueredirect) {
   1628    autoCancel.SetCancelMessage("BadOpaqueRedirectInterceptionWithURL"_ns,
   1629                                requestURL);
   1630    return;
   1631  }
   1632 
   1633  if (requestRedirectMode != RequestRedirect::Follow &&
   1634      response->Redirected()) {
   1635    autoCancel.SetCancelMessage("BadRedirectModeInterceptionWithURL"_ns,
   1636                                requestURL);
   1637    return;
   1638  }
   1639 
   1640  if (NS_WARN_IF(response->BodyUsed())) {
   1641    autoCancel.SetCancelMessage("InterceptedUsedResponseWithURL"_ns,
   1642                                requestURL);
   1643    return;
   1644  }
   1645 
   1646  SafeRefPtr<InternalResponse> ir = response->GetInternalResponse();
   1647  if (NS_WARN_IF(!ir)) {
   1648    return;
   1649  }
   1650 
   1651  // An extra safety check to make sure our invariant that opaque and cors
   1652  // responses always have a URL does not break.
   1653  if (NS_WARN_IF((response->Type() == ResponseType::Opaque ||
   1654                  response->Type() == ResponseType::Cors) &&
   1655                 ir->GetUnfilteredURL().IsEmpty())) {
   1656    MOZ_DIAGNOSTIC_CRASH("Cors or opaque Response without a URL");
   1657    return;
   1658  }
   1659 
   1660  if (requestMode == RequestMode::Same_origin &&
   1661      response->Type() == ResponseType::Cors) {
   1662    // XXXtt: Will have a pref to enable the quirk response in bug 1419684.
   1663    // The variadic template provided by StringArrayAppender requires exactly
   1664    // an nsString.
   1665    NS_ConvertUTF8toUTF16 responseURL(ir->GetUnfilteredURL());
   1666    autoCancel.SetCancelMessage("CorsResponseForSameOriginRequest"_ns,
   1667                                requestURL, responseURL);
   1668    return;
   1669  }
   1670 
   1671  nsCOMPtr<nsIInputStream> body;
   1672  ir->GetUnfilteredBody(getter_AddRefs(body));
   1673  // Errors and redirects may not have a body.
   1674  if (body) {
   1675    ErrorResult error;
   1676    response->SetBodyUsed(aCx, error);
   1677    error.WouldReportJSException();
   1678    if (NS_WARN_IF(error.Failed())) {
   1679      autoCancel.SetCancelErrorResult(aCx, error);
   1680      return;
   1681    }
   1682  }
   1683 
   1684  if (!ir->GetChannelInfo().IsInitialized()) {
   1685    // This is a synthetic response (I think and hope so).
   1686    ir->InitChannelInfo(worker->GetChannelInfo());
   1687  }
   1688 
   1689  autoCancel.Reset();
   1690 
   1691  // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 26: If
   1692  // eventHandled is not null, then resolve eventHandled.
   1693  //
   1694  // mRespondWithPromiseHolder will resolve a MozPromise that will resolve on
   1695  // the worker owner's thread, so it's fine to resolve the mHandled promise now
   1696  // because content will not interfere with respondWith getting the Response to
   1697  // where it's going.
   1698  mHandled->MaybeResolveWithUndefined();
   1699  mRespondWithPromiseHolder.Resolve(
   1700      FetchEventRespondWithResult(std::make_tuple(
   1701          std::move(ir), mRespondWithClosure.ref(),
   1702          FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
   1703      __func__);
   1704 }
   1705 
   1706 void FetchEventOp::RejectedCallback(JSContext* aCx,
   1707                                    JS::Handle<JS::Value> aValue,
   1708                                    ErrorResult& aRv) {
   1709  MOZ_ASSERT(IsCurrentThreadRunningWorker());
   1710  MOZ_ASSERT(mRespondWithClosure);
   1711  MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
   1712  MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   1713 
   1714  mFetchHandlerFinish = TimeStamp::Now();
   1715 
   1716  FetchEventRespondWithClosure& closure = mRespondWithClosure.ref();
   1717 
   1718  nsCString sourceSpec = closure.respondWithScriptSpec();
   1719  uint32_t line = closure.respondWithLineNumber();
   1720  uint32_t column = closure.respondWithColumnNumber();
   1721  nsString valueString;
   1722 
   1723  nsContentUtils::ExtractErrorValues(aCx, aValue, sourceSpec, &line, &column,
   1724                                     valueString);
   1725 
   1726  nsString requestURL;
   1727  GetRequestURL(requestURL);
   1728 
   1729  AsyncLog(sourceSpec, line, column, "InterceptionRejectedResponseWithURL"_ns,
   1730           {std::move(requestURL), valueString});
   1731 
   1732  // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm Step 25.1:
   1733  // If eventHandled is not null, then reject eventHandled with a "NetworkError"
   1734  // DOMException in workerRealm.
   1735  mHandled->MaybeRejectWithNetworkError(
   1736      "FetchEvent.respondWith() Promise rejected"_ns);
   1737  mRespondWithPromiseHolder.Resolve(
   1738      FetchEventRespondWithResult(CancelInterceptionArgs(
   1739          NS_ERROR_INTERCEPTION_FAILED,
   1740          FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
   1741      __func__);
   1742 }
   1743 
   1744 nsresult FetchEventOp::DispatchFetchEvent(JSContext* aCx,
   1745                                          WorkerPrivate* aWorkerPrivate) {
   1746  MOZ_ASSERT(aCx);
   1747  MOZ_ASSERT(aWorkerPrivate);
   1748  aWorkerPrivate->AssertIsOnWorkerThread();
   1749  MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
   1750 
   1751  ParentToChildServiceWorkerFetchEventOpArgs& args =
   1752      mArgs.get_ParentToChildServiceWorkerFetchEventOpArgs();
   1753 
   1754  /**
   1755   * Testing: Failure injection.
   1756   *
   1757   * There are a number of different ways that this fetch event could have
   1758   * failed that would result in cancellation.  This injection point helps
   1759   * simulate them without worrying about shifting implementation details with
   1760   * full fidelity reproductions of current scenarios.
   1761   *
   1762   * Broadly speaking, we expect fetch event scenarios to fail because of:
   1763   * - Script load failure, which results in the CompileScriptRunnable closing
   1764   *   the worker and thereby cancelling all pending operations, including this
   1765   *   fetch.  The `ServiceWorkerOp::Cancel` impl just calls
   1766   *   RejectAll(NS_ERROR_DOM_ABORT_ERR) which we are able to approximate by
   1767   *   returning the same nsresult here, as our caller also calls RejectAll.
   1768   *   (And timing-wise, this rejection will happen in the correct sequence.)
   1769   * - An exception gets thrown in the processing of the promise that was passed
   1770   *   to respondWith and it ends up rejecting.  The rejection will be converted
   1771   *   by `FetchEventOp::RejectedCallback` into a cancellation with
   1772   *   NS_ERROR_INTERCEPTION_FAILED, and by returning that here we approximate
   1773   *   that failure mode.
   1774   */
   1775  if (NS_FAILED(args.common().testingInjectCancellation())) {
   1776    return args.common().testingInjectCancellation();
   1777  }
   1778 
   1779  /**
   1780   * Step 1: get the InternalRequest. The InternalRequest can't be constructed
   1781   * here from mArgs because the IPCStream has to be deserialized on the
   1782   * thread receiving the ServiceWorkerFetchEventOpArgs.
   1783   * FetchEventOpProxyChild will have already deserialized the stream on the
   1784   * correct thread before creating this op, so we can take its saved
   1785   * InternalRequest.
   1786   */
   1787  SafeRefPtr<InternalRequest> internalRequest =
   1788      mActor->ExtractInternalRequest();
   1789 
   1790  /**
   1791   * Step 2: get the worker's global object
   1792   */
   1793  GlobalObject globalObject(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
   1794  nsCOMPtr<nsIGlobalObject> globalObjectAsSupports =
   1795      do_QueryInterface(globalObject.GetAsSupports());
   1796  if (NS_WARN_IF(!globalObjectAsSupports)) {
   1797    return NS_ERROR_DOM_INVALID_STATE_ERR;
   1798  }
   1799 
   1800  /**
   1801   * Step 3: create the public DOM Request object
   1802   * TODO: this Request object should be created with an AbortSignal object
   1803   * which should be aborted if the loading is aborted. See but 1394102.
   1804   */
   1805  RefPtr<Request> request =
   1806      new Request(globalObjectAsSupports, internalRequest.clonePtr(), nullptr);
   1807  MOZ_ASSERT_IF(internalRequest->IsNavigationRequest(),
   1808                request->Redirect() == RequestRedirect::Manual);
   1809 
   1810  /**
   1811   * Step 4a: create the FetchEventInit
   1812   */
   1813  RootedDictionary<FetchEventInit> fetchEventInit(aCx);
   1814  fetchEventInit.mRequest = request;
   1815  fetchEventInit.mBubbles = false;
   1816  fetchEventInit.mCancelable = true;
   1817 
   1818  /**
   1819   * TODO: only expose the FetchEvent.clientId on subresource requests for
   1820   * now. Once we implement .targetClientId we can then start exposing
   1821   * .clientId on non-subresource requests as well.  See bug 1487534.
   1822   */
   1823  if (!args.common().clientId().IsEmpty() &&
   1824      !internalRequest->IsNavigationRequest()) {
   1825    fetchEventInit.mClientId = args.common().clientId();
   1826  }
   1827 
   1828  /*
   1829   * https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
   1830   *
   1831   * "If request is a non-subresource request and request’s
   1832   * destination is not "report", initialize e’s resultingClientId attribute
   1833   * to reservedClient’s [resultingClient's] id, and to the empty string
   1834   * otherwise." (Step 18.8)
   1835   */
   1836  if (!args.common().resultingClientId().IsEmpty() &&
   1837      args.common().isNonSubresourceRequest() &&
   1838      internalRequest->Destination() != RequestDestination::Report) {
   1839    fetchEventInit.mResultingClientId = args.common().resultingClientId();
   1840  }
   1841 
   1842  /**
   1843   * Step 4b: create the FetchEvent
   1844   */
   1845  RefPtr<FetchEvent> fetchEvent =
   1846      FetchEvent::Constructor(globalObject, u"fetch"_ns, fetchEventInit);
   1847  fetchEvent->SetTrusted(true);
   1848  fetchEvent->PostInit(args.common().workerScriptSpec(), this);
   1849  mHandled = fetchEvent->Handled();
   1850  mPreloadResponse = fetchEvent->PreloadResponse();
   1851 
   1852  if (args.common().preloadNavigation()) {
   1853    RefPtr<FetchEventPreloadResponseAvailablePromise> preloadResponsePromise =
   1854        mActor->GetPreloadResponseAvailablePromise();
   1855    MOZ_ASSERT(preloadResponsePromise);
   1856 
   1857    // If preloadResponsePromise has already settled then this callback will get
   1858    // run synchronously here.
   1859    RefPtr<FetchEventOp> self = this;
   1860    preloadResponsePromise
   1861        ->Then(
   1862            GetCurrentSerialEventTarget(), __func__,
   1863            [self, globalObjectAsSupports](
   1864                SafeRefPtr<InternalResponse>&& aPreloadResponse) {
   1865              self->mPreloadResponse->MaybeResolve(
   1866                  MakeRefPtr<Response>(globalObjectAsSupports,
   1867                                       std::move(aPreloadResponse), nullptr));
   1868              self->mPreloadResponseAvailablePromiseRequestHolder.Complete();
   1869            },
   1870            [self](int) {
   1871              self->mPreloadResponseAvailablePromiseRequestHolder.Complete();
   1872            })
   1873        ->Track(mPreloadResponseAvailablePromiseRequestHolder);
   1874 
   1875    RefPtr<PerformanceStorage> performanceStorage =
   1876        aWorkerPrivate->GetPerformanceStorage();
   1877 
   1878    RefPtr<FetchEventPreloadResponseTimingPromise>
   1879        preloadResponseTimingPromise =
   1880            mActor->GetPreloadResponseTimingPromise();
   1881    MOZ_ASSERT(preloadResponseTimingPromise);
   1882    preloadResponseTimingPromise
   1883        ->Then(
   1884            GetCurrentSerialEventTarget(), __func__,
   1885            [self, performanceStorage,
   1886             globalObjectAsSupports](ResponseTiming&& aTiming) {
   1887              if (performanceStorage && !aTiming.entryName().IsEmpty() &&
   1888                  aTiming.initiatorType().Equals(u"navigation"_ns)) {
   1889                performanceStorage->AddEntry(
   1890                    aTiming.entryName(), aTiming.initiatorType(),
   1891                    MakeUnique<PerformanceTimingData>(aTiming.timingData()));
   1892              }
   1893              self->mPreloadResponseTimingPromiseRequestHolder.Complete();
   1894            },
   1895            [self](int) {
   1896              self->mPreloadResponseTimingPromiseRequestHolder.Complete();
   1897            })
   1898        ->Track(mPreloadResponseTimingPromiseRequestHolder);
   1899 
   1900    RefPtr<FetchEventPreloadResponseEndPromise> preloadResponseEndPromise =
   1901        mActor->GetPreloadResponseEndPromise();
   1902    MOZ_ASSERT(preloadResponseEndPromise);
   1903    preloadResponseEndPromise
   1904        ->Then(
   1905            GetCurrentSerialEventTarget(), __func__,
   1906            [self, globalObjectAsSupports](ResponseEndArgs&& aArgs) {
   1907              if (aArgs.endReason() == FetchDriverObserver::eAborted) {
   1908                self->mPreloadResponse->MaybeReject(NS_ERROR_DOM_ABORT_ERR);
   1909              }
   1910              self->mPreloadResponseEndPromiseRequestHolder.Complete();
   1911            },
   1912            [self](int) {
   1913              self->mPreloadResponseEndPromiseRequestHolder.Complete();
   1914            })
   1915        ->Track(mPreloadResponseEndPromiseRequestHolder);
   1916  } else {
   1917    // preload navigation is disabled, resolved preload response promise with
   1918    // undefined as default behavior.
   1919    mPreloadResponse->MaybeResolveWithUndefined();
   1920  }
   1921 
   1922  mFetchHandlerStart = TimeStamp::Now();
   1923 
   1924  /**
   1925   * Step 5: Dispatch the FetchEvent to the worker's global object
   1926   */
   1927  nsresult rv = DispatchExtendableEventOnWorkerScope(
   1928      aCx, aWorkerPrivate->GlobalScope(), fetchEvent, this);
   1929  bool dispatchFailed = NS_FAILED(rv) && rv != NS_ERROR_XPC_JS_THREW_EXCEPTION;
   1930 
   1931  if (NS_WARN_IF(dispatchFailed)) {
   1932    mHandled = nullptr;
   1933    mPreloadResponse = nullptr;
   1934    return rv;
   1935  }
   1936 
   1937  /**
   1938   * At this point, there are 4 (legal) scenarios:
   1939   *
   1940   * 1) If neither waitUntil() nor respondWith() are called,
   1941   * DispatchExtendableEventOnWorkerScope() will have already called
   1942   * FinishedWithResult(), but this call will have recorded the result
   1943   * (mResult) and returned early so that mRespondWithPromiseHolder can be
   1944   * settled first. mRespondWithPromiseHolder will be settled below, followed
   1945   * by a call to MaybeFinished() which settles mPromiseHolder.
   1946   *
   1947   * 2) If waitUntil() is called at least once, and respondWith() is not
   1948   * called, DispatchExtendableEventOnWorkerScope() will NOT have called
   1949   * FinishedWithResult(). We'll settle mRespondWithPromiseHolder first, and
   1950   * at some point in the future when the last waitUntil() promise settles,
   1951   * FinishedWithResult() will be called, settling mPromiseHolder.
   1952   *
   1953   * 3) If waitUntil() is not called, and respondWith() is called,
   1954   * DispatchExtendableEventOnWorkerScope() will NOT have called
   1955   * FinishedWithResult(). We can also guarantee that
   1956   * mRespondWithPromiseHolder will be settled before mPromiseHolder, due to
   1957   * the Promise::AppendNativeHandler() call ordering in
   1958   * FetchEvent::RespondWith().
   1959   *
   1960   * 4) If waitUntil() is called at least once, and respondWith() is also
   1961   * called, the effect is similar to scenario 3), with the most imporant
   1962   * property being mRespondWithPromiseHolder settling before mPromiseHolder.
   1963   *
   1964   * Note that if mPromiseHolder is settled before mRespondWithPromiseHolder,
   1965   * FetchEventOpChild will cancel the interception.
   1966   */
   1967  if (!fetchEvent->WaitToRespond()) {
   1968    MOZ_ASSERT(!mRespondWithPromiseHolder.IsEmpty());
   1969    MOZ_ASSERT(!aWorkerPrivate->UsesSystemPrincipal(),
   1970               "We don't support system-principal serviceworkers");
   1971 
   1972    mFetchHandlerFinish = TimeStamp::Now();
   1973 
   1974    if (fetchEvent->DefaultPrevented(CallerType::NonSystem)) {
   1975      // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
   1976      // Step 24.1.1: If eventHandled is not null, then reject eventHandled with
   1977      // a "NetworkError" DOMException in workerRealm.
   1978      mHandled->MaybeRejectWithNetworkError(
   1979          "FetchEvent.preventDefault() called"_ns);
   1980      mRespondWithPromiseHolder.Resolve(
   1981          FetchEventRespondWithResult(CancelInterceptionArgs(
   1982              NS_ERROR_INTERCEPTION_FAILED,
   1983              FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
   1984          __func__);
   1985    } else {
   1986      // https://w3c.github.io/ServiceWorker/#on-fetch-request-algorithm
   1987      // Step 24.2: If eventHandled is not null, then resolve eventHandled.
   1988      mHandled->MaybeResolveWithUndefined();
   1989      mRespondWithPromiseHolder.Resolve(
   1990          FetchEventRespondWithResult(ResetInterceptionArgs(
   1991              FetchEventTimeStamps(mFetchHandlerStart, mFetchHandlerFinish))),
   1992          __func__);
   1993    }
   1994  } else {
   1995    MOZ_ASSERT(mRespondWithClosure);
   1996  }
   1997 
   1998  mPostDispatchChecksDone = true;
   1999  MaybeFinished();
   2000 
   2001  return NS_OK;
   2002 }
   2003 
   2004 class ExtensionAPIEventOp final : public ServiceWorkerOp {
   2005  using ServiceWorkerOp::ServiceWorkerOp;
   2006 
   2007 public:
   2008  NS_INLINE_DECL_THREADSAFE_REFCOUNTING(ExtensionAPIEventOp, override)
   2009 
   2010 private:
   2011  ~ExtensionAPIEventOp() = default;
   2012 
   2013  bool Exec(JSContext* aCx, WorkerPrivate* aWorkerPrivate) override {
   2014    MOZ_ASSERT(aWorkerPrivate);
   2015    aWorkerPrivate->AssertIsOnWorkerThread();
   2016    MOZ_ASSERT(aWorkerPrivate->IsServiceWorker());
   2017    MOZ_ASSERT(aWorkerPrivate->ExtensionAPIAllowed());
   2018    MOZ_ASSERT(!mPromiseHolder.IsEmpty());
   2019 
   2020    ServiceWorkerExtensionAPIEventOpArgs& args =
   2021        mArgs.get_ServiceWorkerExtensionAPIEventOpArgs();
   2022 
   2023    ServiceWorkerExtensionAPIEventOpResult result;
   2024    result.extensionAPIEventListenerWasAdded() = false;
   2025 
   2026    if (aWorkerPrivate->WorkerScriptExecutedSuccessfully()) {
   2027      GlobalObject globalObj(aCx, aWorkerPrivate->GlobalScope()->GetWrapper());
   2028      RefPtr<ServiceWorkerGlobalScope> scope;
   2029      UNWRAP_OBJECT(ServiceWorkerGlobalScope, globalObj.Get(), scope);
   2030      SafeRefPtr<extensions::ExtensionBrowser> extensionAPI =
   2031          scope->AcquireExtensionBrowser();
   2032      if (!extensionAPI) {
   2033        // If the worker script did never access the WebExtension APIs
   2034        // then we can return earlier, no event listener could have been added.
   2035        mPromiseHolder.Resolve(result, __func__);
   2036        return true;
   2037      }
   2038      // Check if a listener has been subscribed on the expected WebExtensions
   2039      // API event.
   2040      bool hasWakeupListener = extensionAPI->HasWakeupEventListener(
   2041          args.apiNamespace(), args.apiEventName());
   2042      result.extensionAPIEventListenerWasAdded() = hasWakeupListener;
   2043      mPromiseHolder.Resolve(result, __func__);
   2044    } else {
   2045      mPromiseHolder.Resolve(result, __func__);
   2046    }
   2047 
   2048    return true;
   2049  }
   2050 };
   2051 
   2052 /* static */ already_AddRefed<ServiceWorkerOp> ServiceWorkerOp::Create(
   2053    ServiceWorkerOpArgs&& aArgs,
   2054    std::function<void(const ServiceWorkerOpResult&)>&& aCallback) {
   2055  // Can be on the Worker Launcher thread for Terminate operations or on the
   2056  // Worker thread for other operations.
   2057  RefPtr<ServiceWorkerOp> op;
   2058 
   2059  switch (aArgs.type()) {
   2060    case ServiceWorkerOpArgs::TServiceWorkerCheckScriptEvaluationOpArgs:
   2061      op = MakeRefPtr<CheckScriptEvaluationOp>(std::move(aArgs),
   2062                                               std::move(aCallback));
   2063      break;
   2064    case ServiceWorkerOpArgs::TServiceWorkerUpdateStateOpArgs:
   2065      op = MakeRefPtr<UpdateServiceWorkerStateOp>(std::move(aArgs),
   2066                                                  std::move(aCallback));
   2067      break;
   2068    case ServiceWorkerOpArgs::TServiceWorkerTerminateWorkerOpArgs:
   2069      op = MakeRefPtr<TerminateServiceWorkerOp>(std::move(aArgs),
   2070                                                std::move(aCallback));
   2071      break;
   2072    case ServiceWorkerOpArgs::TServiceWorkerLifeCycleEventOpArgs:
   2073      op = MakeRefPtr<LifeCycleEventOp>(std::move(aArgs), std::move(aCallback));
   2074      break;
   2075    case ServiceWorkerOpArgs::TServiceWorkerCookieChangeEventOpArgs:
   2076      op = MakeRefPtr<CookieChangeEventOp>(std::move(aArgs),
   2077                                           std::move(aCallback));
   2078      break;
   2079    case ServiceWorkerOpArgs::TServiceWorkerPushEventOpArgs:
   2080      op = MakeRefPtr<PushEventOp>(std::move(aArgs), std::move(aCallback));
   2081      break;
   2082    case ServiceWorkerOpArgs::TServiceWorkerPushSubscriptionChangeEventOpArgs:
   2083      op = MakeRefPtr<PushSubscriptionChangeEventOp>(std::move(aArgs),
   2084                                                     std::move(aCallback));
   2085      break;
   2086    case ServiceWorkerOpArgs::TServiceWorkerNotificationEventOpArgs:
   2087      op = MakeRefPtr<NotificationEventOp>(std::move(aArgs),
   2088                                           std::move(aCallback));
   2089      break;
   2090    case ServiceWorkerOpArgs::TServiceWorkerMessageEventOpArgs:
   2091      op = MakeRefPtr<MessageEventOp>(std::move(aArgs), std::move(aCallback));
   2092      break;
   2093    case ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs:
   2094      op = MakeRefPtr<FetchEventOp>(std::move(aArgs), std::move(aCallback));
   2095      break;
   2096    case ServiceWorkerOpArgs::TServiceWorkerExtensionAPIEventOpArgs:
   2097      op = MakeRefPtr<ExtensionAPIEventOp>(std::move(aArgs),
   2098                                           std::move(aCallback));
   2099      break;
   2100    case ServiceWorkerOpArgs::
   2101        TServiceWorkerUpdateIsOnContentBlockingAllowListOpArgs:
   2102      op = MakeRefPtr<UpdateIsOnContentBlockingAllowListOp>(
   2103          std::move(aArgs), std::move(aCallback));
   2104      break;
   2105    default:
   2106      MOZ_CRASH("Unknown Service Worker operation!");
   2107      return nullptr;
   2108  }
   2109 
   2110  return op.forget();
   2111 }
   2112 
   2113 }  // namespace mozilla::dom