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"
39 #include "mozilla/dom/JSWindowActorBinding.h"
40 #include "mozilla/dom/JSWindowActorChild.h"
41 #include "mozilla/dom/JSActorService.h"
42 #include "nsIHttpChannelInternal.h"
43 #include "nsIURIMutator.h"
44 #include "nsURLHelper.h"
46 using namespace mozilla::ipc
;
47 using namespace mozilla::dom::ipc
;
49 namespace mozilla::dom
{
51 WindowGlobalChild::WindowGlobalChild(dom::WindowContext
* aWindowContext
,
52 nsIPrincipal
* aPrincipal
,
54 : mWindowContext(aWindowContext
),
55 mDocumentPrincipal(aPrincipal
),
56 mDocumentURI(aDocumentURI
) {
57 MOZ_DIAGNOSTIC_ASSERT(mWindowContext
);
58 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal
);
61 NS_NewURI(getter_AddRefs(mDocumentURI
), "about:blank");
64 // Registers a DOM Window with the profiler. It re-registers the same Inner
65 // Window ID with different URIs because when a Browsing context is first
66 // loaded, the first url loaded in it will be about:blank. This call keeps the
67 // first non-about:blank registration of window and discards the previous one.
68 uint64_t embedderInnerWindowID
= 0;
69 if (BrowsingContext()->GetParent()) {
70 embedderInnerWindowID
= BrowsingContext()->GetEmbedderInnerWindowId();
72 profiler_register_page(
73 BrowsingContext()->BrowserId(), InnerWindowId(),
74 nsContentUtils::TruncatedURLForDisplay(aDocumentURI
, 1024),
75 embedderInnerWindowID
, BrowsingContext()->UsePrivateBrowsing());
78 already_AddRefed
<WindowGlobalChild
> WindowGlobalChild::Create(
79 nsGlobalWindowInner
* aWindow
) {
80 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
81 // Opener policy is set when we start to load a document. Here, we ensure we
82 // have set the correct Opener policy so that it will be available in the
83 // parent process through window global child.
84 nsCOMPtr
<nsIChannel
> chan
= aWindow
->GetDocument()->GetChannel();
85 nsCOMPtr
<nsILoadInfo
> loadInfo
= chan
? chan
->LoadInfo() : nullptr;
86 nsCOMPtr
<nsIHttpChannelInternal
> httpChan
= do_QueryInterface(chan
);
87 nsILoadInfo::CrossOriginOpenerPolicy policy
;
89 loadInfo
->GetExternalContentPolicyType() ==
90 ExtContentPolicy::TYPE_DOCUMENT
&&
91 NS_SUCCEEDED(httpChan
->GetCrossOriginOpenerPolicy(&policy
))) {
92 MOZ_DIAGNOSTIC_ASSERT(policy
==
93 aWindow
->GetBrowsingContext()->GetOpenerPolicy());
97 WindowGlobalInit init
= WindowGlobalActor::WindowInitializer(aWindow
);
98 RefPtr
<WindowGlobalChild
> wgc
= CreateDisconnected(init
);
100 // Send the link constructor over PBrowser, or link over PInProcess.
101 if (XRE_IsParentProcess()) {
102 InProcessChild
* ipChild
= InProcessChild::Singleton();
103 InProcessParent
* ipParent
= InProcessParent::Singleton();
104 if (!ipChild
|| !ipParent
) {
108 ManagedEndpoint
<PWindowGlobalParent
> endpoint
=
109 ipChild
->OpenPWindowGlobalEndpoint(wgc
);
110 ipParent
->BindPWindowGlobalEndpoint(std::move(endpoint
),
111 wgc
->WindowContext()->Canonical());
113 RefPtr
<BrowserChild
> browserChild
=
114 BrowserChild::GetFrom(static_cast<mozIDOMWindow
*>(aWindow
));
115 MOZ_ASSERT(browserChild
);
117 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
118 dom::BrowsingContext
* bc
= aWindow
->GetBrowsingContext();
121 MOZ_DIAGNOSTIC_ASSERT(bc
->AncestorsAreCurrent());
122 MOZ_DIAGNOSTIC_ASSERT(bc
->IsInProcess());
124 ManagedEndpoint
<PWindowGlobalParent
> endpoint
=
125 browserChild
->OpenPWindowGlobalEndpoint(wgc
);
126 browserChild
->SendNewWindowGlobal(std::move(endpoint
), init
);
130 wgc
->InitWindowGlobal(aWindow
);
134 already_AddRefed
<WindowGlobalChild
> WindowGlobalChild::CreateDisconnected(
135 const WindowGlobalInit
& aInit
) {
136 RefPtr
<dom::BrowsingContext
> browsingContext
=
137 dom::BrowsingContext::Get(aInit
.context().mBrowsingContextId
);
139 RefPtr
<dom::WindowContext
> windowContext
=
140 dom::WindowContext::GetById(aInit
.context().mInnerWindowId
);
141 MOZ_RELEASE_ASSERT(!windowContext
, "Creating duplicate WindowContext");
143 // Create our new WindowContext
144 if (XRE_IsParentProcess()) {
145 windowContext
= WindowGlobalParent::CreateDisconnected(aInit
);
147 dom::WindowContext::FieldValues fields
= aInit
.context().mFields
;
148 windowContext
= new dom::WindowContext(
149 browsingContext
, aInit
.context().mInnerWindowId
,
150 aInit
.context().mOuterWindowId
, std::move(fields
));
153 RefPtr
<WindowGlobalChild
> windowChild
= new WindowGlobalChild(
154 windowContext
, aInit
.principal(), aInit
.documentURI());
155 windowContext
->mIsInProcess
= true;
156 windowContext
->mWindowGlobalChild
= windowChild
;
157 return windowChild
.forget();
160 void WindowGlobalChild::Init() {
161 MOZ_ASSERT(mWindowContext
->mWindowGlobalChild
== this);
162 mWindowContext
->Init();
165 void WindowGlobalChild::InitWindowGlobal(nsGlobalWindowInner
* aWindow
) {
166 mWindowGlobal
= aWindow
;
169 void WindowGlobalChild::OnNewDocument(Document
* aDocument
) {
170 MOZ_RELEASE_ASSERT(mWindowGlobal
);
171 MOZ_RELEASE_ASSERT(aDocument
);
173 // Send a series of messages to update document-specific state on
174 // WindowGlobalParent, when we change documents on an existing WindowGlobal.
175 // This data is also all sent when we construct a WindowGlobal, so anything
176 // added here should also be added to WindowGlobalActor::WindowInitializer.
178 // FIXME: Perhaps these should be combined into a smaller number of messages?
179 SendSetIsInitialDocument(aDocument
->IsInitialDocument());
180 SetDocumentURI(aDocument
->GetDocumentURI());
181 SetDocumentPrincipal(aDocument
->NodePrincipal(),
182 aDocument
->EffectiveStoragePrincipal());
184 nsCOMPtr
<nsITransportSecurityInfo
> securityInfo
;
185 if (nsCOMPtr
<nsIChannel
> channel
= aDocument
->GetChannel()) {
186 channel
->GetSecurityInfo(getter_AddRefs(securityInfo
));
188 SendUpdateDocumentSecurityInfo(securityInfo
);
190 SendUpdateDocumentCspSettings(aDocument
->GetBlockAllMixedContent(false),
191 aDocument
->GetUpgradeInsecureRequests(false));
192 SendUpdateSandboxFlags(aDocument
->GetSandboxFlags());
194 net::CookieJarSettingsArgs csArgs
;
195 net::CookieJarSettings::Cast(aDocument
->CookieJarSettings())
197 if (!SendUpdateCookieJarSettings(csArgs
)) {
199 "Failed to update document's cookie jar settings on the "
200 "WindowGlobalParent");
203 SendUpdateHttpsOnlyStatus(aDocument
->HttpsOnlyStatus());
205 // Update window context fields for the newly loaded Document.
206 WindowContext::Transaction txn
;
207 txn
.SetCookieBehavior(
208 Some(aDocument
->CookieJarSettings()->GetCookieBehavior()));
209 txn
.SetIsOnContentBlockingAllowList(
210 aDocument
->CookieJarSettings()->GetIsOnContentBlockingAllowList());
211 txn
.SetIsThirdPartyWindow(aDocument
->HasThirdPartyChannel());
212 txn
.SetIsThirdPartyTrackingResourceWindow(
213 nsContentUtils::IsThirdPartyTrackingResourceWindow(mWindowGlobal
));
214 txn
.SetIsSecureContext(mWindowGlobal
->IsSecureContext());
215 if (auto policy
= aDocument
->GetEmbedderPolicy()) {
216 txn
.SetEmbedderPolicy(*policy
);
219 if (nsCOMPtr
<nsIChannel
> channel
= aDocument
->GetChannel()) {
220 nsCOMPtr
<nsILoadInfo
> loadInfo(channel
->LoadInfo());
221 txn
.SetIsOriginalFrameSource(loadInfo
->GetOriginalFrameSrcLoad());
223 txn
.SetIsOriginalFrameSource(false);
226 // Init Mixed Content Fields
227 nsCOMPtr
<nsIURI
> innerDocURI
=
228 NS_GetInnermostURI(aDocument
->GetDocumentURI());
230 txn
.SetIsSecure(innerDocURI
->SchemeIs("https"));
233 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal
->GetIsLocalIpAddress() ==
234 mWindowContext
->IsLocalIP());
236 MOZ_ALWAYS_SUCCEEDS(txn
.Commit(mWindowContext
));
240 already_AddRefed
<WindowGlobalChild
> WindowGlobalChild::GetByInnerWindowId(
241 uint64_t aInnerWindowId
) {
242 if (RefPtr
<dom::WindowContext
> context
=
243 dom::WindowContext::GetById(aInnerWindowId
)) {
244 return do_AddRef(context
->GetWindowGlobalChild());
249 dom::BrowsingContext
* WindowGlobalChild::BrowsingContext() {
250 return mWindowContext
->GetBrowsingContext();
253 uint64_t WindowGlobalChild::InnerWindowId() {
254 return mWindowContext
->InnerWindowId();
257 uint64_t WindowGlobalChild::OuterWindowId() {
258 return mWindowContext
->OuterWindowId();
261 bool WindowGlobalChild::IsCurrentGlobal() {
262 return CanSend() && mWindowGlobal
->IsCurrentInnerWindow();
265 already_AddRefed
<WindowGlobalParent
> WindowGlobalChild::GetParentActor() {
269 IProtocol
* otherSide
= InProcessChild::ParentActorFor(this);
270 return do_AddRef(static_cast<WindowGlobalParent
*>(otherSide
));
273 already_AddRefed
<BrowserChild
> WindowGlobalChild::GetBrowserChild() {
274 if (IsInProcess() || !CanSend()) {
277 return do_AddRef(static_cast<BrowserChild
*>(Manager()));
280 uint64_t WindowGlobalChild::ContentParentId() {
281 if (XRE_IsParentProcess()) {
284 return ContentChild::GetSingleton()->GetID();
287 // A WindowGlobalChild is the root in its process if it has no parent, or its
288 // embedder is in a different process.
289 bool WindowGlobalChild::IsProcessRoot() {
290 if (!BrowsingContext()->GetParent()) {
294 return !BrowsingContext()->GetEmbedderElement();
297 void WindowGlobalChild::BeforeUnloadAdded() {
298 // Don't bother notifying the parent if we don't have an IPC link open.
299 if (mBeforeUnloadListeners
== 0 && CanSend()) {
300 Unused
<< mWindowContext
->SetHasBeforeUnload(true);
303 mBeforeUnloadListeners
++;
304 MOZ_ASSERT(mBeforeUnloadListeners
> 0);
307 void WindowGlobalChild::BeforeUnloadRemoved() {
308 mBeforeUnloadListeners
--;
309 MOZ_ASSERT(mBeforeUnloadListeners
>= 0);
311 if (mBeforeUnloadListeners
== 0) {
312 Unused
<< mWindowContext
->SetHasBeforeUnload(false);
316 void WindowGlobalChild::Destroy() {
317 JSActorWillDestroy();
319 mWindowContext
->Discard();
321 // Perform async IPC shutdown unless we're not in-process, and our
322 // BrowserChild is in the process of being destroyed, which will destroy
324 RefPtr
<BrowserChild
> browserChild
= GetBrowserChild();
325 if (!browserChild
|| !browserChild
->IsDestroyed()) {
330 mozilla::ipc::IPCResult
WindowGlobalChild::RecvMakeFrameLocal(
331 const MaybeDiscarded
<dom::BrowsingContext
>& aFrameContext
,
332 uint64_t aPendingSwitchId
) {
333 MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
335 MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug
,
336 ("RecvMakeFrameLocal ID=%" PRIx64
, aFrameContext
.ContextId()));
338 if (NS_WARN_IF(aFrameContext
.IsNullOrDiscarded())) {
341 dom::BrowsingContext
* frameContext
= aFrameContext
.get();
343 RefPtr
<Element
> embedderElt
= frameContext
->GetEmbedderElement();
344 if (NS_WARN_IF(!embedderElt
)) {
348 if (NS_WARN_IF(embedderElt
->GetOwnerGlobal() != GetWindowGlobal())) {
352 RefPtr
<nsFrameLoaderOwner
> flo
= do_QueryObject(embedderElt
);
353 MOZ_DIAGNOSTIC_ASSERT(flo
, "Embedder must be a nsFrameLoaderOwner");
355 // Trigger a process switch into the current process.
356 RemotenessOptions options
;
357 options
.mRemoteType
= NOT_REMOTE_TYPE
;
358 options
.mPendingSwitchID
.Construct(aPendingSwitchId
);
359 options
.mSwitchingInProgressLoad
= true;
360 flo
->ChangeRemoteness(options
, IgnoreErrors());
364 mozilla::ipc::IPCResult
WindowGlobalChild::RecvMakeFrameRemote(
365 const MaybeDiscarded
<dom::BrowsingContext
>& aFrameContext
,
366 ManagedEndpoint
<PBrowserBridgeChild
>&& aEndpoint
, const TabId
& aTabId
,
367 const LayersId
& aLayersId
, MakeFrameRemoteResolver
&& aResolve
) {
368 MOZ_DIAGNOSTIC_ASSERT(XRE_IsContentProcess());
370 MOZ_LOG(BrowsingContext::GetLog(), LogLevel::Debug
,
371 ("RecvMakeFrameRemote ID=%" PRIx64
, aFrameContext
.ContextId()));
373 if (!aLayersId
.IsValid()) {
374 return IPC_FAIL(this, "Received an invalid LayersId");
377 // Resolve the promise when this function exits, as we'll have fully unloaded
379 auto scopeExit
= MakeScopeExit([&] { aResolve(true); });
381 // Get a BrowsingContext if we're not null or discarded. We don't want to
382 // early-return before we connect the BrowserBridgeChild, as otherwise we'll
383 // never break the channel in the parent.
384 RefPtr
<dom::BrowsingContext
> frameContext
;
385 if (!aFrameContext
.IsDiscarded()) {
386 frameContext
= aFrameContext
.get();
389 // Immediately construct the BrowserBridgeChild so we can destroy it cleanly
390 // if the process switch fails.
391 RefPtr
<BrowserBridgeChild
> bridge
=
392 new BrowserBridgeChild(frameContext
, aTabId
, aLayersId
);
393 RefPtr
<BrowserChild
> manager
= GetBrowserChild();
395 !manager
->BindPBrowserBridgeEndpoint(std::move(aEndpoint
), bridge
))) {
399 // Synchronously delete de actor here rather than using SendBeginDestroy(), as
400 // we haven't initialized it yet.
402 MakeScopeExit([&] { BrowserBridgeChild::Send__delete__(bridge
); });
404 // Immediately tear down the actor if we don't have a valid FrameContext.
405 if (NS_WARN_IF(aFrameContext
.IsNullOrDiscarded())) {
409 RefPtr
<Element
> embedderElt
= frameContext
->GetEmbedderElement();
410 if (NS_WARN_IF(!embedderElt
)) {
414 if (NS_WARN_IF(embedderElt
->GetOwnerGlobal() != GetWindowGlobal())) {
418 RefPtr
<nsFrameLoaderOwner
> flo
= do_QueryObject(embedderElt
);
419 MOZ_DIAGNOSTIC_ASSERT(flo
, "Embedder must be a nsFrameLoaderOwner");
421 // Trgger a process switch into the specified process.
422 IgnoredErrorResult rv
;
423 flo
->ChangeRemotenessWithBridge(bridge
, rv
);
424 if (NS_WARN_IF(rv
.Failed())) {
428 // Everything succeeded, so don't delete the bridge.
429 deleteBridge
.release();
434 mozilla::ipc::IPCResult
WindowGlobalChild::RecvDrawSnapshot(
435 const Maybe
<IntRect
>& aRect
, const float& aScale
,
436 const nscolor
& aBackgroundColor
, const uint32_t& aFlags
,
437 DrawSnapshotResolver
&& aResolve
) {
438 aResolve(gfx::PaintFragment::Record(BrowsingContext(), aRect
, aScale
,
440 (gfx::CrossProcessPaintFlags
)aFlags
));
444 mozilla::ipc::IPCResult
445 WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() {
446 nsCOMPtr
<nsPIDOMWindowInner
> inner
= GetWindowGlobal();
448 inner
->SaveStorageAccessPermissionGranted();
454 mozilla::ipc::IPCResult
WindowGlobalChild::RecvDispatchSecurityPolicyViolation(
455 const nsString
& aViolationEventJSON
) {
456 nsGlobalWindowInner
* window
= GetWindowGlobal();
461 Document
* doc
= window
->GetDocument();
466 SecurityPolicyViolationEventInit violationEvent
;
467 if (!violationEvent
.Init(aViolationEventJSON
)) {
471 RefPtr
<Event
> event
= SecurityPolicyViolationEvent::Constructor(
472 doc
, u
"securitypolicyviolation"_ns
, violationEvent
);
473 event
->SetTrusted(true);
474 doc
->DispatchEvent(*event
, IgnoreErrors());
478 mozilla::ipc::IPCResult
WindowGlobalChild::RecvAddBlockedFrameNodeByClassifier(
479 const MaybeDiscardedBrowsingContext
& aNode
) {
480 if (aNode
.IsNullOrDiscarded()) {
484 nsGlobalWindowInner
* window
= GetWindowGlobal();
489 Document
* doc
= window
->GetDocument();
494 MOZ_ASSERT(aNode
.get()->GetEmbedderElement()->OwnerDoc() == doc
);
495 doc
->AddBlockedNodeByClassifier(aNode
.get()->GetEmbedderElement());
499 mozilla::ipc::IPCResult
WindowGlobalChild::RecvResetScalingZoom() {
500 if (Document
* doc
= mWindowGlobal
->GetExtantDoc()) {
501 if (PresShell
* ps
= doc
->GetPresShell()) {
502 ps
->SetResolutionAndScaleTo(1.0,
503 ResolutionChangeOrigin::MainThreadAdjustment
);
509 mozilla::ipc::IPCResult
WindowGlobalChild::RecvSetContainerFeaturePolicy(
510 dom::FeaturePolicy
* aContainerFeaturePolicy
) {
511 mContainerFeaturePolicy
= aContainerFeaturePolicy
;
515 mozilla::ipc::IPCResult
WindowGlobalChild::RecvRestoreDocShellState(
516 const dom::sessionstore::DocShellRestoreState
& aState
,
517 RestoreDocShellStateResolver
&& aResolve
) {
519 SessionStoreUtils::RestoreDocShellState(mWindowGlobal
->GetDocShell(),
526 mozilla::ipc::IPCResult
WindowGlobalChild::RecvRestoreTabContent(
527 dom::SessionStoreRestoreData
* aData
, RestoreTabContentResolver
&& aResolve
) {
528 aData
->RestoreInto(BrowsingContext());
533 IPCResult
WindowGlobalChild::RecvRawMessage(
534 const JSActorMessageMeta
& aMeta
, const Maybe
<ClonedMessageData
>& aData
,
535 const Maybe
<ClonedMessageData
>& aStack
) {
536 Maybe
<StructuredCloneData
> data
;
539 data
->BorrowFromClonedMessageData(*aData
);
541 Maybe
<StructuredCloneData
> stack
;
544 stack
->BorrowFromClonedMessageData(*aStack
);
546 ReceiveRawMessage(aMeta
, std::move(data
), std::move(stack
));
550 void WindowGlobalChild::SetDocumentURI(nsIURI
* aDocumentURI
) {
551 // Registers a DOM Window with the profiler. It re-registers the same Inner
552 // Window ID with different URIs because when a Browsing context is first
553 // loaded, the first url loaded in it will be about:blank. This call keeps the
554 // first non-about:blank registration of window and discards the previous one.
555 uint64_t embedderInnerWindowID
= 0;
556 if (BrowsingContext()->GetParent()) {
557 embedderInnerWindowID
= BrowsingContext()->GetEmbedderInnerWindowId();
559 profiler_register_page(
560 BrowsingContext()->BrowserId(), InnerWindowId(),
561 nsContentUtils::TruncatedURLForDisplay(aDocumentURI
, 1024),
562 embedderInnerWindowID
, BrowsingContext()->UsePrivateBrowsing());
563 mDocumentURI
= aDocumentURI
;
564 SendUpdateDocumentURI(aDocumentURI
);
567 void WindowGlobalChild::SetDocumentPrincipal(
568 nsIPrincipal
* aNewDocumentPrincipal
,
569 nsIPrincipal
* aNewDocumentStoragePrincipal
) {
570 MOZ_ASSERT(mDocumentPrincipal
->Equals(aNewDocumentPrincipal
));
571 mDocumentPrincipal
= aNewDocumentPrincipal
;
572 SendUpdateDocumentPrincipal(aNewDocumentPrincipal
,
573 aNewDocumentStoragePrincipal
);
576 const nsACString
& WindowGlobalChild::GetRemoteType() {
577 if (XRE_IsContentProcess()) {
578 return ContentChild::GetSingleton()->GetRemoteType();
581 return NOT_REMOTE_TYPE
;
584 already_AddRefed
<JSWindowActorChild
> WindowGlobalChild::GetActor(
585 JSContext
* aCx
, const nsACString
& aName
, ErrorResult
& aRv
) {
586 return JSActorManager::GetActor(aCx
, aName
, aRv
)
587 .downcast
<JSWindowActorChild
>();
590 already_AddRefed
<JSWindowActorChild
> WindowGlobalChild::GetExistingActor(
591 const nsACString
& aName
) {
592 return JSActorManager::GetExistingActor(aName
).downcast
<JSWindowActorChild
>();
595 already_AddRefed
<JSActor
> WindowGlobalChild::InitJSActor(
596 JS::Handle
<JSObject
*> aMaybeActor
, const nsACString
& aName
,
598 RefPtr
<JSWindowActorChild
> actor
;
599 if (aMaybeActor
.get()) {
600 aRv
= UNWRAP_OBJECT(JSWindowActorChild
, aMaybeActor
.get(), actor
);
605 actor
= new JSWindowActorChild();
608 MOZ_RELEASE_ASSERT(!actor
->GetManager(),
609 "mManager was already initialized once!");
610 actor
->Init(aName
, this);
611 return actor
.forget();
614 void WindowGlobalChild::ActorDestroy(ActorDestroyReason aWhy
) {
615 MOZ_ASSERT(nsContentUtils::IsSafeToRunScript(),
616 "Destroying WindowGlobalChild can run script");
618 // If our WindowContext hasn't been marked as discarded yet, ensure it's
619 // marked as discarded at this point.
620 mWindowContext
->Discard();
622 profiler_unregister_page(InnerWindowId());
624 // Destroy our JSActors, and reject any pending queries.
628 bool WindowGlobalChild::IsSameOriginWith(
629 const dom::WindowContext
* aOther
) const {
630 if (aOther
== WindowContext()) {
634 MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aOther
->Group());
635 if (nsGlobalWindowInner
* otherWin
= aOther
->GetInnerWindow()) {
636 return mDocumentPrincipal
->Equals(otherWin
->GetPrincipal());
641 bool WindowGlobalChild::SameOriginWithTop() {
642 return IsSameOriginWith(WindowContext()->TopWindowContext());
645 // For historical context, see:
647 // Bug 13871: Prevent frameset spoofing
648 // Bug 103638: Targets with same name in different windows open in wrong
649 // window with javascript
650 // Bug 408052: Adopt "ancestor" frame navigation policy
651 // Bug 1570207: Refactor logic to rely on BrowsingContextGroups to enforce
652 // origin attribute isolation
653 // Bug 1810619: Crash at null in nsDocShell::ValidateOrigin
654 bool WindowGlobalChild::CanNavigate(dom::BrowsingContext
* aTarget
,
655 bool aConsiderOpener
) {
656 MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aTarget
->Group(),
657 "A WindowGlobalChild should never try to navigate a "
658 "BrowsingContext from another group");
660 auto isFileScheme
= [](nsIPrincipal
* aPrincipal
) -> bool {
661 // NOTE: This code previously checked for a file scheme using
662 // `nsIPrincipal::GetURI()` combined with `NS_GetInnermostURI`. We no longer
663 // use GetURI, as it has been deprecated, and it makes more sense to take
664 // advantage of the pre-computed origin, which will already use the
665 // innermost URI (bug 1810619)
666 nsAutoCString origin
, scheme
;
667 return NS_SUCCEEDED(aPrincipal
->GetOriginNoSuffix(origin
)) &&
668 NS_SUCCEEDED(net_ExtractURLScheme(origin
, scheme
)) &&
672 // A frame can navigate itself and its own root.
673 if (aTarget
== BrowsingContext() || aTarget
== BrowsingContext()->Top()) {
677 // If the target frame doesn't yet have a WindowContext, start checking
678 // principals from its direct ancestor instead. It would inherit its principal
679 // from this document upon creation.
680 dom::WindowContext
* initialWc
= aTarget
->GetCurrentWindowContext();
682 initialWc
= aTarget
->GetParentWindowContext();
685 // A frame can navigate any frame with a same-origin ancestor.
686 bool isFileDocument
= isFileScheme(DocumentPrincipal());
687 for (dom::WindowContext
* wc
= initialWc
; wc
;
688 wc
= wc
->GetParentWindowContext()) {
689 dom::WindowGlobalChild
* wgc
= wc
->GetWindowGlobalChild();
691 continue; // out-of process, so not same-origin.
694 if (DocumentPrincipal()->Equals(wgc
->DocumentPrincipal())) {
698 // Not strictly equal, special case if both are file: URIs.
700 // file: URIs are considered the same domain for the purpose of frame
701 // navigation, regardless of script accessibility (bug 420425).
702 if (isFileDocument
&& isFileScheme(wgc
->DocumentPrincipal())) {
707 // If the target is a top-level document, a frame can navigate it if it can
708 // navigate its opener.
709 if (aConsiderOpener
&& !aTarget
->GetParent()) {
710 if (RefPtr
<dom::BrowsingContext
> opener
= aTarget
->GetOpener()) {
711 return CanNavigate(opener
, false);
718 // FindWithName follows the rules for choosing a browsing context,
719 // with the exception of sandboxing for iframes. The implementation
720 // for arbitrarily choosing between two browsing contexts with the
721 // same name is as follows:
723 // 1) The start browsing context, i.e. 'this'
724 // 2) Descendants in insertion order
726 // 4) Siblings and their children, both in insertion order
727 // 5) After this we iteratively follow the parent chain, repeating 3
729 // 6) If there is no parent, consider all other top level browsing
730 // contexts and their children, both in insertion order
733 // https://html.spec.whatwg.org/multipage/browsers.html#the-rules-for-choosing-a-browsing-context-given-a-browsing-context-name
734 dom::BrowsingContext
* WindowGlobalChild::FindBrowsingContextWithName(
735 const nsAString
& aName
, bool aUseEntryGlobalForAccessCheck
) {
736 RefPtr
<WindowGlobalChild
> requestingContext
= this;
737 if (aUseEntryGlobalForAccessCheck
) {
738 if (nsGlobalWindowInner
* caller
= nsContentUtils::EntryInnerWindow()) {
739 if (caller
->GetBrowsingContextGroup() == WindowContext()->Group()) {
740 requestingContext
= caller
->GetWindowGlobalChild();
742 MOZ_RELEASE_ASSERT(caller
->GetPrincipal()->IsSystemPrincipal(),
743 "caller must be either same-group or system");
747 MOZ_ASSERT(requestingContext
, "must have a requestingContext");
749 dom::BrowsingContext
* found
= nullptr;
750 if (aName
.IsEmpty()) {
751 // You can't find a browsing context with an empty name.
753 } else if (aName
.LowerCaseEqualsLiteral("_blank")) {
754 // Just return null. Caller must handle creating a new window with
757 } else if (nsContentUtils::IsSpecialName(aName
)) {
758 found
= BrowsingContext()->FindWithSpecialName(aName
, *requestingContext
);
759 } else if (dom::BrowsingContext
* child
=
760 BrowsingContext()->FindWithNameInSubtree(aName
,
761 requestingContext
)) {
764 dom::WindowContext
* current
= WindowContext();
767 Span
<RefPtr
<dom::BrowsingContext
>> siblings
;
768 dom::WindowContext
* parent
= current
->GetParentWindowContext();
771 // We've reached the root of the tree, consider browsing
772 // contexts in the same browsing context group.
773 siblings
= WindowContext()->Group()->Toplevels();
774 } else if (dom::BrowsingContext
* bc
= parent
->GetBrowsingContext();
775 bc
&& bc
->NameEquals(aName
) &&
776 requestingContext
->CanNavigate(bc
) && bc
->IsTargetable()) {
780 siblings
= parent
->NonSyntheticChildren();
783 for (dom::BrowsingContext
* sibling
: siblings
) {
784 if (sibling
== current
->GetBrowsingContext()) {
788 if (dom::BrowsingContext
* relative
=
789 sibling
->FindWithNameInSubtree(aName
, requestingContext
)) {
791 // Breaks the outer loop
801 // Helpers should perform access control checks, which means that we
802 // only need to assert that we can access found.
803 MOZ_DIAGNOSTIC_ASSERT(!found
|| requestingContext
->CanNavigate(found
));
808 void WindowGlobalChild::UnblockBFCacheFor(BFCacheStatus aStatus
) {
809 SendUpdateBFCacheStatus(0, aStatus
);
812 void WindowGlobalChild::BlockBFCacheFor(BFCacheStatus aStatus
) {
813 SendUpdateBFCacheStatus(aStatus
, 0);
816 WindowGlobalChild::~WindowGlobalChild() = default;
818 JSObject
* WindowGlobalChild::WrapObject(JSContext
* aCx
,
819 JS::Handle
<JSObject
*> aGivenProto
) {
820 return WindowGlobalChild_Binding::Wrap(aCx
, this, aGivenProto
);
823 nsISupports
* WindowGlobalChild::GetParentObject() {
824 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
827 NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE_WEAK_PTR(WindowGlobalChild
, mWindowGlobal
,
828 mContainerFeaturePolicy
,
831 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild
)
832 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
833 NS_INTERFACE_MAP_ENTRY(nsISupports
)
836 NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild
)
837 NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild
)
839 } // namespace mozilla::dom