tor-browser

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

SharedWorker.cpp (14791B)


      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 file,
      5 * You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "SharedWorker.h"
      8 
      9 #include "mozilla/AntiTrackingUtils.h"
     10 #include "mozilla/AsyncEventDispatcher.h"
     11 #include "mozilla/BasePrincipal.h"
     12 #include "mozilla/EventDispatcher.h"
     13 #include "mozilla/StorageAccess.h"
     14 #include "mozilla/dom/ClientInfo.h"
     15 #include "mozilla/dom/Event.h"
     16 #include "mozilla/dom/MessageChannel.h"
     17 #include "mozilla/dom/MessagePort.h"
     18 #include "mozilla/dom/PMessagePort.h"
     19 #include "mozilla/dom/RemoteWorkerManager.h"  // RemoteWorkerManager::GetRemoteType
     20 #include "mozilla/dom/RemoteWorkerTypes.h"
     21 #include "mozilla/dom/SharedWorkerBinding.h"
     22 #include "mozilla/dom/SharedWorkerChild.h"
     23 #include "mozilla/dom/TrustedTypeUtils.h"
     24 #include "mozilla/dom/TrustedTypesConstants.h"
     25 #include "mozilla/dom/WorkerBinding.h"
     26 #include "mozilla/dom/WorkerLoadInfo.h"
     27 #include "mozilla/dom/WorkerPrivate.h"
     28 #include "mozilla/ipc/BackgroundChild.h"
     29 #include "mozilla/ipc/BackgroundUtils.h"
     30 #include "mozilla/ipc/PBackgroundChild.h"
     31 #include "mozilla/ipc/URIUtils.h"
     32 #include "mozilla/net/CookieJarSettings.h"
     33 #include "nsGlobalWindowInner.h"
     34 #include "nsPIDOMWindow.h"
     35 
     36 #ifdef XP_WIN
     37 #  undef PostMessage
     38 #endif
     39 
     40 using namespace mozilla;
     41 using namespace mozilla::dom;
     42 using namespace mozilla::ipc;
     43 
     44 SharedWorker::SharedWorker(nsPIDOMWindowInner* aWindow,
     45                           SharedWorkerChild* aActor, MessagePort* aMessagePort)
     46    : DOMEventTargetHelper(aWindow),
     47      mWindow(aWindow),
     48      mActor(aActor),
     49      mMessagePort(aMessagePort),
     50      mFrozen(false) {
     51  AssertIsOnMainThread();
     52  MOZ_ASSERT(aActor);
     53  MOZ_ASSERT(aMessagePort);
     54 }
     55 
     56 SharedWorker::~SharedWorker() {
     57  AssertIsOnMainThread();
     58  Close();
     59 }
     60 
     61 // static
     62 already_AddRefed<SharedWorker> SharedWorker::Constructor(
     63    const GlobalObject& aGlobal, const TrustedScriptURLOrUSVString& aScriptURL,
     64    const StringOrWorkerOptions& aOptions, ErrorResult& aRv) {
     65  AssertIsOnMainThread();
     66 
     67  if (aOptions.IsString()) {
     68    WorkerOptions options;
     69    options.mName = aOptions.GetAsString();
     70    return SharedWorker::Constructor(aGlobal, aScriptURL, options, aRv);
     71  }
     72 
     73  return SharedWorker::Constructor(aGlobal, aScriptURL,
     74                                   aOptions.GetAsWorkerOptions(), aRv);
     75 }
     76 
     77 // static
     78 already_AddRefed<SharedWorker> SharedWorker::Constructor(
     79    const GlobalObject& aGlobal, const TrustedScriptURLOrUSVString& aScriptURL,
     80    const WorkerOptions& aOptions, ErrorResult& aRv) {
     81  AssertIsOnMainThread();
     82 
     83  nsCOMPtr<nsPIDOMWindowInner> window =
     84      do_QueryInterface(aGlobal.GetAsSupports());
     85  MOZ_ASSERT(window);
     86 
     87  // Our current idiom is that storage-related APIs specialize for the system
     88  // principal themselves, which is consistent with StorageAllowedForwindow not
     89  // specializing for the system principal.  Without this specialization we
     90  // would end up with ePrivateBrowsing for system principaled private browsing
     91  // windows which is explicitly not what we want.  System Principal code always
     92  // should have access to storage.  It may make sense to enhance
     93  // StorageAllowedForWindow in the future to handle this after comprehensive
     94  // auditing.
     95  nsCOMPtr<nsIPrincipal> principal = aGlobal.GetSubjectPrincipal();
     96  StorageAccess storageAllowed;
     97  if (principal && principal->IsSystemPrincipal()) {
     98    storageAllowed = StorageAccess::eAllow;
     99  } else {
    100    storageAllowed = StorageAllowedForWindow(window);
    101  }
    102 
    103  if (storageAllowed == StorageAccess::eDeny) {
    104    aRv.ThrowSecurityError("StorageAccess denied.");
    105    return nullptr;
    106  }
    107 
    108  if (ShouldPartitionStorage(storageAllowed) &&
    109      !StoragePartitioningEnabled(
    110          storageAllowed, window->GetExtantDoc()->CookieJarSettings())) {
    111    aRv.ThrowSecurityError("StoragePartitioning not enabled.");
    112    return nullptr;
    113  }
    114 
    115  // Assert that the principal private browsing state matches the
    116  // StorageAccess value.
    117 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    118  if (storageAllowed == StorageAccess::ePrivateBrowsing) {
    119    uint32_t privateBrowsingId = 0;
    120    if (principal) {
    121      MOZ_ALWAYS_SUCCEEDS(principal->GetPrivateBrowsingId(&privateBrowsingId));
    122    }
    123    MOZ_DIAGNOSTIC_ASSERT(privateBrowsingId != 0);
    124  }
    125 #endif  // MOZ_DIAGNOSTIC_ASSERT_ENABLED
    126 
    127  PBackgroundChild* actorChild = BackgroundChild::GetOrCreateForCurrentThread();
    128  if (!actorChild || !actorChild->CanSend()) {
    129    aRv.ThrowSecurityError("PBackground not available.");
    130    return nullptr;
    131  }
    132 
    133  JSContext* cx = aGlobal.Context();
    134 
    135  constexpr nsLiteralString sink = u"SharedWorker constructor"_ns;
    136  Maybe<nsAutoString> compliantStringHolder;
    137  nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(window);
    138  const nsAString* compliantString =
    139      TrustedTypeUtils::GetTrustedTypesCompliantString(
    140          aScriptURL, sink, kTrustedTypesOnlySinkGroup, *global, principal,
    141          compliantStringHolder, aRv);
    142  if (aRv.Failed()) {
    143    return nullptr;
    144  }
    145 
    146  WorkerLoadInfo loadInfo;
    147  aRv = WorkerPrivate::GetLoadInfo(cx, window, nullptr, *compliantString,
    148                                   aOptions.mType, aOptions.mCredentials, false,
    149                                   WorkerPrivate::OverrideLoadGroup,
    150                                   WorkerKindShared, &loadInfo);
    151  if (NS_WARN_IF(aRv.Failed())) {
    152    return nullptr;
    153  }
    154 
    155  PrincipalInfo principalInfo;
    156  aRv = PrincipalToPrincipalInfo(loadInfo.mPrincipal, &principalInfo);
    157  if (NS_WARN_IF(aRv.Failed())) {
    158    return nullptr;
    159  }
    160 
    161  PrincipalInfo loadingPrincipalInfo;
    162  aRv = PrincipalToPrincipalInfo(loadInfo.mLoadingPrincipal,
    163                                 &loadingPrincipalInfo);
    164  if (NS_WARN_IF(aRv.Failed())) {
    165    return nullptr;
    166  }
    167 
    168  // Here, the PartitionedPrincipal is always equal to the SharedWorker's
    169  // principal because the channel is not opened yet, and, because of this, it's
    170  // not classified. We need to force the correct originAttributes.
    171  //
    172  // The sharedWorker's principal could be a null principal, e.g. loading a
    173  // data url. In this case, we don't need to force the OAs for the partitioned
    174  // principal because creating storage from a null principal will fail anyway.
    175  // We should only do this for content principals.
    176  //
    177  // You can find more details in StoragePrincipalHelper.h
    178  if (ShouldPartitionStorage(storageAllowed) &&
    179      BasePrincipal::Cast(loadInfo.mPrincipal)->IsContentPrincipal()) {
    180    nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window);
    181    if (!sop) {
    182      aRv.ThrowSecurityError("ScriptObjectPrincipal not available.");
    183      return nullptr;
    184    }
    185 
    186    nsIPrincipal* windowPrincipal = sop->GetPrincipal();
    187    if (!windowPrincipal) {
    188      aRv.ThrowSecurityError("WindowPrincipal not available.");
    189      return nullptr;
    190    }
    191 
    192    nsIPrincipal* windowPartitionedPrincipal = sop->PartitionedPrincipal();
    193    if (!windowPartitionedPrincipal) {
    194      aRv.ThrowSecurityError("WindowPartitionedPrincipal not available.");
    195      return nullptr;
    196    }
    197 
    198    if (!windowPrincipal->Equals(windowPartitionedPrincipal)) {
    199      loadInfo.mPartitionedPrincipal =
    200          BasePrincipal::Cast(loadInfo.mPrincipal)
    201              ->CloneForcingOriginAttributes(
    202                  BasePrincipal::Cast(windowPartitionedPrincipal)
    203                      ->OriginAttributesRef());
    204    }
    205  }
    206 
    207  PrincipalInfo partitionedPrincipalInfo;
    208  if (loadInfo.mPrincipal->Equals(loadInfo.mPartitionedPrincipal)) {
    209    partitionedPrincipalInfo = principalInfo;
    210  } else {
    211    aRv = PrincipalToPrincipalInfo(loadInfo.mPartitionedPrincipal,
    212                                   &partitionedPrincipalInfo);
    213    if (NS_WARN_IF(aRv.Failed())) {
    214      return nullptr;
    215    }
    216  }
    217 
    218  // We don't actually care about this MessageChannel, but we use it to 'steal'
    219  // its 2 connected ports.
    220  RefPtr<MessageChannel> channel = MessageChannel::Constructor(global, aRv);
    221  if (NS_WARN_IF(aRv.Failed())) {
    222    return nullptr;
    223  }
    224 
    225  UniqueMessagePortId portIdentifier;
    226  channel->Port1()->CloneAndDisentangle(portIdentifier);
    227 
    228  URIParams resolvedScriptURL;
    229  SerializeURI(loadInfo.mResolvedScriptURI, resolvedScriptURL);
    230 
    231  URIParams baseURL;
    232  SerializeURI(loadInfo.mBaseURI, baseURL);
    233 
    234  // Register this component to PBackground.
    235  bool isSecureContext = JS::GetIsSecureContext(js::GetContextRealm(cx));
    236 
    237  Maybe<IPCClientInfo> ipcClientInfo;
    238  Maybe<ClientInfo> clientInfo = window->GetClientInfo();
    239  if (clientInfo.isSome()) {
    240    ipcClientInfo.emplace(clientInfo.value().ToIPC());
    241  }
    242 
    243  nsID agentClusterId = nsID::GenerateUUID();
    244 
    245  net::CookieJarSettingsArgs cjsData;
    246  MOZ_ASSERT(loadInfo.mCookieJarSettings);
    247  net::CookieJarSettings::Cast(loadInfo.mCookieJarSettings)->Serialize(cjsData);
    248 
    249  auto remoteType = RemoteWorkerManager::GetRemoteType(
    250      loadInfo.mPrincipal, WorkerKind::WorkerKindShared);
    251  if (NS_WARN_IF(remoteType.isErr())) {
    252    aRv.Throw(remoteType.unwrapErr());
    253    return nullptr;
    254  }
    255 
    256  Maybe<RFPTargetSet> overriddenFingerprintingSettingsArg;
    257  if (loadInfo.mOverriddenFingerprintingSettings.isSome()) {
    258    overriddenFingerprintingSettingsArg.emplace(
    259        loadInfo.mOverriddenFingerprintingSettings.ref());
    260  }
    261 
    262  RemoteWorkerData remoteWorkerData(
    263      nsString(*compliantString), baseURL, resolvedScriptURL, aOptions,
    264      loadingPrincipalInfo, principalInfo, partitionedPrincipalInfo,
    265      loadInfo.mUseRegularPrincipal, loadInfo.mUsingStorageAccess, cjsData,
    266      loadInfo.mDomain, isSecureContext, ipcClientInfo, loadInfo.mReferrerInfo,
    267      storageAllowed, AntiTrackingUtils::IsThirdPartyWindow(window, nullptr),
    268      loadInfo.mShouldResistFingerprinting, overriddenFingerprintingSettingsArg,
    269      loadInfo.mIsOn3PCBExceptionList,
    270      OriginTrials::FromWindow(nsGlobalWindowInner::Cast(window)),
    271      void_t() /* OptionalServiceWorkerData */, agentClusterId,
    272      remoteType.unwrap());
    273 
    274  PSharedWorkerChild* pActor = actorChild->SendPSharedWorkerConstructor(
    275      remoteWorkerData, loadInfo.mWindowID, portIdentifier.release());
    276  if (!pActor) {
    277    MOZ_ASSERT_UNREACHABLE("We already checked PBackground above.");
    278    aRv.ThrowSecurityError("PBackground not available.");
    279    return nullptr;
    280  }
    281 
    282  RefPtr<SharedWorkerChild> actor = static_cast<SharedWorkerChild*>(pActor);
    283 
    284  RefPtr<SharedWorker> sharedWorker =
    285      new SharedWorker(window, actor, channel->Port2());
    286 
    287  // Let's inform the window about this SharedWorker.
    288  nsGlobalWindowInner::Cast(window)->StoreSharedWorker(sharedWorker);
    289  actor->SetParent(sharedWorker);
    290 
    291  if (nsGlobalWindowInner::Cast(window)->IsSuspended()) {
    292    sharedWorker->Suspend();
    293  }
    294 
    295  return sharedWorker.forget();
    296 }
    297 
    298 MessagePort* SharedWorker::Port() {
    299  AssertIsOnMainThread();
    300  return mMessagePort;
    301 }
    302 
    303 void SharedWorker::Freeze() {
    304  AssertIsOnMainThread();
    305  MOZ_ASSERT(!IsFrozen());
    306 
    307  if (mFrozen) {
    308    return;
    309  }
    310 
    311  mFrozen = true;
    312 
    313  if (mActor) {
    314    mActor->SendFreeze();
    315  }
    316 }
    317 
    318 void SharedWorker::Thaw() {
    319  AssertIsOnMainThread();
    320  MOZ_ASSERT(IsFrozen());
    321 
    322  if (!mFrozen) {
    323    return;
    324  }
    325 
    326  mFrozen = false;
    327 
    328  if (mActor) {
    329    mActor->SendThaw();
    330  }
    331 
    332  if (!mFrozenEvents.IsEmpty()) {
    333    nsTArray<RefPtr<Event>> events = std::move(mFrozenEvents);
    334 
    335    for (uint32_t index = 0; index < events.Length(); index++) {
    336      RefPtr<Event>& event = events[index];
    337      MOZ_ASSERT(event);
    338 
    339      RefPtr<EventTarget> target = event->GetTarget();
    340      ErrorResult rv;
    341      target->DispatchEvent(*event, rv);
    342      if (rv.Failed()) {
    343        NS_WARNING("Failed to dispatch event!");
    344      }
    345    }
    346  }
    347 }
    348 
    349 void SharedWorker::QueueEvent(Event* aEvent) {
    350  AssertIsOnMainThread();
    351  MOZ_ASSERT(aEvent);
    352  MOZ_ASSERT(IsFrozen());
    353 
    354  mFrozenEvents.AppendElement(aEvent);
    355 }
    356 
    357 void SharedWorker::Close() {
    358  AssertIsOnMainThread();
    359 
    360  if (mWindow) {
    361    nsGlobalWindowInner::Cast(mWindow)->ForgetSharedWorker(this);
    362    mWindow = nullptr;
    363  }
    364 
    365  if (mActor) {
    366    mActor->SendClose();
    367    mActor->SetParent(nullptr);
    368    mActor = nullptr;
    369  }
    370 
    371  if (mMessagePort) {
    372    mMessagePort->Close();
    373  }
    374 }
    375 
    376 void SharedWorker::Suspend() {
    377  if (mActor) {
    378    mActor->SendSuspend();
    379  }
    380 }
    381 
    382 void SharedWorker::Resume() {
    383  if (mActor) {
    384    mActor->SendResume();
    385  }
    386 }
    387 
    388 void SharedWorker::PostMessage(JSContext* aCx, JS::Handle<JS::Value> aMessage,
    389                               const Sequence<JSObject*>& aTransferable,
    390                               ErrorResult& aRv) {
    391  AssertIsOnMainThread();
    392  MOZ_ASSERT(mMessagePort);
    393 
    394  mMessagePort->PostMessage(aCx, aMessage, aTransferable, aRv);
    395 }
    396 
    397 NS_IMPL_ADDREF_INHERITED(SharedWorker, DOMEventTargetHelper)
    398 NS_IMPL_RELEASE_INHERITED(SharedWorker, DOMEventTargetHelper)
    399 
    400 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(SharedWorker)
    401 NS_INTERFACE_MAP_END_INHERITING(DOMEventTargetHelper)
    402 
    403 NS_IMPL_CYCLE_COLLECTION_CLASS(SharedWorker)
    404 
    405 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(SharedWorker,
    406                                                  DOMEventTargetHelper)
    407  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindow)
    408  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mMessagePort)
    409  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFrozenEvents)
    410 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    411 
    412 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(SharedWorker,
    413                                                DOMEventTargetHelper)
    414  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindow)
    415  NS_IMPL_CYCLE_COLLECTION_UNLINK(mMessagePort)
    416  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFrozenEvents)
    417 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    418 
    419 JSObject* SharedWorker::WrapObject(JSContext* aCx,
    420                                   JS::Handle<JSObject*> aGivenProto) {
    421  AssertIsOnMainThread();
    422 
    423  return SharedWorker_Binding::Wrap(aCx, this, aGivenProto);
    424 }
    425 
    426 void SharedWorker::GetEventTargetParent(EventChainPreVisitor& aVisitor) {
    427  AssertIsOnMainThread();
    428 
    429  if (IsFrozen()) {
    430    RefPtr<Event> event = aVisitor.mDOMEvent;
    431    if (!event) {
    432      event = EventDispatcher::CreateEvent(aVisitor.mEvent->mOriginalTarget,
    433                                           aVisitor.mPresContext,
    434                                           aVisitor.mEvent, u""_ns);
    435    }
    436 
    437    QueueEvent(event);
    438 
    439    aVisitor.mCanHandle = false;
    440    aVisitor.SetParentTarget(nullptr, false);
    441    return;
    442  }
    443 
    444  DOMEventTargetHelper::GetEventTargetParent(aVisitor);
    445 }
    446 
    447 void SharedWorker::DisconnectFromOwner() {
    448  Close();
    449  DOMEventTargetHelper::DisconnectFromOwner();
    450 }
    451 
    452 void SharedWorker::ErrorPropagation(nsresult aError) {
    453  AssertIsOnMainThread();
    454  MOZ_ASSERT(mActor);
    455  MOZ_ASSERT(NS_FAILED(aError));
    456 
    457  RefPtr<AsyncEventDispatcher> errorEvent =
    458      new AsyncEventDispatcher(this, u"error"_ns, CanBubble::eNo);
    459  errorEvent->PostDOMEvent();
    460 
    461  Close();
    462 }