tor-browser

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

RemoteWorkerChild.cpp (30874B)


      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 "RemoteWorkerChild.h"
      8 
      9 #include <utility>
     10 
     11 #include "MainThreadUtils.h"
     12 #include "RemoteWorkerService.h"
     13 #include "mozilla/ArrayAlgorithm.h"
     14 #include "mozilla/Assertions.h"
     15 #include "mozilla/BasePrincipal.h"
     16 #include "mozilla/ErrorResult.h"
     17 #include "mozilla/PermissionManager.h"
     18 #include "mozilla/SchedulerGroup.h"
     19 #include "mozilla/ScopeExit.h"
     20 #include "mozilla/Services.h"
     21 #include "mozilla/dom/FetchEventOpProxyChild.h"
     22 #include "mozilla/dom/IndexedDatabaseManager.h"
     23 #include "mozilla/dom/MessagePort.h"
     24 #include "mozilla/dom/PolicyContainer.h"
     25 #include "mozilla/dom/RemoteWorkerTypes.h"
     26 #include "mozilla/dom/ServiceWorkerDescriptor.h"
     27 #include "mozilla/dom/ServiceWorkerInterceptController.h"
     28 #include "mozilla/dom/ServiceWorkerOp.h"
     29 #include "mozilla/dom/ServiceWorkerRegistrationDescriptor.h"
     30 #include "mozilla/dom/ServiceWorkerShutdownState.h"
     31 #include "mozilla/dom/ServiceWorkerUtils.h"
     32 #include "mozilla/dom/SharedWorkerOp.h"
     33 #include "mozilla/dom/WorkerCSPContext.h"
     34 #include "mozilla/dom/WorkerError.h"
     35 #include "mozilla/dom/WorkerPrivate.h"
     36 #include "mozilla/dom/WorkerRef.h"
     37 #include "mozilla/dom/WorkerRunnable.h"
     38 #include "mozilla/dom/WorkerScope.h"
     39 #include "mozilla/dom/workerinternals/ScriptLoader.h"
     40 #include "mozilla/ipc/BackgroundUtils.h"
     41 #include "mozilla/ipc/URIUtils.h"
     42 #include "mozilla/net/CookieJarSettings.h"
     43 #include "nsCOMPtr.h"
     44 #include "nsDebug.h"
     45 #include "nsError.h"
     46 #include "nsIConsoleReportCollector.h"
     47 #include "nsIInterfaceRequestor.h"
     48 #include "nsIPrincipal.h"
     49 #include "nsNetUtil.h"
     50 #include "nsThreadUtils.h"
     51 #include "nsXULAppAPI.h"
     52 
     53 mozilla::LazyLogModule gRemoteWorkerChildLog("RemoteWorkerChild");
     54 
     55 #ifdef LOG
     56 #  undef LOG
     57 #endif
     58 #define LOG(fmt) MOZ_LOG(gRemoteWorkerChildLog, mozilla::LogLevel::Verbose, fmt)
     59 
     60 namespace mozilla {
     61 
     62 using namespace ipc;
     63 
     64 namespace dom {
     65 
     66 using workerinternals::ChannelFromScriptURLMainThread;
     67 
     68 using remoteworker::Canceled;
     69 using remoteworker::Killed;
     70 using remoteworker::Pending;
     71 using remoteworker::Running;
     72 
     73 namespace {
     74 
     75 class SharedWorkerInterfaceRequestor final : public nsIInterfaceRequestor {
     76 public:
     77  NS_DECL_ISUPPORTS
     78 
     79  SharedWorkerInterfaceRequestor() {
     80    // This check must match the code nsDocShell::Create.
     81    if (XRE_IsParentProcess()) {
     82      mSWController = new ServiceWorkerInterceptController();
     83    }
     84  }
     85 
     86  NS_IMETHOD
     87  GetInterface(const nsIID& aIID, void** aSink) override {
     88    MOZ_ASSERT(NS_IsMainThread());
     89 
     90    if (mSWController &&
     91        aIID.Equals(NS_GET_IID(nsINetworkInterceptController))) {
     92      // If asked for the network intercept controller, ask the outer requestor,
     93      // which could be the docshell.
     94      RefPtr<ServiceWorkerInterceptController> swController = mSWController;
     95      swController.forget(aSink);
     96      return NS_OK;
     97    }
     98 
     99    return NS_NOINTERFACE;
    100  }
    101 
    102 private:
    103  ~SharedWorkerInterfaceRequestor() = default;
    104 
    105  RefPtr<ServiceWorkerInterceptController> mSWController;
    106 };
    107 
    108 NS_IMPL_ADDREF(SharedWorkerInterfaceRequestor)
    109 NS_IMPL_RELEASE(SharedWorkerInterfaceRequestor)
    110 NS_IMPL_QUERY_INTERFACE(SharedWorkerInterfaceRequestor, nsIInterfaceRequestor)
    111 
    112 // This is used to propagate the CSP violation when loading the SharedWorker
    113 // main-script and nothing else.
    114 class RemoteWorkerCSPEventListener final : public nsICSPEventListener {
    115 public:
    116  NS_DECL_ISUPPORTS
    117 
    118  explicit RemoteWorkerCSPEventListener(RemoteWorkerChild* aActor)
    119      : mActor(aActor) {};
    120 
    121  NS_IMETHOD OnCSPViolationEvent(const nsAString& aJSON) override {
    122    mActor->CSPViolationPropagationOnMainThread(aJSON);
    123    return NS_OK;
    124  }
    125 
    126 private:
    127  ~RemoteWorkerCSPEventListener() = default;
    128 
    129  RefPtr<RemoteWorkerChild> mActor;
    130 };
    131 
    132 NS_IMPL_ISUPPORTS(RemoteWorkerCSPEventListener, nsICSPEventListener)
    133 
    134 }  // anonymous namespace
    135 
    136 RemoteWorkerChild::RemoteWorkerChild(const RemoteWorkerData& aData)
    137    : mState(VariantType<remoteworker::Pending>(), "RemoteWorkerState"),
    138      mServiceKeepAlive(RemoteWorkerService::MaybeGetKeepAlive()),
    139      mIsServiceWorker(aData.serviceWorkerData().type() ==
    140                       OptionalServiceWorkerData::TServiceWorkerData),
    141      mPendingOps("PendingRemoteWorkerOps") {
    142  MOZ_ASSERT(RemoteWorkerService::Thread()->IsOnCurrentThread());
    143 }
    144 
    145 RemoteWorkerChild::~RemoteWorkerChild() {
    146 #ifdef DEBUG
    147  auto lock = mState.Lock();
    148  MOZ_ASSERT(lock->is<Killed>());
    149 #endif
    150 }
    151 
    152 void RemoteWorkerChild::ActorDestroy(ActorDestroyReason) {
    153  auto launcherData = mLauncherData.Access();
    154 
    155  (void)NS_WARN_IF(!launcherData->mTerminationPromise.IsEmpty());
    156  launcherData->mTerminationPromise.RejectIfExists(NS_ERROR_DOM_ABORT_ERR,
    157                                                   __func__);
    158 
    159  auto lock = mState.Lock();
    160 
    161  // If the worker hasn't shutdown or begun shutdown, we need to ensure it gets
    162  // canceled.
    163  if (NS_WARN_IF(!lock->is<Killed>() && !lock->is<Canceled>())) {
    164    // In terms of strong references to this RemoteWorkerChild, at this moment:
    165    // - IPC is holding a strong reference that will be dropped in the near
    166    //   future after this method returns.
    167    // - If the worker has been started by ExecWorkerOnMainThread, then
    168    //   WorkerPrivate::mRemoteWorkerController is a strong reference to us.
    169    //   If the worker has not been started, ExecWorker's runnable lambda will
    170    //   have a strong reference that will cover the call to
    171    //   ExecWorkerOnMainThread.
    172    //   - The WorkerPrivate cancellation and termination callbacks will also
    173    //     hold strong references, but those callbacks will not outlive the
    174    //     WorkerPrivate and are not exposed to callers like
    175    //     mRemoteWorkerController is.
    176    //
    177    // Note that this call to RequestWorkerCancellation can still race worker
    178    // cancellation, in which case the strong reference obtained by
    179    // NewRunnableMethod can end up being the last strong reference.
    180    // (RequestWorkerCancellation handles the case that the Worker is already
    181    // canceled if this happens.)
    182    RefPtr<nsIRunnable> runnable =
    183        NewRunnableMethod("RequestWorkerCancellation", this,
    184                          &RemoteWorkerChild::RequestWorkerCancellation);
    185    MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(runnable.forget()));
    186  }
    187 }
    188 
    189 void RemoteWorkerChild::ExecWorker(
    190    const RemoteWorkerData& aData,
    191    mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild>&&
    192        aChildEp) {
    193 #ifdef DEBUG
    194  MOZ_ASSERT(GetActorEventTarget()->IsOnCurrentThread());
    195  auto launcherData = mLauncherData.Access();
    196  MOZ_ASSERT(CanSend());
    197 #endif
    198 
    199  RefPtr<RemoteWorkerChild> self = this;
    200 
    201  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    202      __func__, [self = std::move(self), data = aData,
    203                 childEp = std::move(aChildEp)]() mutable {
    204        nsresult rv =
    205            self->ExecWorkerOnMainThread(std::move(data), std::move(childEp));
    206 
    207        // Creation failure will already have been reported via the method
    208        // above internally using ScopeExit.
    209        (void)NS_WARN_IF(NS_FAILED(rv));
    210      });
    211 
    212  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    213 }
    214 
    215 nsresult RemoteWorkerChild::ExecWorkerOnMainThread(
    216    RemoteWorkerData&& aData,
    217    mozilla::ipc::Endpoint<PRemoteWorkerNonLifeCycleOpControllerChild>&&
    218        aChildEp) {
    219  MOZ_ASSERT(NS_IsMainThread());
    220 
    221  // Ensure that the IndexedDatabaseManager is initialized so that if any
    222  // workers do any IndexedDB calls that all of IDB's prefs/etc. are
    223  // initialized.
    224  IndexedDatabaseManager* idm = IndexedDatabaseManager::GetOrCreate();
    225  if (idm) {
    226    (void)NS_WARN_IF(NS_FAILED(idm->EnsureLocale()));
    227  } else {
    228    NS_WARNING("Failed to get IndexedDatabaseManager!");
    229  }
    230 
    231  auto scopeExit =
    232      MakeScopeExit([&] { ExceptionalErrorTransitionDuringExecWorker(); });
    233 
    234  auto principalOrErr = PrincipalInfoToPrincipal(aData.principalInfo());
    235  if (NS_WARN_IF(principalOrErr.isErr())) {
    236    return principalOrErr.unwrapErr();
    237  }
    238 
    239  nsCOMPtr<nsIPrincipal> principal = principalOrErr.unwrap();
    240 
    241  auto loadingPrincipalOrErr =
    242      PrincipalInfoToPrincipal(aData.loadingPrincipalInfo());
    243  if (NS_WARN_IF(loadingPrincipalOrErr.isErr())) {
    244    return loadingPrincipalOrErr.unwrapErr();
    245  }
    246 
    247  auto partitionedPrincipalOrErr =
    248      PrincipalInfoToPrincipal(aData.partitionedPrincipalInfo());
    249  if (NS_WARN_IF(partitionedPrincipalOrErr.isErr())) {
    250    return partitionedPrincipalOrErr.unwrapErr();
    251  }
    252 
    253  WorkerLoadInfo info;
    254  info.mBaseURI = DeserializeURI(aData.baseScriptURL());
    255  info.mResolvedScriptURI = DeserializeURI(aData.resolvedScriptURL());
    256 
    257  info.mPrincipalInfo = MakeUnique<PrincipalInfo>(aData.principalInfo());
    258  info.mPartitionedPrincipalInfo =
    259      MakeUnique<PrincipalInfo>(aData.partitionedPrincipalInfo());
    260 
    261  info.mReferrerInfo = aData.referrerInfo();
    262  info.mDomain = aData.domain();
    263  info.mTrials = aData.originTrials();
    264  info.mPrincipal = principal;
    265  info.mPartitionedPrincipal = partitionedPrincipalOrErr.unwrap();
    266  info.mLoadingPrincipal = loadingPrincipalOrErr.unwrap();
    267  info.mStorageAccess = aData.storageAccess();
    268  info.mUseRegularPrincipal = aData.useRegularPrincipal();
    269  info.mUsingStorageAccess = aData.usingStorageAccess();
    270  info.mIsThirdPartyContext = aData.isThirdPartyContext();
    271  info.mOriginAttributes =
    272      BasePrincipal::Cast(principal)->OriginAttributesRef();
    273  info.mShouldResistFingerprinting = aData.shouldResistFingerprinting();
    274  Maybe<RFPTargetSet> overriddenFingerprintingSettings;
    275  if (aData.overriddenFingerprintingSettings().isSome()) {
    276    overriddenFingerprintingSettings.emplace(
    277        aData.overriddenFingerprintingSettings().ref());
    278  }
    279  info.mOverriddenFingerprintingSettings = overriddenFingerprintingSettings;
    280  net::CookieJarSettings::Deserialize(aData.cookieJarSettings(),
    281                                      getter_AddRefs(info.mCookieJarSettings));
    282  info.mCookieJarSettingsArgs = aData.cookieJarSettings();
    283  info.mIsOn3PCBExceptionList = aData.isOn3PCBExceptionList();
    284  info.mSecureContext = aData.isSecureContext()
    285                            ? WorkerLoadInfo::eSecureContext
    286                            : WorkerLoadInfo::eInsecureContext;
    287 
    288  WorkerPrivate::OverrideLoadInfoLoadGroup(info, info.mLoadingPrincipal);
    289 
    290  RefPtr<SharedWorkerInterfaceRequestor> requestor =
    291      new SharedWorkerInterfaceRequestor();
    292  info.mInterfaceRequestor->SetOuterRequestor(requestor);
    293 
    294  Maybe<ClientInfo> clientInfo;
    295  if (aData.clientInfo().isSome()) {
    296    clientInfo.emplace(ClientInfo(aData.clientInfo().ref()));
    297  }
    298 
    299  if (mIsServiceWorker) {
    300    info.mSourceInfo = clientInfo;
    301  } else {
    302    if (clientInfo.isSome()) {
    303      Maybe<mozilla::ipc::PolicyContainerArgs> policyContainerArgs =
    304          clientInfo.ref().GetPolicyContainerArgs();
    305      if (policyContainerArgs.isSome() && policyContainerArgs->csp().isSome()) {
    306        info.mCSP = CSPInfoToCSP(*policyContainerArgs->csp(), nullptr);
    307        mozilla::Result<UniquePtr<WorkerCSPContext>, nsresult> ctx =
    308            WorkerCSPContext::CreateFromCSP(info.mCSP);
    309        if (ctx.isErr()) {
    310          return ctx.unwrapErr();
    311        }
    312        info.mCSPContext = ctx.unwrap();
    313      }
    314    }
    315  }
    316 
    317  nsresult rv = info.SetPrincipalsAndCSPOnMainThread(
    318      info.mPrincipal, info.mPartitionedPrincipal, info.mLoadGroup, info.mCSP);
    319  if (NS_WARN_IF(NS_FAILED(rv))) {
    320    return rv;
    321  }
    322 
    323  nsString workerPrivateId;
    324 
    325  if (mIsServiceWorker) {
    326    ServiceWorkerData& data = aData.serviceWorkerData().get_ServiceWorkerData();
    327 
    328    MOZ_ASSERT(!data.id().IsEmpty());
    329    workerPrivateId = std::move(data.id());
    330 
    331    info.mServiceWorkerCacheName = data.cacheName();
    332    info.mServiceWorkerDescriptor.emplace(data.descriptor());
    333    info.mServiceWorkerRegistrationDescriptor.emplace(
    334        data.registrationDescriptor());
    335    info.mLoadFlags = static_cast<nsLoadFlags>(data.loadFlags());
    336  } else {
    337    // Top level workers' main script use the document charset for the script
    338    // uri encoding.
    339    rv = ChannelFromScriptURLMainThread(
    340        info.mLoadingPrincipal, nullptr /* parent document */, info.mLoadGroup,
    341        info.mResolvedScriptURI, aData.workerOptions().mType,
    342        aData.workerOptions().mCredentials, clientInfo,
    343        nsIContentPolicy::TYPE_INTERNAL_SHARED_WORKER, info.mCookieJarSettings,
    344        info.mReferrerInfo, getter_AddRefs(info.mChannel));
    345    if (NS_WARN_IF(NS_FAILED(rv))) {
    346      return rv;
    347    }
    348 
    349    nsCOMPtr<nsILoadInfo> loadInfo = info.mChannel->LoadInfo();
    350 
    351    auto* cspEventListener = new RemoteWorkerCSPEventListener(this);
    352    rv = loadInfo->SetCspEventListener(cspEventListener);
    353    if (NS_WARN_IF(NS_FAILED(rv))) {
    354      return rv;
    355    }
    356  }
    357 
    358  info.mAgentClusterId = aData.agentClusterId();
    359 
    360  AutoJSAPI jsapi;
    361  jsapi.Init();
    362 
    363  ErrorResult error;
    364  RefPtr<RemoteWorkerChild> self = this;
    365  RefPtr<WorkerPrivate> workerPrivate = WorkerPrivate::Constructor(
    366      jsapi.cx(), aData.originalScriptURL(), false,
    367      mIsServiceWorker ? WorkerKindService : WorkerKindShared,
    368      aData.workerOptions().mCredentials, aData.workerOptions().mType,
    369      aData.workerOptions().mName, VoidCString(), &info, error,
    370      std::move(workerPrivateId),
    371      [self](bool aEverRan) {
    372        self->OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled();
    373      },
    374      // This will be invoked here on the main thread when the worker is already
    375      // fully shutdown.  This replaces a prior approach where we would
    376      // begin to transition when the worker thread would reach the Canceling
    377      // state.  This lambda ensures that we not only wait for the Killing state
    378      // to be reached but that the global shutdown has already occurred.
    379      [self]() { self->TransitionStateFromCanceledToKilled(); },
    380      std::move(aChildEp));
    381 
    382  if (NS_WARN_IF(error.Failed())) {
    383    MOZ_ASSERT(!workerPrivate);
    384 
    385    rv = error.StealNSResult();
    386    return rv;
    387  }
    388 
    389  workerPrivate->SetRemoteWorkerController(this);
    390 
    391  // This wants to run as a normal task sequentially after the top level script
    392  // evaluation, so the hybrid target is the correct choice between hybrid and
    393  // `ControlEventTarget`.
    394  nsCOMPtr<nsISerialEventTarget> workerTarget =
    395      workerPrivate->HybridEventTarget();
    396 
    397  nsCOMPtr<nsIRunnable> runnable = NewCancelableRunnableMethod(
    398      "InitialzeOnWorker", this, &RemoteWorkerChild::InitializeOnWorker);
    399 
    400  {
    401    MOZ_ASSERT(workerPrivate);
    402    auto lock = mState.Lock();
    403    // We MUST be pending here, so direct access is ok.
    404    lock->as<Pending>().mWorkerPrivate = std::move(workerPrivate);
    405  }
    406 
    407  if (mIsServiceWorker) {
    408    nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    409        __func__, [workerTarget,
    410                   initializeWorkerRunnable = std::move(runnable)]() mutable {
    411          (void)NS_WARN_IF(NS_FAILED(
    412              workerTarget->Dispatch(initializeWorkerRunnable.forget())));
    413        });
    414 
    415    RefPtr<PermissionManager> permissionManager =
    416        PermissionManager::GetInstance();
    417    if (!permissionManager) {
    418      return NS_ERROR_FAILURE;
    419    }
    420    permissionManager->WhenPermissionsAvailable(principal, r);
    421  } else {
    422    if (NS_WARN_IF(NS_FAILED(workerTarget->Dispatch(runnable.forget())))) {
    423      rv = NS_ERROR_FAILURE;
    424      return rv;
    425    }
    426  }
    427 
    428  scopeExit.release();
    429 
    430  return NS_OK;
    431 }
    432 
    433 void RemoteWorkerChild::RequestWorkerCancellation() {
    434  MOZ_ASSERT(NS_IsMainThread());
    435 
    436  LOG(("RequestWorkerCancellation[this=%p]", this));
    437 
    438  // We want to ensure that we've requested the worker be canceled.  So if the
    439  // worker is running, cancel it.  We can't do this with the lock held,
    440  // however, because our lambdas will want to manipulate the state.
    441  RefPtr<WorkerPrivate> cancelWith;
    442  {
    443    auto lock = mState.Lock();
    444    if (lock->is<Pending>()) {
    445      cancelWith = lock->as<Pending>().mWorkerPrivate;
    446    } else if (lock->is<Running>()) {
    447      cancelWith = lock->as<Running>().mWorkerPrivate;
    448    }
    449  }
    450 
    451  if (cancelWith) {
    452    cancelWith->Cancel();
    453  }
    454 }
    455 
    456 // This method will be invoked on the worker after the top-level
    457 // CompileScriptRunnable task has succeeded and as long as the worker has not
    458 // been closed/canceled.  There are edge-cases related to cancellation, but we
    459 // have our caller ensure that we are only called as long as the worker's state
    460 // is Running.
    461 //
    462 // (https://bugzilla.mozilla.org/show_bug.cgi?id=1800659 will eliminate
    463 // cancellation, and the documentation around that bug / in design documents
    464 // helps provide more context about this.)
    465 void RemoteWorkerChild::InitializeOnWorker() {
    466  nsCOMPtr<nsIRunnable> r =
    467      NewRunnableMethod("TransitionStateToRunning", this,
    468                        &RemoteWorkerChild::TransitionStateToRunning);
    469  MOZ_ALWAYS_SUCCEEDS(SchedulerGroup::Dispatch(r.forget()));
    470 }
    471 
    472 RefPtr<GenericNonExclusivePromise> RemoteWorkerChild::GetTerminationPromise() {
    473  auto launcherData = mLauncherData.Access();
    474  return launcherData->mTerminationPromise.Ensure(__func__);
    475 }
    476 
    477 void RemoteWorkerChild::CreationSucceededOnAnyThread() {
    478  CreationSucceededOrFailedOnAnyThread(true);
    479 }
    480 
    481 void RemoteWorkerChild::CreationFailedOnAnyThread() {
    482  CreationSucceededOrFailedOnAnyThread(false);
    483 }
    484 
    485 void RemoteWorkerChild::CreationSucceededOrFailedOnAnyThread(
    486    bool aDidCreationSucceed) {
    487 #ifdef DEBUG
    488  {
    489    auto lock = mState.Lock();
    490    MOZ_ASSERT_IF(aDidCreationSucceed, lock->is<Running>());
    491    MOZ_ASSERT_IF(!aDidCreationSucceed, lock->is<Killed>());
    492  }
    493 #endif
    494 
    495  RefPtr<RemoteWorkerChild> self = this;
    496 
    497  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    498      __func__,
    499      [self = std::move(self), didCreationSucceed = aDidCreationSucceed] {
    500        auto launcherData = self->mLauncherData.Access();
    501 
    502        if (!self->CanSend() || launcherData->mDidSendCreated) {
    503          return;
    504        }
    505 
    506        (void)self->SendCreated(didCreationSucceed);
    507        launcherData->mDidSendCreated = true;
    508      });
    509 
    510  GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    511 }
    512 
    513 void RemoteWorkerChild::CloseWorkerOnMainThread() {
    514  AssertIsOnMainThread();
    515 
    516  LOG(("CloseWorkerOnMainThread[this=%p]", this));
    517 
    518  // We can't hold the state lock while calling WorkerPrivate::Cancel because
    519  // the lambda callback will want to touch the state, so save off the
    520  // WorkerPrivate so we can cancel it (if we need to cancel it).
    521  RefPtr<WorkerPrivate> cancelWith;
    522  {
    523    auto lock = mState.Lock();
    524 
    525    if (lock->is<Pending>()) {
    526      cancelWith = lock->as<Pending>().mWorkerPrivate;
    527      // There should be no way for this code to run before we
    528      // ExecWorkerOnMainThread runs, which means that either it should have
    529      // set a WorkerPrivate on Pending, or its error handling should already
    530      // have transitioned us to Canceled and Killing in that order.  (It's
    531      // also possible that it assigned a WorkerPrivate and subsequently we
    532      // transitioned to Running, which would put us in the next branch.)
    533      MOZ_DIAGNOSTIC_ASSERT(cancelWith);
    534    } else if (lock->is<Running>()) {
    535      cancelWith = lock->as<Running>().mWorkerPrivate;
    536    }
    537  }
    538 
    539  // It's very okay for us to not have a WorkerPrivate here if we've already
    540  // canceled the worker or if errors happened.
    541  if (cancelWith) {
    542    cancelWith->Cancel();
    543  }
    544 }
    545 
    546 /**
    547 * Error reporting method
    548 */
    549 void RemoteWorkerChild::ErrorPropagation(const ErrorValue& aValue) {
    550  MOZ_ASSERT(GetActorEventTarget()->IsOnCurrentThread());
    551 
    552  if (!CanSend()) {
    553    return;
    554  }
    555 
    556  (void)SendError(aValue);
    557 }
    558 
    559 void RemoteWorkerChild::ErrorPropagationDispatch(nsresult aError) {
    560  MOZ_ASSERT(NS_FAILED(aError));
    561 
    562  RefPtr<RemoteWorkerChild> self = this;
    563  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    564      "RemoteWorkerChild::ErrorPropagationDispatch",
    565      [self = std::move(self), aError]() { self->ErrorPropagation(aError); });
    566 
    567  GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    568 }
    569 
    570 void RemoteWorkerChild::ErrorPropagationOnMainThread(
    571    const WorkerErrorReport* aReport, bool aIsErrorEvent) {
    572  AssertIsOnMainThread();
    573 
    574  ErrorValue value;
    575  if (aIsErrorEvent) {
    576    ErrorData data(
    577        aReport->mIsWarning, aReport->mLineNumber, aReport->mColumnNumber,
    578        aReport->mMessage, aReport->mFilename,
    579        TransformIntoNewArray(aReport->mNotes, [](const WorkerErrorNote& note) {
    580          return ErrorDataNote(note.mLineNumber, note.mColumnNumber,
    581                               note.mMessage, note.mFilename);
    582        }));
    583    value = data;
    584  } else {
    585    value = void_t();
    586  }
    587 
    588  RefPtr<RemoteWorkerChild> self = this;
    589  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    590      "RemoteWorkerChild::ErrorPropagationOnMainThread",
    591      [self = std::move(self), value]() { self->ErrorPropagation(value); });
    592 
    593  GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    594 }
    595 
    596 void RemoteWorkerChild::CSPViolationPropagationOnMainThread(
    597    const nsAString& aJSON) {
    598  AssertIsOnMainThread();
    599 
    600  RefPtr<RemoteWorkerChild> self = this;
    601  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(
    602      "RemoteWorkerChild::ErrorPropagationDispatch",
    603      [self = std::move(self), json = nsString(aJSON)]() {
    604        CSPViolation violation(json);
    605        self->ErrorPropagation(violation);
    606      });
    607 
    608  GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    609 }
    610 
    611 void RemoteWorkerChild::NotifyLock(bool aCreated) {
    612  nsCOMPtr<nsIRunnable> r =
    613      NS_NewRunnableFunction(__func__, [self = RefPtr(this), aCreated] {
    614        if (!self->CanSend()) {
    615          return;
    616        }
    617 
    618        (void)self->SendNotifyLock(aCreated);
    619      });
    620 
    621  GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    622 }
    623 
    624 void RemoteWorkerChild::NotifyWebTransport(bool aCreated) {
    625  nsCOMPtr<nsIRunnable> r =
    626      NS_NewRunnableFunction(__func__, [self = RefPtr(this), aCreated] {
    627        if (!self->CanSend()) {
    628          return;
    629        }
    630 
    631        (void)self->SendNotifyWebTransport(aCreated);
    632      });
    633 
    634  GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    635 }
    636 
    637 void RemoteWorkerChild::FlushReportsOnMainThread(
    638    nsIConsoleReportCollector* aReporter) {
    639  AssertIsOnMainThread();
    640 
    641  bool reportErrorToBrowserConsole = true;
    642 
    643  // Flush the reports.
    644  for (uint32_t i = 0, len = mWindowIDs.Length(); i < len; ++i) {
    645    aReporter->FlushReportsToConsole(
    646        mWindowIDs[i], nsIConsoleReportCollector::ReportAction::Save);
    647    reportErrorToBrowserConsole = false;
    648  }
    649 
    650  // Finally report to browser console if there is no any window.
    651  if (reportErrorToBrowserConsole) {
    652    aReporter->FlushReportsToConsole(0);
    653    return;
    654  }
    655 
    656  aReporter->ClearConsoleReports();
    657 }
    658 
    659 /**
    660 * Worker state transition methods
    661 */
    662 void RemoteWorkerChild::
    663    OnWorkerCancellationTransitionStateFromPendingOrRunningToCanceled() {
    664  auto lock = mState.Lock();
    665 
    666  LOG(("TransitionStateFromPendingOrRunningToCanceled[this=%p]", this));
    667 
    668  if (lock->is<Pending>()) {
    669    TransitionStateFromPendingToCanceled(lock.ref());
    670  } else if (lock->is<Running>()) {
    671    *lock = VariantType<remoteworker::Canceled>();
    672  } else {
    673    MOZ_ASSERT(false, "State should have been Pending or Running");
    674  }
    675 }
    676 
    677 void RemoteWorkerChild::TransitionStateFromPendingToCanceled(
    678    RemoteWorkerState& aState) {
    679  AssertIsOnMainThread();
    680  MOZ_ASSERT(aState.is<Pending>());
    681  LOG(("TransitionStateFromPendingToCanceled[this=%p]", this));
    682 
    683  CancelAllPendingOps(aState);
    684 
    685  aState = VariantType<remoteworker::Canceled>();
    686 }
    687 
    688 void RemoteWorkerChild::TransitionStateFromCanceledToKilled() {
    689  AssertIsOnMainThread();
    690 
    691  LOG(("TransitionStateFromCanceledToKilled[this=%p]", this));
    692 
    693  auto lock = mState.Lock();
    694  MOZ_ASSERT(lock->is<Canceled>());
    695 
    696  *lock = VariantType<remoteworker::Killed>();
    697 
    698  RefPtr<RemoteWorkerChild> self = this;
    699  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self]() {
    700    auto launcherData = self->mLauncherData.Access();
    701 
    702    // (We maintain the historical ordering of resolving this promise prior to
    703    // calling SendClose, however the previous code used 2 separate dispatches
    704    // to this thread for the resolve and SendClose, and there inherently
    705    // would be a race between the runnables resulting from the resolved
    706    // promise and the promise containing the call to SendClose.  Now it's
    707    // entirely clear that our call to SendClose will effectively run before
    708    // any of the resolved promises are able to do anything.)
    709    launcherData->mTerminationPromise.ResolveIfExists(true, __func__);
    710 
    711    if (self->CanSend()) {
    712      (void)self->SendClose();
    713    }
    714  });
    715 
    716  GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    717 }
    718 
    719 void RemoteWorkerChild::TransitionStateToRunning() {
    720  AssertIsOnMainThread();
    721 
    722  LOG(("TransitionStateToRunning[this=%p]", this));
    723 
    724  nsTArray<RefPtr<RemoteWorkerOp>> pendingOps;
    725 
    726  {
    727    auto lock = mState.Lock();
    728 
    729    // Because this is an async notification sent from the worker to the main
    730    // thread, it's very possible that we've already decided on the main thread
    731    // to transition to the Canceled state, in which case there is nothing for
    732    // us to do here.
    733    if (!lock->is<Pending>()) {
    734      LOG(("State is already not pending in TransitionStateToRunning[this=%p]!",
    735           this));
    736      return;
    737    }
    738 
    739    RefPtr<WorkerPrivate> workerPrivate =
    740        std::move(lock->as<Pending>().mWorkerPrivate);
    741    pendingOps = std::move(lock->as<Pending>().mPendingOps);
    742 
    743    // Move the worker private into place to avoid gratuitous ref churn; prior
    744    // comments here suggest the Variant can't accept a move.
    745    *lock = VariantType<remoteworker::Running>();
    746    lock->as<Running>().mWorkerPrivate = std::move(workerPrivate);
    747  }
    748 
    749  CreationSucceededOnAnyThread();
    750 
    751  RefPtr<RemoteWorkerChild> self = this;
    752  for (auto& op : pendingOps) {
    753    op->StartOnMainThread(self);
    754  }
    755 }
    756 
    757 void RemoteWorkerChild::ExceptionalErrorTransitionDuringExecWorker() {
    758  AssertIsOnMainThread();
    759 
    760  LOG(("ExceptionalErrorTransitionDuringExecWorker[this=%p]", this));
    761 
    762  // This method is called synchronously by ExecWorkerOnMainThread in the event
    763  // of any error.  Because we only transition to Running on the main thread
    764  // as the result of a notification from the worker, we know our state will be
    765  // Pending, but mWorkerPrivate may or may not be null, as we may not have
    766  // gotten to spawning the worker.
    767  //
    768  // In the event the worker exists, we need to Cancel() it.  We must do this
    769  // without the lock held because our call to Cancel() will invoke the
    770  // cancellation callback we created which will call TransitionStateToCanceled,
    771  // and we can't be holding the lock when that happens.
    772 
    773  RefPtr<WorkerPrivate> cancelWith;
    774 
    775  {
    776    auto lock = mState.Lock();
    777 
    778    MOZ_ASSERT(lock->is<Pending>());
    779    if (lock->is<Pending>()) {
    780      cancelWith = lock->as<Pending>().mWorkerPrivate;
    781      if (!cancelWith) {
    782        // The worker wasn't actually created, so we should synthetically
    783        // transition to canceled and onward.  Since we have the lock,
    784        // perform the transition now for clarity, but we'll handle the rest of
    785        // this case after dropping the lock.
    786        TransitionStateFromPendingToCanceled(lock.ref());
    787      }
    788    }
    789  }
    790 
    791  if (cancelWith) {
    792    cancelWith->Cancel();
    793  } else {
    794    TransitionStateFromCanceledToKilled();
    795    CreationFailedOnAnyThread();
    796  }
    797 }
    798 
    799 void RemoteWorkerChild::CancelAllPendingOps(RemoteWorkerState& aState) {
    800  MOZ_ASSERT(aState.is<Pending>());
    801 
    802  auto pendingOps = std::move(aState.as<Pending>().mPendingOps);
    803 
    804  for (auto& op : pendingOps) {
    805    op->Cancel();
    806  }
    807 }
    808 
    809 void RemoteWorkerChild::PendRemoteWorkerOp(RefPtr<RemoteWorkerOp> aOp) {
    810  MOZ_ASSERT_DEBUG_OR_FUZZING(mIsThawing);
    811 
    812  auto pendingOps = mPendingOps.Lock();
    813 
    814  pendingOps->AppendElement(std::move(aOp));
    815 }
    816 
    817 void RemoteWorkerChild::RunAllPendingOpsOnMainThread() {
    818  RefPtr<RemoteWorkerChild> self = this;
    819 
    820  auto pendingOps = mPendingOps.Lock();
    821 
    822  for (auto& op : *pendingOps) {
    823    op->StartOnMainThread(self);
    824  }
    825 
    826  pendingOps->Clear();
    827 }
    828 
    829 void RemoteWorkerChild::MaybeStartOp(RefPtr<RemoteWorkerOp>&& aOp) {
    830  MOZ_ASSERT(aOp);
    831 
    832  if (mIsThawing) {
    833    PendRemoteWorkerOp(std::move(aOp));
    834    return;
    835  }
    836 
    837  auto lock = mState.Lock();
    838 
    839  if (!aOp->MaybeStart(this, lock.ref())) {
    840    // Maybestart returns false only if we are <Pending>.
    841    lock->as<Pending>().mPendingOps.AppendElement(std::move(aOp));
    842  }
    843 }
    844 
    845 IPCResult RemoteWorkerChild::RecvExecOp(SharedWorkerOpArgs&& aOpArgs) {
    846  MOZ_ASSERT(!mIsServiceWorker);
    847 
    848  MaybeStartOp(new SharedWorkerOp(std::move(aOpArgs)));
    849 
    850  return IPC_OK();
    851 }
    852 
    853 IPCResult RemoteWorkerChild::RecvExecServiceWorkerOp(
    854    ServiceWorkerOpArgs&& aArgs, ExecServiceWorkerOpResolver&& aResolve) {
    855  MOZ_ASSERT(mIsServiceWorker);
    856  MOZ_ASSERT(
    857      aArgs.type() !=
    858          ServiceWorkerOpArgs::TParentToChildServiceWorkerFetchEventOpArgs,
    859      "FetchEvent operations should be sent via PFetchEventOp(Proxy) actors!");
    860 
    861  MaybeReportServiceWorkerShutdownProgress(aArgs);
    862 
    863  MaybeStartOp(ServiceWorkerOp::Create(std::move(aArgs), std::move(aResolve)));
    864 
    865  return IPC_OK();
    866 }
    867 
    868 RefPtr<GenericPromise>
    869 RemoteWorkerChild::MaybeSendSetServiceWorkerSkipWaitingFlag() {
    870  RefPtr<GenericPromise::Private> promise =
    871      new GenericPromise::Private(__func__);
    872 
    873  RefPtr<RemoteWorkerChild> self = this;
    874 
    875  nsCOMPtr<nsIRunnable> r = NS_NewRunnableFunction(__func__, [self = std::move(
    876                                                                  self),
    877                                                              promise] {
    878    if (!self->CanSend()) {
    879      promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
    880      return;
    881    }
    882 
    883    self->SendSetServiceWorkerSkipWaitingFlag()->Then(
    884        GetCurrentSerialEventTarget(), __func__,
    885        [promise](
    886            const SetServiceWorkerSkipWaitingFlagPromise::ResolveOrRejectValue&
    887                aResult) {
    888          if (NS_WARN_IF(aResult.IsReject())) {
    889            promise->Reject(NS_ERROR_DOM_ABORT_ERR, __func__);
    890            return;
    891          }
    892 
    893          promise->Resolve(aResult.ResolveValue(), __func__);
    894        });
    895  });
    896 
    897  GetActorEventTarget()->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
    898 
    899  return promise;
    900 }
    901 
    902 /**
    903 * PFetchEventOpProxy methods
    904 */
    905 already_AddRefed<PFetchEventOpProxyChild>
    906 RemoteWorkerChild::AllocPFetchEventOpProxyChild(
    907    const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
    908  return RefPtr{new FetchEventOpProxyChild()}.forget();
    909 }
    910 
    911 IPCResult RemoteWorkerChild::RecvPFetchEventOpProxyConstructor(
    912    PFetchEventOpProxyChild* aActor,
    913    const ParentToChildServiceWorkerFetchEventOpArgs& aArgs) {
    914  MOZ_ASSERT(aActor);
    915 
    916  (static_cast<FetchEventOpProxyChild*>(aActor))->Initialize(aArgs);
    917 
    918  return IPC_OK();
    919 }
    920 
    921 }  // namespace dom
    922 }  // namespace mozilla