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/. */
7 #include "mozilla/dom/WindowGlobalChild.h"
9 #include "mozilla/AntiTrackingUtils.h"
10 #include "mozilla/ClearOnShutdown.h"
11 #include "mozilla/dom/WindowGlobalParent.h"
12 #include "mozilla/dom/BrowsingContext.h"
13 #include "mozilla/dom/BrowsingContextGroup.h"
14 #include "mozilla/dom/ContentChild.h"
15 #include "mozilla/dom/MozFrameLoaderOwnerBinding.h"
16 #include "mozilla/dom/BrowserChild.h"
17 #include "mozilla/dom/BrowserBridgeChild.h"
18 #include "mozilla/dom/ContentParent.h"
19 #include "mozilla/dom/SecurityPolicyViolationEvent.h"
20 #include "mozilla/dom/SessionStoreRestoreData.h"
21 #include "mozilla/dom/WindowGlobalActorsBinding.h"
22 #include "mozilla/dom/WindowContext.h"
23 #include "mozilla/dom/InProcessChild.h"
24 #include "mozilla/dom/InProcessParent.h"
25 #include "mozilla/ipc/Endpoint.h"
26 #include "mozilla/PresShell.h"
27 #include "mozilla/ScopeExit.h"
28 #include "GeckoProfiler.h"
29 #include "nsContentUtils.h"
30 #include "nsDocShell.h"
31 #include "nsFocusManager.h"
32 #include "nsFrameLoaderOwner.h"
33 #include "nsGlobalWindowInner.h"
34 #include "nsNetUtil.h"
35 #include "nsQueryObject.h"
36 #include "nsSerializationHelper.h"
37 #include "nsFrameLoader.h"
38 #include "nsScriptSecurityManager.h"
40 #include "mozilla/dom/JSWindowActorBinding.h"
41 #include "mozilla/dom/JSWindowActorChild.h"
42 #include "mozilla/dom/JSActorService.h"
43 #include "nsIHttpChannelInternal.h"
44 #include "nsIURIMutator.h"
45 #include "nsURLHelper.h"
47 using namespace mozilla::ipc
;
48 using namespace mozilla::dom::ipc
;
50 namespace mozilla::dom
{
52 WindowGlobalChild::WindowGlobalChild(dom::WindowContext
* aWindowContext
,
53 nsIPrincipal
* aPrincipal
,
55 : mWindowContext(aWindowContext
),
56 mDocumentPrincipal(aPrincipal
),
57 mDocumentURI(aDocumentURI
) {
58 MOZ_DIAGNOSTIC_ASSERT(mWindowContext
);
59 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal
);
62 NS_NewURI(getter_AddRefs(mDocumentURI
), "about:blank");
65 // Registers a DOM Window with the profiler. It re-registers the same Inner
66 // Window ID with different URIs because when a Browsing context is first
67 // loaded, the first url loaded in it will be about:blank. This call keeps the
68 // first non-about:blank registration of window and discards the previous one.
69 uint64_t embedderInnerWindowID
= 0;
70 if (BrowsingContext()->GetParent()) {
71 embedderInnerWindowID
= BrowsingContext()->GetEmbedderInnerWindowId();
73 profiler_register_page(
74 BrowsingContext()->BrowserId(), InnerWindowId(),
75 nsContentUtils::TruncatedURLForDisplay(aDocumentURI
, 1024),
76 embedderInnerWindowID
, BrowsingContext()->UsePrivateBrowsing());
79 already_AddRefed
<WindowGlobalChild
> WindowGlobalChild::Create(
80 nsGlobalWindowInner
* aWindow
) {
81 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
82 // Opener policy is set when we start to load a document. Here, we ensure we
83 // have set the correct Opener policy so that it will be available in the
84 // parent process through window global child.
85 nsCOMPtr
<nsIChannel
> chan
= aWindow
->GetDocument()->GetChannel();
86 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
? chan
->LoadInfo() : nullptr;
87 nsCOMPtr
<nsIHttpChannelInternal
> httpChan
= do_QueryInterface(chan
);
88 nsILoadInfo::CrossOriginOpenerPolicy policy
;
90 loadInfo
->GetExternalContentPolicyType() ==
91 ExtContentPolicy::TYPE_DOCUMENT
&&
92 NS_SUCCEEDED(httpChan
->GetCrossOriginOpenerPolicy(&policy
))) {
93 MOZ_DIAGNOSTIC_ASSERT(policy
==
94 aWindow
->GetBrowsingContext()->GetOpenerPolicy());
98 WindowGlobalInit init
= WindowGlobalActor::WindowInitializer(aWindow
);
99 RefPtr
<WindowGlobalChild
> wgc
= CreateDisconnected(init
);
101 // Send the link constructor over PBrowser, or link over PInProcess.
102 if (XRE_IsParentProcess()) {
103 InProcessChild
* ipChild
= InProcessChild::Singleton();
104 InProcessParent
* ipParent
= InProcessParent::Singleton();
105 if (!ipChild
|| !ipParent
) {
109 ManagedEndpoint
<PWindowGlobalParent
> endpoint
=
110 ipChild
->OpenPWindowGlobalEndpoint(wgc
);
111 ipParent
->BindPWindowGlobalEndpoint(std::move(endpoint
),
112 wgc
->WindowContext()->Canonical());
114 RefPtr
<BrowserChild
> browserChild
=
115 BrowserChild::GetFrom(static_cast<mozIDOMWindow
*>(aWindow
));
116 MOZ_ASSERT(browserChild
);
118 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
119 dom::BrowsingContext
* bc
= aWindow
->GetBrowsingContext();
122 MOZ_DIAGNOSTIC_ASSERT(bc
->AncestorsAreCurrent());
123 MOZ_DIAGNOSTIC_ASSERT(bc
->IsInProcess());
125 ManagedEndpoint
<PWindowGlobalParent
> endpoint
=
126 browserChild
->OpenPWindowGlobalEndpoint(wgc
);
127 browserChild
->SendNewWindowGlobal(std::move(endpoint
), init
);
131 wgc
->InitWindowGlobal(aWindow
);
135 already_AddRefed
<WindowGlobalChild
> WindowGlobalChild::CreateDisconnected(
136 const WindowGlobalInit
& aInit
) {
137 RefPtr
<dom::BrowsingContext
> browsingContext
=
138 dom::BrowsingContext::Get(aInit
.context().mBrowsingContextId
);
140 RefPtr
<dom::WindowContext
> windowContext
=
141 dom::WindowContext::GetById(aInit
.context().mInnerWindowId
);
142 MOZ_RELEASE_ASSERT(!windowContext
, "Creating duplicate WindowContext");
144 // Create our new WindowContext
145 if (XRE_IsParentProcess()) {
146 windowContext
= WindowGlobalParent::CreateDisconnected(aInit
);
148 dom::WindowContext::FieldValues fields
= aInit
.context().mFields
;
149 windowContext
= new dom::WindowContext(
150 browsingContext
, aInit
.context().mInnerWindowId
,
151 aInit
.context().mOuterWindowId
, std::move(fields
));
154 RefPtr
<WindowGlobalChild
> windowChild
= new WindowGlobalChild(
155 windowContext
, aInit
.principal(), aInit
.documentURI());
156 windowContext
->mIsInProcess
= true;
157 windowContext
->mWindowGlobalChild
= windowChild
;
158 return windowChild
.forget();
161 void WindowGlobalChild::Init() {
162 MOZ_ASSERT(mWindowContext
->mWindowGlobalChild
== this);
163 mWindowContext
->Init();
166 void WindowGlobalChild::InitWindowGlobal(nsGlobalWindowInner
* aWindow
) {
167 mWindowGlobal
= aWindow
;
170 void WindowGlobalChild::OnNewDocument(Document
* aDocument
) {
171 MOZ_RELEASE_ASSERT(mWindowGlobal
);
172 MOZ_RELEASE_ASSERT(aDocument
);
174 // Send a series of messages to update document-specific state on
175 // WindowGlobalParent, when we change documents on an existing WindowGlobal.
176 // This data is also all sent when we construct a WindowGlobal, so anything
177 // added here should also be added to WindowGlobalActor::WindowInitializer.
179 // FIXME: Perhaps these should be combined into a smaller number of messages?
180 SendSetIsInitialDocument(aDocument
->IsInitialDocument());
181 SetDocumentURI(aDocument
->GetDocumentURI());
182 SetDocumentPrincipal(aDocument
->NodePrincipal(),
183 aDocument
->EffectiveStoragePrincipal());
185 nsCOMPtr
<nsITransportSecurityInfo
> securityInfo
;
186 if (nsCOMPtr
<nsIChannel
> channel
= aDocument
->GetChannel()) {
187 channel
->GetSecurityInfo(getter_AddRefs(securityInfo
));
189 SendUpdateDocumentSecurityInfo(securityInfo
);
191 SendUpdateDocumentCspSettings(aDocument
->GetBlockAllMixedContent(false),
192 aDocument
->GetUpgradeInsecureRequests(false));
193 SendUpdateSandboxFlags(aDocument
->GetSandboxFlags());
195 net::CookieJarSettingsArgs csArgs
;
196 net::CookieJarSettings::Cast(aDocument
->CookieJarSettings())
198 if (!SendUpdateCookieJarSettings(csArgs
)) {
200 "Failed to update document's cookie jar settings on the "
201 "WindowGlobalParent");
204 SendUpdateHttpsOnlyStatus(aDocument
->HttpsOnlyStatus());
206 // Update window context fields for the newly loaded Document.
207 WindowContext::Transaction txn
;
208 txn
.SetCookieBehavior(
209 Some(aDocument
->CookieJarSettings()->GetCookieBehavior()));
210 txn
.SetIsOnContentBlockingAllowList(
211 aDocument
->CookieJarSettings()->GetIsOnContentBlockingAllowList());
212 txn
.SetIsThirdPartyWindow(aDocument
->HasThirdPartyChannel());
213 txn
.SetIsThirdPartyTrackingResourceWindow(
214 nsContentUtils::IsThirdPartyTrackingResourceWindow(mWindowGlobal
));
215 txn
.SetIsSecureContext(mWindowGlobal
->IsSecureContext());
216 if (auto policy
= aDocument
->GetEmbedderPolicy()) {
217 txn
.SetEmbedderPolicy(*policy
);
219 txn
.SetShouldResistFingerprinting(aDocument
->ShouldResistFingerprinting(
220 RFPTarget::IsAlwaysEnabledForPrecompute
));
221 txn
.SetOverriddenFingerprintingSettings(
222 aDocument
->GetOverriddenFingerprintingSettings());
224 if (nsCOMPtr
<nsIChannel
> channel
= aDocument
->GetChannel()) {
225 nsCOMPtr
<nsILoadInfo
> loadInfo(channel
->LoadInfo());
226 txn
.SetIsOriginalFrameSource(loadInfo
->GetOriginalFrameSrcLoad());
227 txn
.SetUsingStorageAccess(loadInfo
->GetStoragePermission() !=
228 nsILoadInfo::NoStoragePermission
);
230 txn
.SetIsOriginalFrameSource(false);
233 // Init Mixed Content Fields
234 nsCOMPtr
<nsIURI
> innerDocURI
=
235 NS_GetInnermostURI(aDocument
->GetDocumentURI());
237 txn
.SetIsSecure(innerDocURI
->SchemeIs("https"));
240 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal
->GetIsLocalIpAddress() ==
241 mWindowContext
->IsLocalIP());
243 MOZ_ALWAYS_SUCCEEDS(txn
.Commit(mWindowContext
));
247 already_AddRefed
<WindowGlobalChild
> WindowGlobalChild::GetByInnerWindowId(
248 uint64_t aInnerWindowId
) {
249 if (RefPtr
<dom::WindowContext
> context
=
250 dom::WindowContext::GetById(aInnerWindowId
)) {
251 return do_AddRef(context
->GetWindowGlobalChild());
256 dom::BrowsingContext
* WindowGlobalChild::BrowsingContext() {
257 return mWindowContext
->GetBrowsingContext();
260 uint64_t WindowGlobalChild::InnerWindowId() {
261 return mWindowContext
->InnerWindowId();
264 uint64_t WindowGlobalChild::OuterWindowId() {
265 return mWindowContext
->OuterWindowId();
268 bool WindowGlobalChild::IsCurrentGlobal() {
269 return CanSend() && mWindowGlobal
->IsCurrentInnerWindow();
272 already_AddRefed
<WindowGlobalParent
> WindowGlobalChild::GetParentActor() {
276 IProtocol
* otherSide
= InProcessChild::ParentActorFor(this);
277 return do_AddRef(static_cast<WindowGlobalParent
*>(otherSide
));
280 already_AddRefed
<BrowserChild
> WindowGlobalChild::GetBrowserChild() {
281 if (IsInProcess() || !CanSend()) {
284 return do_AddRef(static_cast<BrowserChild
*>(Manager()));
287 uint64_t WindowGlobalChild::ContentParentId() {
288 if (XRE_IsParentProcess()) {
291 return ContentChild::GetSingleton()->GetID();
294 // A WindowGlobalChild is the root in its process if it has no parent, or its
295 // embedder is in a different process.
296 bool WindowGlobalChild::IsProcessRoot() {
297 if (!BrowsingContext()->GetParent()) {
301 return !BrowsingContext()->GetEmbedderElement();
304 void WindowGlobalChild::BeforeUnloadAdded() {
305 // Don't bother notifying the parent if we don't have an IPC link open.
306 if (mBeforeUnloadListeners
== 0 && CanSend()) {
307 Unused
<< mWindowContext
->SetHasBeforeUnload(true);
310 mBeforeUnloadListeners
++;
311 MOZ_ASSERT(mBeforeUnloadListeners
> 0);
314 void WindowGlobalChild::BeforeUnloadRemoved() {
315 mBeforeUnloadListeners
--;
316 MOZ_ASSERT(mBeforeUnloadListeners
>= 0);
318 if (mBeforeUnloadListeners
== 0) {
319 Unused
<< mWindowContext
->SetHasBeforeUnload(false);
323 void WindowGlobalChild::Destroy() {
324 JSActorWillDestroy();
326 mWindowContext
->Discard();
328 // Perform async IPC shutdown unless we're not in-process, and our
329 // BrowserChild is in the process of being destroyed, which will destroy
331 RefPtr
<BrowserChild
> browserChild
= GetBrowserChild();
332 if (!browserChild
|| !browserChild
->IsDestroyed()) {
337 mozilla::ipc::IPCResult
WindowGlobalChild::RecvMakeFrameLocal(
338 const MaybeDiscarded
<dom::BrowsingContext
>& aFrameContext
,
339 uint64_t aPendingSwitchId
) {
340 MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
342 MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug
,
343 ("RecvMakeFrameLocal ID=%" PRIx64
, aFrameContext
.ContextId()));
345 if (NS_WARN_IF(aFrameContext
.IsNullOrDiscarded())) {
348 dom::BrowsingContext
* frameContext
= aFrameContext
.get();
350 RefPtr
<Element
> embedderElt
= frameContext
->GetEmbedderElement();
351 if (NS_WARN_IF(!embedderElt
)) {
355 if (NS_WARN_IF(embedderElt
->GetOwnerGlobal() != GetWindowGlobal())) {
359 RefPtr
<nsFrameLoaderOwner
> flo
= do_QueryObject(embedderElt
);
360 MOZ_DIAGNOSTIC_ASSERT(flo
, "Embedder must be a nsFrameLoaderOwner");
362 // Trigger a process switch into the current process.
363 RemotenessOptions options
;
364 options
.mRemoteType
= NOT_REMOTE_TYPE
;
365 options
.mPendingSwitchID
.Construct(aPendingSwitchId
);
366 options
.mSwitchingInProgressLoad
= true;
367 flo
->ChangeRemoteness(options
, IgnoreErrors());
371 mozilla::ipc::IPCResult
WindowGlobalChild::RecvMakeFrameRemote(
372 const MaybeDiscarded
<dom::BrowsingContext
>& aFrameContext
,
373 ManagedEndpoint
<PBrowserBridgeChild
>&& aEndpoint
, const TabId
& aTabId
,
374 const LayersId
& aLayersId
, MakeFrameRemoteResolver
&& aResolve
) {
375 MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
377 MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug
,
378 ("RecvMakeFrameRemote ID=%" PRIx64
, aFrameContext
.ContextId()));
380 if (!aLayersId
.IsValid()) {
381 return IPC_FAIL(this, "Received an invalid LayersId");
384 // Resolve the promise when this function exits, as we'll have fully unloaded
386 auto scopeExit
= MakeScopeExit([&] { aResolve(true); });
388 // Get a BrowsingContext if we're not null or discarded. We don't want to
389 // early-return before we connect the BrowserBridgeChild, as otherwise we'll
390 // never break the channel in the parent.
391 RefPtr
<dom::BrowsingContext
> frameContext
;
392 if (!aFrameContext
.IsDiscarded()) {
393 frameContext
= aFrameContext
.get();
396 // Immediately construct the BrowserBridgeChild so we can destroy it cleanly
397 // if the process switch fails.
398 RefPtr
<BrowserBridgeChild
> bridge
=
399 new BrowserBridgeChild(frameContext
, aTabId
, aLayersId
);
400 RefPtr
<BrowserChild
> manager
= GetBrowserChild();
402 !manager
->BindPBrowserBridgeEndpoint(std::move(aEndpoint
), bridge
))) {
406 // Synchronously delete de actor here rather than using SendBeginDestroy(), as
407 // we haven't initialized it yet.
409 MakeScopeExit([&] { BrowserBridgeChild::Send__delete__(bridge
); });
411 // Immediately tear down the actor if we don't have a valid FrameContext.
412 if (NS_WARN_IF(aFrameContext
.IsNullOrDiscarded())) {
416 RefPtr
<Element
> embedderElt
= frameContext
->GetEmbedderElement();
417 if (NS_WARN_IF(!embedderElt
)) {
421 if (NS_WARN_IF(embedderElt
->GetOwnerGlobal() != GetWindowGlobal())) {
425 RefPtr
<nsFrameLoaderOwner
> flo
= do_QueryObject(embedderElt
);
426 MOZ_DIAGNOSTIC_ASSERT(flo
, "Embedder must be a nsFrameLoaderOwner");
428 // Trgger a process switch into the specified process.
429 IgnoredErrorResult rv
;
430 flo
->ChangeRemotenessWithBridge(bridge
, rv
);
431 if (NS_WARN_IF(rv
.Failed())) {
435 // Everything succeeded, so don't delete the bridge.
436 deleteBridge
.release();
441 mozilla::ipc::IPCResult
WindowGlobalChild::RecvDrawSnapshot(
442 const Maybe
<IntRect
>& aRect
, const float& aScale
,
443 const nscolor
& aBackgroundColor
, const uint32_t& aFlags
,
444 DrawSnapshotResolver
&& aResolve
) {
445 aResolve(gfx::PaintFragment::Record(BrowsingContext(), aRect
, aScale
,
447 (gfx::CrossProcessPaintFlags
)aFlags
));
451 mozilla::ipc::IPCResult
452 WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() {
453 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetWindowGlobal();
455 inner
->SaveStorageAccessPermissionGranted();
461 mozilla::ipc::IPCResult
WindowGlobalChild::RecvDispatchSecurityPolicyViolation(
462 const nsString
& aViolationEventJSON
) {
463 nsGlobalWindowInner
* window
= GetWindowGlobal();
468 Document
* doc
= window
->GetDocument();
473 SecurityPolicyViolationEventInit violationEvent
;
474 if (!violationEvent
.Init(aViolationEventJSON
)) {
478 RefPtr
<Event
> event
= SecurityPolicyViolationEvent::Constructor(
479 doc
, u
"securitypolicyviolation"_ns
, violationEvent
);
480 event
->SetTrusted(true);
481 doc
->DispatchEvent(*event
, IgnoreErrors());
485 mozilla::ipc::IPCResult
WindowGlobalChild::RecvAddBlockedFrameNodeByClassifier(
486 const MaybeDiscardedBrowsingContext
& aNode
) {
487 if (aNode
.IsNullOrDiscarded()) {
491 nsGlobalWindowInner
* window
= GetWindowGlobal();
496 Document
* doc
= window
->GetDocument();
501 MOZ_ASSERT(aNode
.get()->GetEmbedderElement()->OwnerDoc() == doc
);
502 doc
->AddBlockedNodeByClassifier(aNode
.get()->GetEmbedderElement());
506 mozilla::ipc::IPCResult
WindowGlobalChild::RecvResetScalingZoom() {
507 if (Document
* doc
= mWindowGlobal
->GetExtantDoc()) {
508 if (PresShell
* ps
= doc
->GetPresShell()) {
509 ps
->SetResolutionAndScaleTo(1.0,
510 ResolutionChangeOrigin::MainThreadAdjustment
);
516 mozilla::ipc::IPCResult
WindowGlobalChild::RecvSetContainerFeaturePolicy(
517 dom::FeaturePolicy
* aContainerFeaturePolicy
) {
518 mContainerFeaturePolicy
= aContainerFeaturePolicy
;
522 mozilla::ipc::IPCResult
WindowGlobalChild::RecvRestoreDocShellState(
523 const dom::sessionstore::DocShellRestoreState
& aState
,
524 RestoreDocShellStateResolver
&& aResolve
) {
526 SessionStoreUtils::RestoreDocShellState(mWindowGlobal
->GetDocShell(),
533 mozilla::ipc::IPCResult
WindowGlobalChild::RecvRestoreTabContent(
534 dom::SessionStoreRestoreData
* aData
, RestoreTabContentResolver
&& aResolve
) {
535 aData
->RestoreInto(BrowsingContext());
540 IPCResult
WindowGlobalChild::RecvRawMessage(
541 const JSActorMessageMeta
& aMeta
, const Maybe
<ClonedMessageData
>& aData
,
542 const Maybe
<ClonedMessageData
>& aStack
) {
543 Maybe
<StructuredCloneData
> data
;
546 data
->BorrowFromClonedMessageData(*aData
);
548 Maybe
<StructuredCloneData
> stack
;
551 stack
->BorrowFromClonedMessageData(*aStack
);
553 ReceiveRawMessage(aMeta
, std::move(data
), std::move(stack
));
557 IPCResult
WindowGlobalChild::RecvNotifyPermissionChange(const nsCString
& aType
,
558 uint32_t aPermission
) {
559 nsCOMPtr
<nsIObserverService
> observerService
= services::GetObserverService();
560 NS_ENSURE_TRUE(observerService
,
561 IPC_FAIL(this, "Failed to get observer service"));
562 nsPIDOMWindowInner
* notifyTarget
=
563 static_cast<nsPIDOMWindowInner
*>(this->GetWindowGlobal());
564 observerService
->NotifyObservers(notifyTarget
, "perm-changed-notify-only",
565 NS_ConvertUTF8toUTF16(aType
).get());
566 // We only need to handle the revoked permission case here. The permission
567 // grant case is handled via the Storage Access API code.
568 if (this->GetWindowGlobal() &&
569 this->GetWindowGlobal()->UsingStorageAccess() &&
570 aPermission
!= nsIPermissionManager::ALLOW_ACTION
) {
571 this->GetWindowGlobal()->SaveStorageAccessPermissionRevoked();
576 void WindowGlobalChild::SetDocumentURI(nsIURI
* aDocumentURI
) {
577 // Registers a DOM Window with the profiler. It re-registers the same Inner
578 // Window ID with different URIs because when a Browsing context is first
579 // loaded, the first url loaded in it will be about:blank. This call keeps the
580 // first non-about:blank registration of window and discards the previous one.
581 uint64_t embedderInnerWindowID
= 0;
582 if (BrowsingContext()->GetParent()) {
583 embedderInnerWindowID
= BrowsingContext()->GetEmbedderInnerWindowId();
585 profiler_register_page(
586 BrowsingContext()->BrowserId(), InnerWindowId(),
587 nsContentUtils::TruncatedURLForDisplay(aDocumentURI
, 1024),
588 embedderInnerWindowID
, BrowsingContext()->UsePrivateBrowsing());
590 if (StaticPrefs::dom_security_setdocumenturi()) {
591 nsCOMPtr
<nsIURI
> principalURI
= mDocumentPrincipal
->GetURI();
592 if (mDocumentPrincipal
->GetIsNullPrincipal()) {
593 nsCOMPtr
<nsIPrincipal
> precursor
=
594 mDocumentPrincipal
->GetPrecursorPrincipal();
596 principalURI
= precursor
->GetURI();
600 MOZ_DIAGNOSTIC_ASSERT(!nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(
601 principalURI
, aDocumentURI
),
602 "Setting DocumentURI with a different origin "
603 "than principal URI");
606 mDocumentURI
= aDocumentURI
;
607 SendUpdateDocumentURI(WrapNotNull(aDocumentURI
));
610 void WindowGlobalChild::SetDocumentPrincipal(
611 nsIPrincipal
* aNewDocumentPrincipal
,
612 nsIPrincipal
* aNewDocumentStoragePrincipal
) {
613 MOZ_ASSERT(mDocumentPrincipal
->Equals(aNewDocumentPrincipal
));
614 mDocumentPrincipal
= aNewDocumentPrincipal
;
615 SendUpdateDocumentPrincipal(aNewDocumentPrincipal
,
616 aNewDocumentStoragePrincipal
);
619 const nsACString
& WindowGlobalChild::GetRemoteType() {
620 if (XRE_IsContentProcess()) {
621 return ContentChild::GetSingleton()->GetRemoteType();
624 return NOT_REMOTE_TYPE
;
627 already_AddRefed
<JSWindowActorChild
> WindowGlobalChild::GetActor(
628 JSContext
* aCx
, const nsACString
& aName
, ErrorResult
& aRv
) {
629 return JSActorManager::GetActor(aCx
, aName
, aRv
)
630 .downcast
<JSWindowActorChild
>();
633 already_AddRefed
<JSWindowActorChild
> WindowGlobalChild::GetExistingActor(
634 const nsACString
& aName
) {
635 return JSActorManager::GetExistingActor(aName
).downcast
<JSWindowActorChild
>();
638 already_AddRefed
<JSActor
> WindowGlobalChild::InitJSActor(
639 JS::Handle
<JSObject
*> aMaybeActor
, const nsACString
& aName
,
641 RefPtr
<JSWindowActorChild
> actor
;
642 if (aMaybeActor
.get()) {
643 aRv
= UNWRAP_OBJECT(JSWindowActorChild
, aMaybeActor
.get(), actor
);
648 actor
= new JSWindowActorChild();
651 MOZ_RELEASE_ASSERT(!actor
->GetManager(),
652 "mManager was already initialized once!");
653 actor
->Init(aName
, this);
654 return actor
.forget();
657 void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy
) {
658 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
659 "Destroying WindowGlobalChild can run script");
661 // If our WindowContext hasn't been marked as discarded yet, ensure it's
662 // marked as discarded at this point.
663 mWindowContext
->Discard();
665 profiler_unregister_page(InnerWindowId());
667 // Destroy our JSActors, and reject any pending queries.
671 bool WindowGlobalChild::IsSameOriginWith(
672 const dom::WindowContext
* aOther
) const {
673 if (aOther
== WindowContext()) {
677 MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aOther
->Group());
678 if (nsGlobalWindowInner
* otherWin
= aOther
->GetInnerWindow()) {
679 return mDocumentPrincipal
->Equals(otherWin
->GetPrincipal());
684 bool WindowGlobalChild::SameOriginWithTop() {
685 return IsSameOriginWith(WindowContext()->TopWindowContext());
688 // For historical context, see:
690 // Bug 13871: Prevent frameset spoofing
691 // Bug 103638: Targets with same name in different windows open in wrong
692 // window with javascript
693 // Bug 408052: Adopt "ancestor" frame navigation policy
694 // Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
695 // origin attribute isolation
696 // Bug 1810619: Crash at null in nsDocShell::ValidateOrigin
697 bool WindowGlobalChild::CanNavigate(dom::BrowsingContext
* aTarget
,
698 bool aConsiderOpener
) {
699 MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aTarget
->Group(),
700 "A WindowGlobalChild should never try to navigate a "
701 "BrowsingContext from another group");
703 auto isFileScheme
= [](nsIPrincipal
* aPrincipal
) -> bool {
704 // NOTE: This code previously checked for a file scheme using
705 // `nsIPrincipal::GetURI()` combined with `NS_GetInnermostURI`. We no longer
706 // use GetURI, as it has been deprecated, and it makes more sense to take
707 // advantage of the pre-computed origin, which will already use the
708 // innermost URI (bug 1810619)
709 nsAutoCString origin
, scheme
;
710 return NS_SUCCEEDED(aPrincipal
->GetOriginNoSuffix(origin
)) &&
711 NS_SUCCEEDED(net_ExtractURLScheme(origin
, scheme
)) &&
715 // A frame can navigate itself and its own root.
716 if (aTarget
== BrowsingContext() || aTarget
== BrowsingContext()->Top()) {
720 // If the target frame doesn't yet have a WindowContext, start checking
721 // principals from its direct ancestor instead. It would inherit its principal
722 // from this document upon creation.
723 dom::WindowContext
* initialWc
= aTarget
->GetCurrentWindowContext();
725 initialWc
= aTarget
->GetParentWindowContext();
728 // A frame can navigate any frame with a same-origin ancestor.
729 bool isFileDocument
= isFileScheme(DocumentPrincipal());
730 for (dom::WindowContext
* wc
= initialWc
; wc
;
731 wc
= wc
->GetParentWindowContext()) {
732 dom::WindowGlobalChild
* wgc
= wc
->GetWindowGlobalChild();
734 continue; // out-of process, so not same-origin.
737 if (DocumentPrincipal()->Equals(wgc
->DocumentPrincipal())) {
741 // Not strictly equal, special case if both are file: URIs.
743 // file: URIs are considered the same domain for the purpose of frame
744 // navigation, regardless of script accessibility (bug 420425).
745 if (isFileDocument
&& isFileScheme(wgc
->DocumentPrincipal())) {
750 // If the target is a top-level document, a frame can navigate it if it can
751 // navigate its opener.
752 if (aConsiderOpener
&& !aTarget
->GetParent()) {
753 if (RefPtr
<dom::BrowsingContext
> opener
= aTarget
->GetOpener()) {
754 return CanNavigate(opener
, false);
761 // FindWithName follows the rules for choosing a browsing context,
762 // with the exception of sandboxing for iframes. The implementation
763 // for arbitrarily choosing between two browsing contexts with the
764 // same name is as follows:
766 // 1) The start browsing context, i.e. 'this'
767 // 2) Descendants in insertion order
769 // 4) Siblings and their children, both in insertion order
770 // 5) After this we iteratively follow the parent chain, repeating 3
772 // 6) If there is no parent, consider all other top level browsing
773 // contexts and their children, both in insertion order
776 // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
777 dom::BrowsingContext
* WindowGlobalChild::FindBrowsingContextWithName(
778 const nsAString
& aName
, bool aUseEntryGlobalForAccessCheck
) {
779 RefPtr
<WindowGlobalChild
> requestingContext
= this;
780 if (aUseEntryGlobalForAccessCheck
) {
781 if (nsGlobalWindowInner
* caller
= nsContentUtils::EntryInnerWindow()) {
782 if (caller
->GetBrowsingContextGroup() == WindowContext()->Group()) {
783 requestingContext
= caller
->GetWindowGlobalChild();
785 MOZ_RELEASE_ASSERT(caller
->GetPrincipal()->IsSystemPrincipal(),
786 "caller must be either same-group or system");
790 MOZ_ASSERT(requestingContext
, "must have a requestingContext");
792 dom::BrowsingContext
* found
= nullptr;
793 if (aName
.IsEmpty()) {
794 // You can't find a browsing context with an empty name.
796 } else if (aName
.LowerCaseEqualsLiteral("_blank")) {
797 // Just return null. Caller must handle creating a new window with
800 } else if (nsContentUtils::IsSpecialName(aName
)) {
801 found
= BrowsingContext()->FindWithSpecialName(aName
, *requestingContext
);
802 } else if (dom::BrowsingContext
* child
=
803 BrowsingContext()->FindWithNameInSubtree(aName
,
804 requestingContext
)) {
807 dom::WindowContext
* current
= WindowContext();
810 Span
<RefPtr
<dom::BrowsingContext
>> siblings
;
811 dom::WindowContext
* parent
= current
->GetParentWindowContext();
814 // We've reached the root of the tree, consider browsing
815 // contexts in the same browsing context group.
816 siblings
= WindowContext()->Group()->Toplevels();
817 } else if (dom::BrowsingContext
* bc
= parent
->GetBrowsingContext();
818 bc
&& bc
->NameEquals(aName
) &&
819 requestingContext
->CanNavigate(bc
) && bc
->IsTargetable()) {
823 siblings
= parent
->NonSyntheticChildren();
826 for (dom::BrowsingContext
* sibling
: siblings
) {
827 if (sibling
== current
->GetBrowsingContext()) {
831 if (dom::BrowsingContext
* relative
=
832 sibling
->FindWithNameInSubtree(aName
, requestingContext
)) {
834 // Breaks the outer loop
844 // Helpers should perform access control checks, which means that we
845 // only need to assert that we can access found.
846 MOZ_DIAGNOSTIC_ASSERT(!found
|| requestingContext
->CanNavigate(found
));
851 void WindowGlobalChild::UnblockBFCacheFor(BFCacheStatus aStatus
) {
852 SendUpdateBFCacheStatus(0, aStatus
);
855 void WindowGlobalChild::BlockBFCacheFor(BFCacheStatus aStatus
) {
856 SendUpdateBFCacheStatus(aStatus
, 0);
859 WindowGlobalChild::~WindowGlobalChild() = default;
861 JSObject
* WindowGlobalChild::WrapObject(JSContext
* aCx
,
862 JS::Handle
<JSObject
*> aGivenProto
) {
863 return WindowGlobalChild_Binding::Wrap(aCx
, this, aGivenProto
);
866 nsISupports
* WindowGlobalChild::GetParentObject() {
867 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
870 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(WindowGlobalChild
, mWindowGlobal
,
871 mContainerFeaturePolicy
,
874 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild
)
875 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
876 NS_INTERFACE_MAP_ENTRY(nsISupports
)
879 NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild
)
880 NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild
)
882 } // namespace mozilla::dom