Bug 1824634 disconnect from GtkIMContext OnDestroyWindow r=masayuki
[gecko.git] / dom / ipc / WindowGlobalChild.cpp
blobf400bef2b42da6d5b8ac7e432264f5a8b4910ff1
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,
53 nsIURI* aDocumentURI)
54 : mWindowContext(aWindowContext),
55 mDocumentPrincipal(aPrincipal),
56 mDocumentURI(aDocumentURI) {
57 MOZ_DIAGNOSTIC_ASSERT(mWindowContext);
58 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal);
60 if (!mDocumentURI) {
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;
88 if (httpChan &&
89 loadInfo->GetExternalContentPolicyType() ==
90 ExtContentPolicy::TYPE_DOCUMENT &&
91 NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy))) {
92 MOZ_DIAGNOSTIC_ASSERT(policy ==
93 aWindow->GetBrowsingContext()->GetOpenerPolicy());
95 #endif
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) {
105 return nullptr;
108 ManagedEndpoint<PWindowGlobalParent> endpoint =
109 ipChild->OpenPWindowGlobalEndpoint(wgc);
110 ipParent->BindPWindowGlobalEndpoint(std::move(endpoint),
111 wgc->WindowContext()->Canonical());
112 } else {
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();
119 #endif
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);
129 wgc->Init();
130 wgc->InitWindowGlobal(aWindow);
131 return wgc.forget();
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);
146 } else {
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())
196 ->Serialize(csArgs);
197 if (!SendUpdateCookieJarSettings(csArgs)) {
198 NS_WARNING(
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());
222 } else {
223 txn.SetIsOriginalFrameSource(false);
226 // Init Mixed Content Fields
227 nsCOMPtr<nsIURI> innerDocURI =
228 NS_GetInnermostURI(aDocument->GetDocumentURI());
229 if (innerDocURI) {
230 txn.SetIsSecure(innerDocURI->SchemeIs("https"));
233 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
234 mWindowContext->IsLocalIP());
236 MOZ_ALWAYS_SUCCEEDS(txn.Commit(mWindowContext));
239 /* static */
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());
246 return nullptr;
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() {
266 if (!CanSend()) {
267 return nullptr;
269 IProtocol* otherSide = InProcessChild::ParentActorFor(this);
270 return do_AddRef(static_cast<WindowGlobalParent*>(otherSide));
273 already_AddRefed<BrowserChild> WindowGlobalChild::GetBrowserChild() {
274 if (IsInProcess() || !CanSend()) {
275 return nullptr;
277 return do_AddRef(static_cast<BrowserChild*>(Manager()));
280 uint64_t WindowGlobalChild::ContentParentId() {
281 if (XRE_IsParentProcess()) {
282 return 0;
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()) {
291 return true;
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
323 // us as well.
324 RefPtr<BrowserChild> browserChild = GetBrowserChild();
325 if (!browserChild || !browserChild->IsDestroyed()) {
326 SendDestroy();
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())) {
339 return IPC_OK();
341 dom::BrowsingContext* frameContext = aFrameContext.get();
343 RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
344 if (NS_WARN_IF(!embedderElt)) {
345 return IPC_OK();
348 if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
349 return IPC_OK();
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());
361 return IPC_OK();
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
378 // at that point.
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();
394 if (NS_WARN_IF(
395 !manager->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge))) {
396 return IPC_OK();
399 // Synchronously delete de actor here rather than using SendBeginDestroy(), as
400 // we haven't initialized it yet.
401 auto deleteBridge =
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())) {
406 return IPC_OK();
409 RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
410 if (NS_WARN_IF(!embedderElt)) {
411 return IPC_OK();
414 if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
415 return IPC_OK();
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())) {
425 return IPC_OK();
428 // Everything succeeded, so don't delete the bridge.
429 deleteBridge.release();
431 return IPC_OK();
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,
439 aBackgroundColor,
440 (gfx::CrossProcessPaintFlags)aFlags));
441 return IPC_OK();
444 mozilla::ipc::IPCResult
445 WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() {
446 nsCOMPtr<nsPIDOMWindowInner> inner = GetWindowGlobal();
447 if (inner) {
448 inner->SaveStorageAccessPermissionGranted();
451 return IPC_OK();
454 mozilla::ipc::IPCResult WindowGlobalChild::RecvDispatchSecurityPolicyViolation(
455 const nsString& aViolationEventJSON) {
456 nsGlobalWindowInner* window = GetWindowGlobal();
457 if (!window) {
458 return IPC_OK();
461 Document* doc = window->GetDocument();
462 if (!doc) {
463 return IPC_OK();
466 SecurityPolicyViolationEventInit violationEvent;
467 if (!violationEvent.Init(aViolationEventJSON)) {
468 return IPC_OK();
471 RefPtr<Event> event = SecurityPolicyViolationEvent::Constructor(
472 doc, u"securitypolicyviolation"_ns, violationEvent);
473 event->SetTrusted(true);
474 doc->DispatchEvent(*event, IgnoreErrors());
475 return IPC_OK();
478 mozilla::ipc::IPCResult WindowGlobalChild::RecvAddBlockedFrameNodeByClassifier(
479 const MaybeDiscardedBrowsingContext& aNode) {
480 if (aNode.IsNullOrDiscarded()) {
481 return IPC_OK();
484 nsGlobalWindowInner* window = GetWindowGlobal();
485 if (!window) {
486 return IPC_OK();
489 Document* doc = window->GetDocument();
490 if (!doc) {
491 return IPC_OK();
494 MOZ_ASSERT(aNode.get()->GetEmbedderElement()->OwnerDoc() == doc);
495 doc->AddBlockedNodeByClassifier(aNode.get()->GetEmbedderElement());
496 return IPC_OK();
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);
506 return IPC_OK();
509 mozilla::ipc::IPCResult WindowGlobalChild::RecvSetContainerFeaturePolicy(
510 dom::FeaturePolicy* aContainerFeaturePolicy) {
511 mContainerFeaturePolicy = aContainerFeaturePolicy;
512 return IPC_OK();
515 mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreDocShellState(
516 const dom::sessionstore::DocShellRestoreState& aState,
517 RestoreDocShellStateResolver&& aResolve) {
518 if (mWindowGlobal) {
519 SessionStoreUtils::RestoreDocShellState(mWindowGlobal->GetDocShell(),
520 aState);
522 aResolve(true);
523 return IPC_OK();
526 mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreTabContent(
527 dom::SessionStoreRestoreData* aData, RestoreTabContentResolver&& aResolve) {
528 aData->RestoreInto(BrowsingContext());
529 aResolve(true);
530 return IPC_OK();
533 IPCResult WindowGlobalChild::RecvRawMessage(
534 const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
535 const Maybe<ClonedMessageData>& aStack) {
536 Maybe<StructuredCloneData> data;
537 if (aData) {
538 data.emplace();
539 data->BorrowFromClonedMessageData(*aData);
541 Maybe<StructuredCloneData> stack;
542 if (aStack) {
543 stack.emplace();
544 stack->BorrowFromClonedMessageData(*aStack);
546 ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
547 return IPC_OK();
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,
597 ErrorResult& aRv) {
598 RefPtr<JSWindowActorChild> actor;
599 if (aMaybeActor.get()) {
600 aRv = UNWRAP_OBJECT(JSWindowActorChild, aMaybeActor.get(), actor);
601 if (aRv.Failed()) {
602 return nullptr;
604 } else {
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.
625 JSActorDidDestroy();
628 bool WindowGlobalChild::IsSameOriginWith(
629 const dom::WindowContext* aOther) const {
630 if (aOther == WindowContext()) {
631 return true;
634 MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aOther->Group());
635 if (nsGlobalWindowInner* otherWin = aOther->GetInnerWindow()) {
636 return mDocumentPrincipal->Equals(otherWin->GetPrincipal());
638 return false;
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)) &&
669 scheme == "file"_ns;
672 // A frame can navigate itself and its own root.
673 if (aTarget == BrowsingContext() || aTarget == BrowsingContext()->Top()) {
674 return true;
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();
681 if (!initialWc) {
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();
690 if (!wgc) {
691 continue; // out-of process, so not same-origin.
694 if (DocumentPrincipal()->Equals(wgc->DocumentPrincipal())) {
695 return true;
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())) {
703 return true;
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);
715 return 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
725 // 3) The parent
726 // 4) Siblings and their children, both in insertion order
727 // 5) After this we iteratively follow the parent chain, repeating 3
728 // and 4 until
729 // 6) If there is no parent, consider all other top level browsing
730 // contexts and their children, both in insertion order
732 // See
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();
741 } else {
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.
752 found = nullptr;
753 } else if (aName.LowerCaseEqualsLiteral("_blank")) {
754 // Just return null. Caller must handle creating a new window with
755 // a blank name.
756 found = nullptr;
757 } else if (nsContentUtils::IsSpecialName(aName)) {
758 found = BrowsingContext()->FindWithSpecialName(aName, *requestingContext);
759 } else if (dom::BrowsingContext* child =
760 BrowsingContext()->FindWithNameInSubtree(aName,
761 requestingContext)) {
762 found = child;
763 } else {
764 dom::WindowContext* current = WindowContext();
766 do {
767 Span<RefPtr<dom::BrowsingContext>> siblings;
768 dom::WindowContext* parent = current->GetParentWindowContext();
770 if (!parent) {
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()) {
777 found = bc;
778 break;
779 } else {
780 siblings = parent->NonSyntheticChildren();
783 for (dom::BrowsingContext* sibling : siblings) {
784 if (sibling == current->GetBrowsingContext()) {
785 continue;
788 if (dom::BrowsingContext* relative =
789 sibling->FindWithNameInSubtree(aName, requestingContext)) {
790 found = relative;
791 // Breaks the outer loop
792 parent = nullptr;
793 break;
797 current = parent;
798 } while (current);
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));
805 return 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,
829 mWindowContext)
831 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild)
832 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
833 NS_INTERFACE_MAP_ENTRY(nsISupports)
834 NS_INTERFACE_MAP_END
836 NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild)
837 NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild)
839 } // namespace mozilla::dom