tor-browser

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

WindowGlobalChild.cpp (34798B)


      1 /* -*- Mode: C++; c-basic-offset: 2; indent-tabs-mode: nil; tab-width: 8 -*- */
      2 /* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
      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 "mozilla/dom/WindowGlobalChild.h"
      8 
      9 #include "GeckoProfiler.h"
     10 #include "mozilla/AntiTrackingUtils.h"
     11 #include "mozilla/ClearOnShutdown.h"
     12 #include "mozilla/PresShell.h"
     13 #include "mozilla/ScopeExit.h"
     14 #include "mozilla/dom/BrowserBridgeChild.h"
     15 #include "mozilla/dom/BrowserChild.h"
     16 #include "mozilla/dom/BrowsingContext.h"
     17 #include "mozilla/dom/BrowsingContextGroup.h"
     18 #include "mozilla/dom/CloseWatcherManager.h"
     19 #include "mozilla/dom/ContentChild.h"
     20 #include "mozilla/dom/ContentParent.h"
     21 #include "mozilla/dom/IdentityCredential.h"
     22 #include "mozilla/dom/InProcessChild.h"
     23 #include "mozilla/dom/InProcessParent.h"
     24 #include "mozilla/dom/JSActorService.h"
     25 #include "mozilla/dom/JSWindowActorBinding.h"
     26 #include "mozilla/dom/JSWindowActorChild.h"
     27 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
     28 #include "mozilla/dom/SecurityPolicyViolationEvent.h"
     29 #include "mozilla/dom/SessionStoreRestoreData.h"
     30 #include "mozilla/dom/WindowContext.h"
     31 #include "mozilla/dom/WindowGlobalActorsBinding.h"
     32 #include "mozilla/dom/WindowGlobalParent.h"
     33 #include "mozilla/dom/nsMixedContentBlocker.h"
     34 #include "mozilla/ipc/Endpoint.h"
     35 #include "nsContentUtils.h"
     36 #include "nsDocShell.h"
     37 #include "nsFocusManager.h"
     38 #include "nsFrameLoader.h"
     39 #include "nsFrameLoaderOwner.h"
     40 #include "nsGlobalWindowInner.h"
     41 #include "nsIHttpChannelInternal.h"
     42 #include "nsIURIMutator.h"
     43 #include "nsNetUtil.h"
     44 #include "nsQueryObject.h"
     45 #include "nsScriptSecurityManager.h"
     46 #include "nsSerializationHelper.h"
     47 #include "nsURLHelper.h"
     48 
     49 using namespace mozilla::ipc;
     50 using namespace mozilla::dom::ipc;
     51 
     52 namespace mozilla::dom {
     53 
     54 WindowGlobalChild::WindowGlobalChild(dom::WindowContext* aWindowContext,
     55                                     nsIPrincipal* aPrincipal,
     56                                     nsIURI* aDocumentURI)
     57    : mWindowContext(aWindowContext),
     58      mDocumentPrincipal(aPrincipal),
     59      mDocumentURI(aDocumentURI) {
     60  MOZ_DIAGNOSTIC_ASSERT(mWindowContext);
     61  MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal);
     62  MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
     63                        mWindowContext->IsLocalIP());
     64 
     65  if (!mDocumentURI) {
     66    NS_NewURI(getter_AddRefs(mDocumentURI), "about:blank");
     67  }
     68 
     69  // Registers a DOM Window with the profiler. It re-registers the same Inner
     70  // Window ID with different URIs because when a Browsing context is first
     71  // loaded, the first url loaded in it will be about:blank. This call keeps the
     72  // first non-about:blank registration of window and discards the previous one.
     73  uint64_t embedderInnerWindowID = 0;
     74  if (BrowsingContext()->GetParent()) {
     75    embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId();
     76  }
     77  profiler_register_page(
     78      BrowsingContext()->BrowserId(), InnerWindowId(),
     79      nsContentUtils::TruncatedURLForDisplay(aDocumentURI, 1024),
     80      embedderInnerWindowID, BrowsingContext()->UsePrivateBrowsing());
     81 }
     82 
     83 void VerifyStoragePrincipalMatchesDocumentPrincipal(WindowGlobalInit aInit) {
     84  // WindowGlobalParent::CreateDisconnected performs similar checks in
     85  // SetDocumentStoragePrincipal. If they fail, the parent process crashes.
     86  // Let's ensure we crash in content instead, and assert each condition
     87  // separately to find out what fails. See bug 2003449.
     88  nsCString noSuffix, storageNoSuffix;
     89  aInit.principal()->GetOriginNoSuffix(noSuffix);
     90  aInit.storagePrincipal()->GetOriginNoSuffix(storageNoSuffix);
     91  MOZ_RELEASE_ASSERT(noSuffix == storageNoSuffix);
     92  MOZ_RELEASE_ASSERT(
     93      aInit.principal()->OriginAttributesRef().EqualsIgnoringPartitionKey(
     94          aInit.storagePrincipal()->OriginAttributesRef()));
     95 }
     96 
     97 already_AddRefed<WindowGlobalChild> WindowGlobalChild::Create(
     98    nsGlobalWindowInner* aWindow) {
     99 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    100  // Opener policy is set when we start to load a document. Here, we ensure we
    101  // have set the correct Opener policy so that it will be available in the
    102  // parent process through window global child.
    103  nsCOMPtr<nsIChannel> chan = aWindow->GetDocument()->GetChannel();
    104  nsCOMPtr<nsILoadInfo> loadInfo = chan ? chan->LoadInfo() : nullptr;
    105  nsCOMPtr<nsIHttpChannelInternal> httpChan = do_QueryInterface(chan);
    106  nsILoadInfo::CrossOriginOpenerPolicy policy;
    107  if (httpChan &&
    108      loadInfo->GetExternalContentPolicyType() ==
    109          ExtContentPolicy::TYPE_DOCUMENT &&
    110      NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy))) {
    111    MOZ_DIAGNOSTIC_ASSERT(policy ==
    112                          aWindow->GetBrowsingContext()->GetOpenerPolicy());
    113  }
    114 #endif
    115 
    116  WindowGlobalInit init = WindowGlobalActor::WindowInitializer(aWindow);
    117  RefPtr<WindowGlobalChild> wgc = CreateDisconnected(init);
    118 
    119  // Send the link constructor over PBrowser, or link over PInProcess.
    120  if (XRE_IsParentProcess()) {
    121    InProcessChild* ipChild = InProcessChild::Singleton();
    122    InProcessParent* ipParent = InProcessParent::Singleton();
    123    if (!ipChild || !ipParent) {
    124      return nullptr;
    125    }
    126 
    127    ManagedEndpoint<PWindowGlobalParent> endpoint =
    128        ipChild->OpenPWindowGlobalEndpoint(wgc);
    129    ipParent->BindPWindowGlobalEndpoint(std::move(endpoint),
    130                                        wgc->WindowContext()->Canonical());
    131  } else {
    132    RefPtr<BrowserChild> browserChild =
    133        BrowserChild::GetFrom(static_cast<mozIDOMWindow*>(aWindow));
    134    MOZ_ASSERT(browserChild);
    135 
    136 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
    137    dom::BrowsingContext* bc = aWindow->GetBrowsingContext();
    138 #endif
    139 
    140    MOZ_DIAGNOSTIC_ASSERT(bc->AncestorsAreCurrent());
    141    MOZ_DIAGNOSTIC_ASSERT(bc->IsInProcess());
    142 
    143    VerifyStoragePrincipalMatchesDocumentPrincipal(init);
    144 
    145    ManagedEndpoint<PWindowGlobalParent> endpoint =
    146        browserChild->OpenPWindowGlobalEndpoint(wgc);
    147    browserChild->SendNewWindowGlobal(std::move(endpoint), init);
    148  }
    149 
    150  wgc->Init();
    151  wgc->InitWindowGlobal(aWindow);
    152  return wgc.forget();
    153 }
    154 
    155 already_AddRefed<WindowGlobalChild> WindowGlobalChild::CreateDisconnected(
    156    const WindowGlobalInit& aInit) {
    157  RefPtr<dom::BrowsingContext> browsingContext =
    158      dom::BrowsingContext::Get(aInit.context().mBrowsingContextId);
    159 
    160  RefPtr<dom::WindowContext> windowContext =
    161      dom::WindowContext::GetById(aInit.context().mInnerWindowId);
    162  MOZ_RELEASE_ASSERT(!windowContext, "Creating duplicate WindowContext");
    163 
    164  // Create our new WindowContext
    165  if (XRE_IsParentProcess()) {
    166    windowContext = WindowGlobalParent::CreateDisconnected(aInit);
    167  } else {
    168    dom::WindowContext::FieldValues fields = aInit.context().mFields;
    169    windowContext = new dom::WindowContext(
    170        browsingContext, aInit.context().mInnerWindowId,
    171        aInit.context().mOuterWindowId, std::move(fields));
    172  }
    173 
    174  RefPtr<WindowGlobalChild> windowChild = new WindowGlobalChild(
    175      windowContext, aInit.principal(), aInit.documentURI());
    176  windowContext->mIsInProcess = true;
    177  windowContext->mWindowGlobalChild = windowChild;
    178  return windowChild.forget();
    179 }
    180 
    181 void WindowGlobalChild::Init() {
    182  MOZ_ASSERT(mWindowContext->mWindowGlobalChild == this);
    183  mWindowContext->Init();
    184 }
    185 
    186 void WindowGlobalChild::InitWindowGlobal(nsGlobalWindowInner* aWindow) {
    187  mWindowGlobal = aWindow;
    188 }
    189 
    190 void WindowGlobalChild::OnNewDocument(Document* aDocument) {
    191  MOZ_RELEASE_ASSERT(mWindowGlobal);
    192  MOZ_RELEASE_ASSERT(aDocument);
    193 
    194  // Send a series of messages to update document-specific state on
    195  // WindowGlobalParent, when we change documents on an existing WindowGlobal.
    196  // This data is also all sent when we construct a WindowGlobal, so anything
    197  // added here should also be added to WindowGlobalActor::WindowInitializer.
    198 
    199  // FIXME: Perhaps these should be combined into a smaller number of messages?
    200  SendSetIsInitialDocument(aDocument->IsInitialDocument());
    201  SetDocumentURI(aDocument->GetDocumentURI());
    202  SetDocumentPrincipal(aDocument->NodePrincipal(),
    203                       aDocument->EffectiveStoragePrincipal());
    204 
    205  nsCOMPtr<nsITransportSecurityInfo> securityInfo;
    206  if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) {
    207    channel->GetSecurityInfo(getter_AddRefs(securityInfo));
    208  }
    209  SendUpdateDocumentSecurityInfo(securityInfo);
    210 
    211  SendUpdateDocumentCspSettings(aDocument->GetBlockAllMixedContent(false),
    212                                aDocument->GetUpgradeInsecureRequests(false));
    213  SendUpdateSandboxFlags(aDocument->GetSandboxFlags());
    214 
    215  net::CookieJarSettingsArgs csArgs;
    216  net::CookieJarSettings::Cast(aDocument->CookieJarSettings())
    217      ->Serialize(csArgs);
    218  if (!SendUpdateCookieJarSettings(csArgs)) {
    219    NS_WARNING(
    220        "Failed to update document's cookie jar settings on the "
    221        "WindowGlobalParent");
    222  }
    223 
    224  SendUpdateHttpsOnlyStatus(aDocument->HttpsOnlyStatus());
    225 
    226  // Update window context fields for the newly loaded Document.
    227  WindowContext::Transaction txn;
    228  txn.SetCookieBehavior(
    229      Some(aDocument->CookieJarSettings()->GetCookieBehavior()));
    230  txn.SetIsOnContentBlockingAllowList(
    231      aDocument->CookieJarSettings()->GetIsOnContentBlockingAllowList());
    232  txn.SetIsThirdPartyWindow(aDocument->HasThirdPartyChannel());
    233  txn.SetIsThirdPartyTrackingResourceWindow(
    234      nsContentUtils::IsThirdPartyTrackingResourceWindow(mWindowGlobal));
    235  txn.SetIsSecureContext(mWindowGlobal->IsSecureContext());
    236  if (auto policy = aDocument->GetEmbedderPolicy()) {
    237    txn.SetEmbedderPolicy(*policy);
    238  }
    239  txn.SetShouldResistFingerprinting(aDocument->ShouldResistFingerprinting(
    240      RFPTarget::IsAlwaysEnabledForPrecompute));
    241  txn.SetOverriddenFingerprintingSettings(
    242      aDocument->GetOverriddenFingerprintingSettings());
    243 
    244  if (nsCOMPtr<nsIChannel> channel = aDocument->GetChannel()) {
    245    nsCOMPtr<nsILoadInfo> loadInfo(channel->LoadInfo());
    246    txn.SetIsOriginalFrameSource(loadInfo->GetOriginalFrameSrcLoad());
    247 
    248    nsILoadInfo::StoragePermissionState storageAccess =
    249        loadInfo->GetStoragePermission();
    250    txn.SetUsingStorageAccess(
    251        storageAccess == nsILoadInfo::HasStoragePermission ||
    252        storageAccess == nsILoadInfo::StoragePermissionAllowListed);
    253  } else {
    254    txn.SetIsOriginalFrameSource(false);
    255  }
    256 
    257  // Init Mixed Content Fields
    258  nsCOMPtr<nsIURI> innerDocURI =
    259      NS_GetInnermostURI(aDocument->GetDocumentURI());
    260  if (innerDocURI) {
    261    txn.SetIsSecure(
    262        innerDocURI->SchemeIs("https") ||
    263        nsMixedContentBlocker::IsPotentiallyTrustworthyOnion(innerDocURI));
    264  }
    265 
    266  MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
    267                        mWindowContext->IsLocalIP());
    268 
    269  MOZ_ALWAYS_SUCCEEDS(txn.Commit(mWindowContext));
    270 }
    271 
    272 /* static */
    273 already_AddRefed<WindowGlobalChild> WindowGlobalChild::GetByInnerWindowId(
    274    uint64_t aInnerWindowId) {
    275  if (RefPtr<dom::WindowContext> context =
    276          dom::WindowContext::GetById(aInnerWindowId)) {
    277    return do_AddRef(context->GetWindowGlobalChild());
    278  }
    279  return nullptr;
    280 }
    281 
    282 dom::BrowsingContext* WindowGlobalChild::BrowsingContext() {
    283  return mWindowContext->GetBrowsingContext();
    284 }
    285 
    286 Nullable<WindowProxyHolder> WindowGlobalChild::GetContentWindow() {
    287  if (IsCurrentGlobal()) {
    288    return WindowProxyHolder(BrowsingContext());
    289  }
    290  return nullptr;
    291 }
    292 
    293 uint64_t WindowGlobalChild::InnerWindowId() {
    294  return mWindowContext->InnerWindowId();
    295 }
    296 
    297 uint64_t WindowGlobalChild::OuterWindowId() {
    298  return mWindowContext->OuterWindowId();
    299 }
    300 
    301 bool WindowGlobalChild::IsCurrentGlobal() {
    302  return CanSend() && mWindowGlobal->IsCurrentInnerWindow();
    303 }
    304 
    305 already_AddRefed<WindowGlobalParent> WindowGlobalChild::GetParentActor() {
    306  if (!CanSend()) {
    307    return nullptr;
    308  }
    309  IProtocol* otherSide = InProcessChild::ParentActorFor(this);
    310  return do_AddRef(static_cast<WindowGlobalParent*>(otherSide));
    311 }
    312 
    313 already_AddRefed<BrowserChild> WindowGlobalChild::GetBrowserChild() {
    314  if (IsInProcess() || !CanSend()) {
    315    return nullptr;
    316  }
    317  return do_AddRef(static_cast<BrowserChild*>(Manager()));
    318 }
    319 
    320 uint64_t WindowGlobalChild::ContentParentId() {
    321  if (XRE_IsParentProcess()) {
    322    return 0;
    323  }
    324  return ContentChild::GetSingleton()->GetID();
    325 }
    326 
    327 // A WindowGlobalChild is the root in its process if it has no parent, or its
    328 // embedder is in a different process.
    329 bool WindowGlobalChild::IsProcessRoot() {
    330  if (!BrowsingContext()->GetParent()) {
    331    return true;
    332  }
    333 
    334  return !BrowsingContext()->GetEmbedderElement();
    335 }
    336 
    337 // When a "beforeunload" handler is added, it's recorded to be able to know when
    338 // dispatching "beforeunload" is needed.
    339 void WindowGlobalChild::BeforeUnloadAdded() {
    340  // Don't bother notifying the parent if we don't have an IPC link open.
    341  if (mBeforeUnloadListeners == 0 && CanSend()) {
    342    (void)mWindowContext->SetNeedsBeforeUnload(true);
    343  }
    344 
    345  mBeforeUnloadListeners++;
    346  MOZ_ASSERT(mBeforeUnloadListeners > 0);
    347 }
    348 
    349 // This is the inverse of `BeforeUnloadAdded`, making sure that "beforeunload"
    350 // isn't dispatched if all "beforeunload" handlers have been removed.
    351 void WindowGlobalChild::BeforeUnloadRemoved() {
    352  mBeforeUnloadListeners--;
    353  MOZ_ASSERT(mBeforeUnloadListeners >= 0);
    354 
    355  if (mBeforeUnloadListeners == 0) {
    356    (void)mWindowContext->SetNeedsBeforeUnload(false);
    357  }
    358 }
    359 
    360 // This is very similar to what is done for "beforeunload" and uses the same
    361 // state to keep track, but is only ever used for a top level window. It's used
    362 // to be able to track when a "navigate" event needs to be dispatched to the top
    363 // level window's navigation object, which needs to happen right after a
    364 // "beforeunload" event for that window would be dispatched, regardless of if it
    365 // is.
    366 void WindowGlobalChild::NavigateAdded() {
    367  if (!BrowsingContext()->IsTop()) {
    368    return;
    369  }
    370  BeforeUnloadAdded();
    371 }
    372 
    373 // The inverse of `NavigateAdded`, again only ever used for a top level window.
    374 void WindowGlobalChild::NavigateRemoved() {
    375  if (!BrowsingContext()->IsTop()) {
    376    return;
    377  }
    378  BeforeUnloadRemoved();
    379 }
    380 
    381 void WindowGlobalChild::Destroy() {
    382  JSActorWillDestroy();
    383 
    384  mWindowContext->Discard();
    385 
    386  // Perform async IPC shutdown unless we're not in-process, and our
    387  // BrowserChild is in the process of being destroyed, which will destroy
    388  // us as well.
    389  RefPtr<BrowserChild> browserChild = GetBrowserChild();
    390  if (!browserChild || !browserChild->IsDestroyed()) {
    391    SendDestroy();
    392  }
    393 }
    394 
    395 mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameLocal(
    396    const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
    397    uint64_t aPendingSwitchId) {
    398  MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
    399 
    400  MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
    401          ("RecvMakeFrameLocal ID=%" PRIx64, aFrameContext.ContextId()));
    402 
    403  if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
    404    return IPC_OK();
    405  }
    406  dom::BrowsingContext* frameContext = aFrameContext.get();
    407 
    408  RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
    409  if (NS_WARN_IF(!embedderElt)) {
    410    return IPC_OK();
    411  }
    412 
    413  if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
    414    return IPC_OK();
    415  }
    416 
    417  RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
    418  MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
    419 
    420  // Trigger a process switch into the current process.
    421  RemotenessOptions options;
    422  options.mRemoteType = NOT_REMOTE_TYPE;
    423  options.mPendingSwitchID.Construct(aPendingSwitchId);
    424  options.mSwitchingInProgressLoad = true;
    425  flo->ChangeRemoteness(options, IgnoreErrors());
    426  return IPC_OK();
    427 }
    428 
    429 mozilla::ipc::IPCResult WindowGlobalChild::RecvMakeFrameRemote(
    430    const MaybeDiscarded<dom::BrowsingContext>& aFrameContext,
    431    ManagedEndpoint<PBrowserBridgeChild>&& aEndpoint, const TabId& aTabId,
    432    const LayersId& aLayersId, MakeFrameRemoteResolver&& aResolve) {
    433  MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
    434 
    435  MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug,
    436          ("RecvMakeFrameRemote ID=%" PRIx64, aFrameContext.ContextId()));
    437 
    438  if (!aLayersId.IsValid()) {
    439    return IPC_FAIL(this, "Received an invalid LayersId");
    440  }
    441 
    442  // Resolve the promise when this function exits, as we'll have fully unloaded
    443  // at that point.
    444  auto scopeExit = MakeScopeExit([&] { aResolve(true); });
    445 
    446  // Get a BrowsingContext if we're not null or discarded. We don't want to
    447  // early-return before we connect the BrowserBridgeChild, as otherwise we'll
    448  // never break the channel in the parent.
    449  RefPtr<dom::BrowsingContext> frameContext;
    450  if (!aFrameContext.IsDiscarded()) {
    451    frameContext = aFrameContext.get();
    452  }
    453 
    454  // Immediately construct the BrowserBridgeChild so we can destroy it cleanly
    455  // if the process switch fails.
    456  RefPtr<BrowserBridgeChild> bridge =
    457      new BrowserBridgeChild(frameContext, aTabId, aLayersId);
    458  RefPtr<BrowserChild> manager = GetBrowserChild();
    459  if (NS_WARN_IF(
    460          !manager->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge))) {
    461    return IPC_OK();
    462  }
    463 
    464  // Synchronously delete de actor here rather than using SendBeginDestroy(), as
    465  // we haven't initialized it yet.
    466  auto deleteBridge =
    467      MakeScopeExit([&] { BrowserBridgeChild::Send__delete__(bridge); });
    468 
    469  // Immediately tear down the actor if we don't have a valid FrameContext.
    470  if (NS_WARN_IF(aFrameContext.IsNullOrDiscarded())) {
    471    return IPC_OK();
    472  }
    473 
    474  RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
    475  if (NS_WARN_IF(!embedderElt)) {
    476    return IPC_OK();
    477  }
    478 
    479  if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
    480    return IPC_OK();
    481  }
    482 
    483  RefPtr<nsFrameLoaderOwner> flo = do_QueryObject(embedderElt);
    484  MOZ_DIAGNOSTIC_ASSERT(flo, "Embedder must be a nsFrameLoaderOwner");
    485 
    486  // Trgger a process switch into the specified process.
    487  IgnoredErrorResult rv;
    488  flo->ChangeRemotenessWithBridge(bridge, rv);
    489  if (NS_WARN_IF(rv.Failed())) {
    490    return IPC_OK();
    491  }
    492 
    493  // Everything succeeded, so don't delete the bridge.
    494  deleteBridge.release();
    495 
    496  return IPC_OK();
    497 }
    498 
    499 mozilla::ipc::IPCResult WindowGlobalChild::RecvDrawSnapshot(
    500    const Maybe<IntRect>& aRect, const float& aScale,
    501    const nscolor& aBackgroundColor, const uint32_t& aFlags,
    502    DrawSnapshotResolver&& aResolve) {
    503  aResolve(gfx::PaintFragment::Record(BrowsingContext(), aRect, aScale,
    504                                      aBackgroundColor,
    505                                      (gfx::CrossProcessPaintFlags)aFlags));
    506  return IPC_OK();
    507 }
    508 
    509 mozilla::ipc::IPCResult
    510 WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() {
    511  nsCOMPtr<nsPIDOMWindowInner> inner = GetWindowGlobal();
    512  if (inner) {
    513    inner->SaveStorageAccessPermissionGranted();
    514  }
    515 
    516  return IPC_OK();
    517 }
    518 
    519 mozilla::ipc::IPCResult WindowGlobalChild::RecvDispatchSecurityPolicyViolation(
    520    const nsString& aViolationEventJSON) {
    521  nsGlobalWindowInner* window = GetWindowGlobal();
    522  if (!window) {
    523    return IPC_OK();
    524  }
    525 
    526  Document* doc = window->GetDocument();
    527  if (!doc) {
    528    return IPC_OK();
    529  }
    530 
    531  SecurityPolicyViolationEventInit violationEvent;
    532  if (!violationEvent.Init(aViolationEventJSON)) {
    533    return IPC_OK();
    534  }
    535 
    536  RefPtr<Event> event = SecurityPolicyViolationEvent::Constructor(
    537      doc, u"securitypolicyviolation"_ns, violationEvent);
    538  event->SetTrusted(true);
    539  doc->DispatchEvent(*event, IgnoreErrors());
    540  return IPC_OK();
    541 }
    542 
    543 mozilla::ipc::IPCResult WindowGlobalChild::RecvAddBlockedFrameNodeByClassifier(
    544    const MaybeDiscardedBrowsingContext& aNode) {
    545  if (aNode.IsNullOrDiscarded()) {
    546    return IPC_OK();
    547  }
    548 
    549  nsGlobalWindowInner* window = GetWindowGlobal();
    550  if (!window) {
    551    return IPC_OK();
    552  }
    553 
    554  Document* doc = window->GetDocument();
    555  if (!doc) {
    556    return IPC_OK();
    557  }
    558 
    559  MOZ_ASSERT(aNode.get()->GetEmbedderElement()->OwnerDoc() == doc);
    560  doc->AddBlockedNodeByClassifier(aNode.get()->GetEmbedderElement());
    561  return IPC_OK();
    562 }
    563 
    564 mozilla::ipc::IPCResult WindowGlobalChild::RecvResetScalingZoom() {
    565  if (Document* doc = mWindowGlobal->GetExtantDoc()) {
    566    if (PresShell* ps = doc->GetPresShell()) {
    567      ps->SetResolutionAndScaleTo(1.0,
    568                                  ResolutionChangeOrigin::MainThreadAdjustment);
    569    }
    570  }
    571  return IPC_OK();
    572 }
    573 
    574 mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreDocShellState(
    575    const dom::sessionstore::DocShellRestoreState& aState,
    576    RestoreDocShellStateResolver&& aResolve) {
    577  if (mWindowGlobal) {
    578    SessionStoreUtils::RestoreDocShellState(mWindowGlobal->GetDocShell(),
    579                                            aState);
    580  }
    581  aResolve(true);
    582  return IPC_OK();
    583 }
    584 
    585 mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreTabContent(
    586    dom::SessionStoreRestoreData* aData, RestoreTabContentResolver&& aResolve) {
    587  aData->RestoreInto(BrowsingContext());
    588  aResolve(true);
    589  return IPC_OK();
    590 }
    591 
    592 IPCResult WindowGlobalChild::RecvRawMessage(
    593    const JSActorMessageMeta& aMeta, JSIPCValue&& aData,
    594    const UniquePtr<ClonedMessageData>& aStack) {
    595  UniquePtr<StructuredCloneData> stack;
    596  if (aStack) {
    597    stack = MakeUnique<StructuredCloneData>();
    598    stack->BorrowFromClonedMessageData(*aStack);
    599  }
    600  ReceiveRawMessage(aMeta, std::move(aData), std::move(stack));
    601  return IPC_OK();
    602 }
    603 
    604 IPCResult WindowGlobalChild::RecvNotifyPermissionChange(const nsCString& aType,
    605                                                        uint32_t aPermission) {
    606  nsCOMPtr<nsIObserverService> observerService = services::GetObserverService();
    607  NS_ENSURE_TRUE(observerService,
    608                 IPC_FAIL(this, "Failed to get observer service"));
    609  nsPIDOMWindowInner* notifyTarget =
    610      static_cast<nsPIDOMWindowInner*>(this->GetWindowGlobal());
    611  observerService->NotifyObservers(notifyTarget, "perm-changed-notify-only",
    612                                   NS_ConvertUTF8toUTF16(aType).get());
    613  // We only need to handle the revoked permission case here. The permission
    614  // grant case is handled via the Storage Access API code.
    615  if (this->GetWindowGlobal() &&
    616      this->GetWindowGlobal()->UsingStorageAccess() &&
    617      aPermission != nsIPermissionManager::ALLOW_ACTION) {
    618    this->GetWindowGlobal()->SaveStorageAccessPermissionRevoked();
    619  }
    620  return IPC_OK();
    621 }
    622 
    623 IPCResult WindowGlobalChild::RecvProcessCloseRequest(
    624    const MaybeDiscarded<dom::BrowsingContext>& aFocused) {
    625  RefPtr<nsFocusManager> focusManager = nsFocusManager::GetFocusManager();
    626  RefPtr<dom::BrowsingContext> focusedContext =
    627      focusManager ? focusManager->GetFocusedBrowsingContext() : nullptr;
    628  MOZ_ASSERT(focusedContext, "Cannot find focused context");
    629  // Only the currently focused context's CloseWatcher should be processed.
    630  if (RefPtr<Document> doc = focusedContext->GetExtantDocument()) {
    631    RefPtr<nsPIDOMWindowInner> win = doc->GetInnerWindow();
    632    if (win && win->IsFullyActive()) {
    633      RefPtr manager = win->EnsureCloseWatcherManager();
    634      manager->ProcessCloseRequest();
    635    }
    636  }
    637  return IPC_OK();
    638 }
    639 
    640 void WindowGlobalChild::SetDocumentURI(nsIURI* aDocumentURI) {
    641  // Registers a DOM Window with the profiler. It re-registers the same Inner
    642  // Window ID with different URIs because when a Browsing context is first
    643  // loaded, the first url loaded in it will be about:blank. This call keeps the
    644  // first non-about:blank registration of window and discards the previous one.
    645  uint64_t embedderInnerWindowID = 0;
    646  if (BrowsingContext()->GetParent()) {
    647    embedderInnerWindowID = BrowsingContext()->GetEmbedderInnerWindowId();
    648  }
    649  profiler_register_page(
    650      BrowsingContext()->BrowserId(), InnerWindowId(),
    651      nsContentUtils::TruncatedURLForDisplay(aDocumentURI, 1024),
    652      embedderInnerWindowID, BrowsingContext()->UsePrivateBrowsing());
    653 
    654  nsCOMPtr<nsIURI> principalURI = mDocumentPrincipal->GetURI();
    655  if (mDocumentPrincipal->GetIsNullPrincipal()) {
    656    if (nsCOMPtr<nsIPrincipal> precursor =
    657            mDocumentPrincipal->GetPrecursorPrincipal()) {
    658      principalURI = precursor->GetURI();
    659    }
    660  }
    661 
    662  MOZ_DIAGNOSTIC_ASSERT(!nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(
    663                            principalURI, aDocumentURI),
    664                        "Setting DocumentURI with a different origin "
    665                        "than principal URI");
    666 
    667  mDocumentURI = aDocumentURI;
    668  SendUpdateDocumentURI(WrapNotNull(aDocumentURI));
    669 }
    670 
    671 void WindowGlobalChild::SetDocumentPrincipal(
    672    nsIPrincipal* aNewDocumentPrincipal,
    673    nsIPrincipal* aNewDocumentStoragePrincipal) {
    674  MOZ_ASSERT(mDocumentPrincipal->Equals(aNewDocumentPrincipal));
    675  mDocumentPrincipal = aNewDocumentPrincipal;
    676  SendUpdateDocumentPrincipal(aNewDocumentPrincipal,
    677                              aNewDocumentStoragePrincipal);
    678 }
    679 
    680 const nsACString& WindowGlobalChild::GetRemoteType() const {
    681  if (XRE_IsContentProcess()) {
    682    return ContentChild::GetSingleton()->GetRemoteType();
    683  }
    684 
    685  return NOT_REMOTE_TYPE;
    686 }
    687 
    688 already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetActor(
    689    JSContext* aCx, const nsACString& aName, ErrorResult& aRv) {
    690  return JSActorManager::GetActor(aCx, aName, aRv)
    691      .downcast<JSWindowActorChild>();
    692 }
    693 
    694 already_AddRefed<JSWindowActorChild> WindowGlobalChild::GetExistingActor(
    695    const nsACString& aName) {
    696  return JSActorManager::GetExistingActor(aName).downcast<JSWindowActorChild>();
    697 }
    698 
    699 already_AddRefed<JSActor> WindowGlobalChild::InitJSActor(
    700    JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
    701    ErrorResult& aRv) {
    702  RefPtr<JSWindowActorChild> actor;
    703  if (aMaybeActor.get()) {
    704    aRv = UNWRAP_OBJECT(JSWindowActorChild, aMaybeActor.get(), actor);
    705    if (aRv.Failed()) {
    706      return nullptr;
    707    }
    708  } else {
    709    actor = new JSWindowActorChild();
    710  }
    711 
    712  MOZ_RELEASE_ASSERT(!actor->GetManager(),
    713                     "mManager was already initialized once!");
    714  actor->Init(aName, this);
    715  return actor.forget();
    716 }
    717 
    718 void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy) {
    719  MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
    720             "Destroying WindowGlobalChild can run script");
    721 
    722  // If our WindowContext hasn't been marked as discarded yet, ensure it's
    723  // marked as discarded at this point.
    724  mWindowContext->Discard();
    725 
    726  profiler_unregister_page(InnerWindowId());
    727 
    728  // Destroy our JSActors, and reject any pending queries.
    729  JSActorDidDestroy();
    730 }
    731 
    732 bool WindowGlobalChild::IsSameOriginWith(
    733    const dom::WindowContext* aOther) const {
    734  if (aOther == WindowContext()) {
    735    return true;
    736  }
    737 
    738  MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aOther->Group());
    739  if (nsGlobalWindowInner* otherWin = aOther->GetInnerWindow()) {
    740    return mDocumentPrincipal->Equals(otherWin->GetPrincipal());
    741  }
    742  return false;
    743 }
    744 
    745 bool WindowGlobalChild::SameOriginWithTop() {
    746  return IsSameOriginWith(WindowContext()->TopWindowContext());
    747 }
    748 
    749 // For historical context, see:
    750 //
    751 // Bug 13871:   Prevent frameset spoofing
    752 // Bug 103638:  Targets with same name in different windows open in wrong
    753 //              window with javascript
    754 // Bug 408052:  Adopt "ancestor" frame navigation policy
    755 // Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
    756 //              origin attribute isolation
    757 // Bug 1810619: Crash at null in nsDocShell::ValidateOrigin
    758 bool WindowGlobalChild::CanNavigate(dom::BrowsingContext* aTarget,
    759                                    bool aConsiderOpener) {
    760  MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aTarget->Group(),
    761                        "A WindowGlobalChild should never try to navigate a "
    762                        "BrowsingContext from another group");
    763 
    764  auto isFileScheme = [](nsIPrincipal* aPrincipal) -> bool {
    765    // NOTE: This code previously checked for a file scheme using
    766    // `nsIPrincipal::GetURI()` combined with `NS_GetInnermostURI`. We no longer
    767    // use GetURI, as it has been deprecated, and it makes more sense to take
    768    // advantage of the pre-computed origin, which will already use the
    769    // innermost URI (bug 1810619)
    770    nsAutoCString origin, scheme;
    771    return NS_SUCCEEDED(aPrincipal->GetOriginNoSuffix(origin)) &&
    772           NS_SUCCEEDED(net_ExtractURLScheme(origin, scheme)) &&
    773           scheme == "file"_ns;
    774  };
    775 
    776  // A frame can navigate itself and its own root.
    777  if (aTarget == BrowsingContext() || aTarget == BrowsingContext()->Top()) {
    778    return true;
    779  }
    780 
    781  // If the target frame doesn't yet have a WindowContext, start checking
    782  // principals from its direct ancestor instead. It would inherit its principal
    783  // from this document upon creation.
    784  dom::WindowContext* initialWc = aTarget->GetCurrentWindowContext();
    785  if (!initialWc) {
    786    initialWc = aTarget->GetParentWindowContext();
    787  }
    788 
    789  // A frame can navigate any frame with a same-origin ancestor.
    790  bool isFileDocument = isFileScheme(DocumentPrincipal());
    791  for (dom::WindowContext* wc = initialWc; wc;
    792       wc = wc->GetParentWindowContext()) {
    793    dom::WindowGlobalChild* wgc = wc->GetWindowGlobalChild();
    794    if (!wgc) {
    795      continue;  // out-of process, so not same-origin.
    796    }
    797 
    798    if (DocumentPrincipal()->Equals(wgc->DocumentPrincipal())) {
    799      return true;
    800    }
    801 
    802    // Not strictly equal, special case if both are file: URIs.
    803    //
    804    // file: URIs are considered the same domain for the purpose of frame
    805    // navigation, regardless of script accessibility (bug 420425).
    806    if (isFileDocument && isFileScheme(wgc->DocumentPrincipal())) {
    807      return true;
    808    }
    809  }
    810 
    811  // If the target is a top-level document, a frame can navigate it if it can
    812  // navigate its opener.
    813  if (aConsiderOpener && !aTarget->GetParent()) {
    814    if (RefPtr<dom::BrowsingContext> opener = aTarget->GetOpener()) {
    815      return CanNavigate(opener, false);
    816    }
    817  }
    818 
    819  return false;
    820 }
    821 
    822 // FindWithName follows the rules for choosing a browsing context,
    823 // with the exception of sandboxing for iframes. The implementation
    824 // for arbitrarily choosing between two browsing contexts with the
    825 // same name is as follows:
    826 //
    827 // 1) The start browsing context, i.e. 'this'
    828 // 2) Descendants in insertion order
    829 // 3) The parent
    830 // 4) Siblings and their children, both in insertion order
    831 // 5) After this we iteratively follow the parent chain, repeating 3
    832 //    and 4 until
    833 // 6) If there is no parent, consider all other top level browsing
    834 //    contexts and their children, both in insertion order
    835 //
    836 // See
    837 // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
    838 dom::BrowsingContext* WindowGlobalChild::FindBrowsingContextWithName(
    839    const nsAString& aName, bool aUseEntryGlobalForAccessCheck) {
    840  RefPtr<WindowGlobalChild> requestingContext = this;
    841  if (aUseEntryGlobalForAccessCheck) {
    842    if (nsGlobalWindowInner* caller = nsContentUtils::EntryInnerWindow()) {
    843      if (caller->GetBrowsingContextGroup() == WindowContext()->Group()) {
    844        requestingContext = caller->GetWindowGlobalChild();
    845      } else {
    846        MOZ_RELEASE_ASSERT(caller->GetPrincipal()->IsSystemPrincipal(),
    847                           "caller must be either same-group or system");
    848      }
    849    }
    850  }
    851  MOZ_ASSERT(requestingContext, "must have a requestingContext");
    852 
    853  dom::BrowsingContext* found = nullptr;
    854  if (aName.IsEmpty()) {
    855    // You can't find a browsing context with an empty name.
    856    found = nullptr;
    857  } else if (aName.LowerCaseEqualsLiteral("_blank")) {
    858    // Just return null. Caller must handle creating a new window with
    859    // a blank name.
    860    found = nullptr;
    861  } else if (nsContentUtils::IsSpecialName(aName)) {
    862    found = BrowsingContext()->FindWithSpecialName(aName, *requestingContext);
    863  } else if (dom::BrowsingContext* child =
    864                 BrowsingContext()->FindWithNameInSubtree(aName,
    865                                                          requestingContext)) {
    866    found = child;
    867  } else {
    868    dom::WindowContext* current = WindowContext();
    869 
    870    do {
    871      Span<RefPtr<dom::BrowsingContext>> siblings;
    872      dom::WindowContext* parent = current->GetParentWindowContext();
    873 
    874      if (!parent) {
    875        // We've reached the root of the tree, consider browsing
    876        // contexts in the same browsing context group.
    877        siblings = WindowContext()->Group()->Toplevels();
    878      } else if (dom::BrowsingContext* bc = parent->GetBrowsingContext();
    879                 bc && bc->NameEquals(aName) &&
    880                 requestingContext->CanNavigate(bc) && bc->IsTargetable()) {
    881        found = bc;
    882        break;
    883      } else {
    884        siblings = parent->NonSyntheticChildren();
    885      }
    886 
    887      for (dom::BrowsingContext* sibling : siblings) {
    888        if (sibling == current->GetBrowsingContext()) {
    889          continue;
    890        }
    891 
    892        if (dom::BrowsingContext* relative =
    893                sibling->FindWithNameInSubtree(aName, requestingContext)) {
    894          found = relative;
    895          // Breaks the outer loop
    896          parent = nullptr;
    897          break;
    898        }
    899      }
    900 
    901      current = parent;
    902    } while (current);
    903  }
    904 
    905  // Helpers should perform access control checks, which means that we
    906  // only need to assert that we can access found.
    907  MOZ_DIAGNOSTIC_ASSERT(!found || requestingContext->CanNavigate(found));
    908 
    909  return found;
    910 }
    911 
    912 void WindowGlobalChild::UnblockBFCacheFor(BFCacheStatus aStatus) {
    913  SendUpdateBFCacheStatus(0, aStatus);
    914 }
    915 
    916 void WindowGlobalChild::BlockBFCacheFor(BFCacheStatus aStatus) {
    917  SendUpdateBFCacheStatus(aStatus, 0);
    918 }
    919 
    920 WindowGlobalChild::~WindowGlobalChild() = default;
    921 
    922 JSObject* WindowGlobalChild::WrapObject(JSContext* aCx,
    923                                        JS::Handle<JSObject*> aGivenProto) {
    924  return WindowGlobalChild_Binding::Wrap(aCx, this, aGivenProto);
    925 }
    926 
    927 nsISupports* WindowGlobalChild::GetParentObject() {
    928  return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
    929 }
    930 
    931 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_CLASS(WindowGlobalChild)
    932 
    933 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(WindowGlobalChild)
    934  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowGlobal)
    935  NS_IMPL_CYCLE_COLLECTION_UNLINK(mContainerFeaturePolicy)
    936  NS_IMPL_CYCLE_COLLECTION_UNLINK(mWindowContext)
    937  tmp->UnlinkManager();
    938  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
    939  NS_IMPL_CYCLE_COLLECTION_UNLINK_WEAK_PTR
    940 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    941 
    942 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(WindowGlobalChild)
    943  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowGlobal)
    944  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mContainerFeaturePolicy)
    945  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mWindowContext)
    946  if (!tmp->IsInProcess()) {
    947    CycleCollectionNoteChild(cb, static_cast<BrowserChild*>(tmp->Manager()),
    948                             "Manager()");
    949  }
    950 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    951 
    952 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild)
    953  NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
    954  NS_INTERFACE_MAP_ENTRY(nsISupports)
    955 NS_INTERFACE_MAP_END
    956 
    957 NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild)
    958 NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild)
    959 
    960 }  // namespace mozilla::dom