tor-browser

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

SharedWorkerManager.cpp (10820B)


      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 "SharedWorkerManager.h"
      8 
      9 #include "SharedWorkerParent.h"
     10 #include "SharedWorkerService.h"
     11 #include "mozilla/AppShutdown.h"
     12 #include "mozilla/dom/MessagePort.h"
     13 #include "mozilla/dom/PSharedWorker.h"
     14 #include "mozilla/dom/RemoteWorkerController.h"
     15 #include "mozilla/ipc/BackgroundParent.h"
     16 #include "mozilla/ipc/URIUtils.h"
     17 #include "nsIConsoleReportCollector.h"
     18 #include "nsIPrincipal.h"
     19 #include "nsProxyRelease.h"
     20 
     21 namespace mozilla::dom {
     22 
     23 // static
     24 already_AddRefed<SharedWorkerManagerHolder> SharedWorkerManager::Create(
     25    SharedWorkerService* aService, nsIEventTarget* aPBackgroundEventTarget,
     26    const RemoteWorkerData& aData, nsIPrincipal* aLoadingPrincipal,
     27    const OriginAttributes& aEffectiveStoragePrincipalAttrs) {
     28  MOZ_ASSERT(NS_IsMainThread());
     29 
     30  RefPtr<SharedWorkerManager> manager =
     31      new SharedWorkerManager(aPBackgroundEventTarget, aData, aLoadingPrincipal,
     32                              aEffectiveStoragePrincipalAttrs);
     33 
     34  RefPtr<SharedWorkerManagerHolder> holder =
     35      new SharedWorkerManagerHolder(manager, aService);
     36  return holder.forget();
     37 }
     38 
     39 SharedWorkerManager::SharedWorkerManager(
     40    nsIEventTarget* aPBackgroundEventTarget, const RemoteWorkerData& aData,
     41    nsIPrincipal* aLoadingPrincipal,
     42    const OriginAttributes& aEffectiveStoragePrincipalAttrs)
     43    : mPBackgroundEventTarget(aPBackgroundEventTarget),
     44      mLoadingPrincipal(aLoadingPrincipal),
     45      mDomain(aData.domain()),
     46      mEffectiveStoragePrincipalAttrs(aEffectiveStoragePrincipalAttrs),
     47      mResolvedScriptURL(DeserializeURI(aData.resolvedScriptURL())),
     48      mWorkerOptions(aData.workerOptions()),
     49      mIsSecureContext(aData.isSecureContext()),
     50      mSuspended(false),
     51      mFrozen(false) {
     52  MOZ_ASSERT(NS_IsMainThread());
     53  MOZ_ASSERT(aLoadingPrincipal);
     54 }
     55 
     56 SharedWorkerManager::~SharedWorkerManager() {
     57  NS_ReleaseOnMainThread("SharedWorkerManager::mLoadingPrincipal",
     58                         mLoadingPrincipal.forget());
     59  NS_ProxyRelease("SharedWorkerManager::mRemoteWorkerController",
     60                  mPBackgroundEventTarget, mRemoteWorkerController.forget());
     61 }
     62 
     63 bool SharedWorkerManager::MaybeCreateRemoteWorker(
     64    const RemoteWorkerData& aData, uint64_t aWindowID,
     65    UniqueMessagePortId& aPortIdentifier, base::ProcessId aProcessId) {
     66  ::mozilla::ipc::AssertIsOnBackgroundThread();
     67 
     68  // Creating remote workers may result in creating new processes, but during
     69  // parent shutdown that would add just noise, so better bail out.
     70  if (AppShutdown::IsInOrBeyond(ShutdownPhase::AppShutdownConfirmed)) {
     71    return false;
     72  }
     73 
     74  if (!mRemoteWorkerController) {
     75    mRemoteWorkerController =
     76        RemoteWorkerController::Create(aData, this, aProcessId);
     77    if (NS_WARN_IF(!mRemoteWorkerController)) {
     78      return false;
     79    }
     80  }
     81 
     82  if (aWindowID) {
     83    mRemoteWorkerController->AddWindowID(aWindowID);
     84  }
     85 
     86  mRemoteWorkerController->AddPortIdentifier(aPortIdentifier.release());
     87  return true;
     88 }
     89 
     90 already_AddRefed<SharedWorkerManagerHolder>
     91 SharedWorkerManager::MatchOnMainThread(
     92    SharedWorkerService* aService, const RemoteWorkerData& aData,
     93    nsIURI* aScriptURL, nsIPrincipal* aLoadingPrincipal,
     94    const OriginAttributes& aEffectiveStoragePrincipalAttrs,
     95    bool* aMatchNameButNotOptions) {
     96  MOZ_ASSERT(NS_IsMainThread());
     97  MOZ_ASSERT(aMatchNameButNotOptions);
     98 
     99  bool urlEquals;
    100  if (NS_FAILED(aScriptURL->Equals(mResolvedScriptURL, &urlEquals))) {
    101    return nullptr;
    102  }
    103 
    104  bool match =
    105      aData.domain() == mDomain && urlEquals &&
    106      aData.workerOptions().mName == mWorkerOptions.mName &&
    107      // We want to be sure that the window's principal subsumes the
    108      // SharedWorker's loading principal and vice versa.
    109      mLoadingPrincipal->Subsumes(aLoadingPrincipal) &&
    110      aLoadingPrincipal->Subsumes(mLoadingPrincipal) &&
    111      mEffectiveStoragePrincipalAttrs == aEffectiveStoragePrincipalAttrs;
    112  if (!match) {
    113    return nullptr;
    114  }
    115 
    116  *aMatchNameButNotOptions =
    117      aData.workerOptions().mType != mWorkerOptions.mType ||
    118      aData.workerOptions().mCredentials != mWorkerOptions.mCredentials;
    119 
    120  if (*aMatchNameButNotOptions) {
    121    return nullptr;
    122  }
    123 
    124  RefPtr<SharedWorkerManagerHolder> holder =
    125      new SharedWorkerManagerHolder(this, aService);
    126  return holder.forget();
    127 }
    128 
    129 void SharedWorkerManager::AddActor(SharedWorkerParent* aParent) {
    130  ::mozilla::ipc::AssertIsOnBackgroundThread();
    131  MOZ_ASSERT(aParent);
    132  MOZ_ASSERT(!mActors.Contains(aParent));
    133 
    134  mActors.AppendElement(aParent);
    135 
    136  if (mLockCount) {
    137    (void)aParent->SendNotifyLock(true);
    138  }
    139 
    140  if (mWebTransportCount) {
    141    (void)aParent->SendNotifyWebTransport(true);
    142  }
    143 
    144  // NB: We don't update our Suspended/Frozen state here, yet. The aParent is
    145  // responsible for doing so from SharedWorkerParent::ManagerCreated.
    146  // XXX But we could avoid iterating all of our actors because if aParent is
    147  // not frozen and we are, we would just need to thaw ourselves.
    148 }
    149 
    150 void SharedWorkerManager::RemoveActor(SharedWorkerParent* aParent) {
    151  ::mozilla::ipc::AssertIsOnBackgroundThread();
    152  MOZ_ASSERT(aParent);
    153  MOZ_ASSERT(mActors.Contains(aParent));
    154 
    155  uint64_t windowID = aParent->WindowID();
    156  if (windowID) {
    157    mRemoteWorkerController->RemoveWindowID(windowID);
    158  }
    159 
    160  mActors.RemoveElement(aParent);
    161 
    162  if (!mActors.IsEmpty()) {
    163    // Our remaining actors could be all suspended or frozen.
    164    UpdateSuspend();
    165    UpdateFrozen();
    166    return;
    167  }
    168 }
    169 
    170 void SharedWorkerManager::Terminate() {
    171  ::mozilla::ipc::AssertIsOnBackgroundThread();
    172  MOZ_ASSERT(mActors.IsEmpty());
    173  MOZ_ASSERT(mHolders.IsEmpty());
    174 
    175  // mRemoteWorkerController creation can fail. If the creation fails
    176  // mRemoteWorkerController is nullptr and we should stop termination here.
    177  if (!mRemoteWorkerController) {
    178    return;
    179  }
    180 
    181  mRemoteWorkerController->Terminate();
    182  mRemoteWorkerController = nullptr;
    183 }
    184 
    185 void SharedWorkerManager::UpdateSuspend() {
    186  ::mozilla::ipc::AssertIsOnBackgroundThread();
    187  MOZ_ASSERT(mRemoteWorkerController);
    188 
    189  uint32_t suspended = 0;
    190 
    191  for (SharedWorkerParent* actor : mActors) {
    192    if (actor->IsSuspended()) {
    193      ++suspended;
    194    }
    195  }
    196 
    197  // Call Suspend only when all of our actors' windows are suspended and call
    198  // Resume only when one of them resumes.
    199  if ((mSuspended && suspended == mActors.Length()) ||
    200      (!mSuspended && suspended != mActors.Length())) {
    201    return;
    202  }
    203 
    204  if (!mSuspended) {
    205    mSuspended = true;
    206    mRemoteWorkerController->Suspend();
    207  } else {
    208    mSuspended = false;
    209    mRemoteWorkerController->Resume();
    210  }
    211 }
    212 
    213 void SharedWorkerManager::UpdateFrozen() {
    214  ::mozilla::ipc::AssertIsOnBackgroundThread();
    215  MOZ_ASSERT(mRemoteWorkerController);
    216 
    217  uint32_t frozen = 0;
    218 
    219  for (SharedWorkerParent* actor : mActors) {
    220    if (actor->IsFrozen()) {
    221      ++frozen;
    222    }
    223  }
    224 
    225  // Similar to UpdateSuspend, above, we only want to be frozen when all of our
    226  // actors are frozen.
    227  if ((mFrozen && frozen == mActors.Length()) ||
    228      (!mFrozen && frozen != mActors.Length())) {
    229    return;
    230  }
    231 
    232  if (!mFrozen) {
    233    mFrozen = true;
    234    mRemoteWorkerController->Freeze();
    235  } else {
    236    mFrozen = false;
    237    mRemoteWorkerController->Thaw();
    238  }
    239 }
    240 
    241 bool SharedWorkerManager::IsSecureContext() const { return mIsSecureContext; }
    242 
    243 void SharedWorkerManager::CreationFailed() {
    244  ::mozilla::ipc::AssertIsOnBackgroundThread();
    245 
    246  for (SharedWorkerParent* actor : mActors) {
    247    (void)actor->SendError(NS_ERROR_FAILURE);
    248  }
    249 }
    250 
    251 void SharedWorkerManager::CreationSucceeded() {
    252  ::mozilla::ipc::AssertIsOnBackgroundThread();
    253  // Nothing to do here.
    254 }
    255 
    256 void SharedWorkerManager::ErrorReceived(const ErrorValue& aValue) {
    257  ::mozilla::ipc::AssertIsOnBackgroundThread();
    258 
    259  for (SharedWorkerParent* actor : mActors) {
    260    (void)actor->SendError(aValue);
    261  }
    262 }
    263 
    264 void SharedWorkerManager::LockNotified(bool aCreated) {
    265  ::mozilla::ipc::AssertIsOnBackgroundThread();
    266  MOZ_ASSERT_IF(!aCreated, mLockCount > 0);
    267 
    268  mLockCount += aCreated ? 1 : -1;
    269 
    270  // Notify only when we either:
    271  // 1. Got a new lock when nothing were there
    272  // 2. Lost all locks
    273  if ((aCreated && mLockCount == 1) || !mLockCount) {
    274    for (SharedWorkerParent* actor : mActors) {
    275      (void)actor->SendNotifyLock(aCreated);
    276    }
    277  }
    278 };
    279 
    280 void SharedWorkerManager::WebTransportNotified(bool aCreated) {
    281  ::mozilla::ipc::AssertIsOnBackgroundThread();
    282  MOZ_ASSERT_IF(!aCreated, mWebTransportCount > 0);
    283 
    284  mWebTransportCount += aCreated ? 1 : -1;
    285 
    286  // Notify only when we either:
    287  // 1. Got a first WebTransport
    288  // 2. The last WebTransport goes away
    289  if ((aCreated && mWebTransportCount == 1) || mWebTransportCount == 0) {
    290    for (SharedWorkerParent* actor : mActors) {
    291      (void)actor->SendNotifyWebTransport(aCreated);
    292    }
    293  }
    294 };
    295 
    296 void SharedWorkerManager::Terminated() {
    297  ::mozilla::ipc::AssertIsOnBackgroundThread();
    298 
    299  for (SharedWorkerParent* actor : mActors) {
    300    (void)actor->SendTerminate();
    301  }
    302 }
    303 
    304 void SharedWorkerManager::RegisterHolder(SharedWorkerManagerHolder* aHolder) {
    305  MOZ_ASSERT(NS_IsMainThread());
    306  MOZ_ASSERT(aHolder);
    307  MOZ_ASSERT(!mHolders.Contains(aHolder));
    308 
    309  mHolders.AppendElement(aHolder);
    310 }
    311 
    312 void SharedWorkerManager::UnregisterHolder(SharedWorkerManagerHolder* aHolder) {
    313  MOZ_ASSERT(NS_IsMainThread());
    314  MOZ_ASSERT(aHolder);
    315  MOZ_ASSERT(mHolders.Contains(aHolder));
    316 
    317  mHolders.RemoveElement(aHolder);
    318 
    319  if (!mHolders.IsEmpty()) {
    320    return;
    321  }
    322 
    323  // Time to go.
    324 
    325  aHolder->Service()->RemoveWorkerManagerOnMainThread(this);
    326 
    327  RefPtr<SharedWorkerManager> self = this;
    328  mPBackgroundEventTarget->Dispatch(
    329      NS_NewRunnableFunction(
    330          "SharedWorkerService::RemoveWorkerManagerOnMainThread",
    331          [self]() { self->Terminate(); }),
    332      NS_DISPATCH_NORMAL);
    333 }
    334 
    335 SharedWorkerManagerHolder::SharedWorkerManagerHolder(
    336    SharedWorkerManager* aManager, SharedWorkerService* aService)
    337    : mManager(aManager), mService(aService) {
    338  MOZ_ASSERT(NS_IsMainThread());
    339  MOZ_ASSERT(aManager);
    340  MOZ_ASSERT(aService);
    341 
    342  aManager->RegisterHolder(this);
    343 }
    344 
    345 SharedWorkerManagerHolder::~SharedWorkerManagerHolder() {
    346  MOZ_ASSERT(NS_IsMainThread());
    347  mManager->UnregisterHolder(this);
    348 }
    349 
    350 SharedWorkerManagerWrapper::SharedWorkerManagerWrapper(
    351    already_AddRefed<SharedWorkerManagerHolder> aHolder)
    352    : mHolder(aHolder) {
    353  MOZ_ASSERT(NS_IsMainThread());
    354 }
    355 
    356 SharedWorkerManagerWrapper::~SharedWorkerManagerWrapper() {
    357  NS_ReleaseOnMainThread("SharedWorkerManagerWrapper::mHolder",
    358                         mHolder.forget());
    359 }
    360 
    361 }  // namespace mozilla::dom