no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / ipc / WindowGlobalChild.cpp
blob40728289119e54975445a713496fcf5c2fc07469
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,
54 nsIURI* aDocumentURI)
55 : mWindowContext(aWindowContext),
56 mDocumentPrincipal(aPrincipal),
57 mDocumentURI(aDocumentURI) {
58 MOZ_DIAGNOSTIC_ASSERT(mWindowContext);
59 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal);
61 if (!mDocumentURI) {
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;
89 if (httpChan &&
90 loadInfo->GetExternalContentPolicyType() ==
91 ExtContentPolicy::TYPE_DOCUMENT &&
92 NS_SUCCEEDED(httpChan->GetCrossOriginOpenerPolicy(&policy))) {
93 MOZ_DIAGNOSTIC_ASSERT(policy ==
94 aWindow->GetBrowsingContext()->GetOpenerPolicy());
96 #endif
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) {
106 return nullptr;
109 ManagedEndpoint<PWindowGlobalParent> endpoint =
110 ipChild->OpenPWindowGlobalEndpoint(wgc);
111 ipParent->BindPWindowGlobalEndpoint(std::move(endpoint),
112 wgc->WindowContext()->Canonical());
113 } else {
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();
120 #endif
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);
130 wgc->Init();
131 wgc->InitWindowGlobal(aWindow);
132 return wgc.forget();
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);
147 } else {
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())
197 ->Serialize(csArgs);
198 if (!SendUpdateCookieJarSettings(csArgs)) {
199 NS_WARNING(
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);
229 } else {
230 txn.SetIsOriginalFrameSource(false);
233 // Init Mixed Content Fields
234 nsCOMPtr<nsIURI> innerDocURI =
235 NS_GetInnermostURI(aDocument->GetDocumentURI());
236 if (innerDocURI) {
237 txn.SetIsSecure(innerDocURI->SchemeIs("https"));
240 MOZ_DIAGNOSTIC_ASSERT(mDocumentPrincipal->GetIsLocalIpAddress() ==
241 mWindowContext->IsLocalIP());
243 MOZ_ALWAYS_SUCCEEDS(txn.Commit(mWindowContext));
246 /* static */
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());
253 return nullptr;
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() {
273 if (!CanSend()) {
274 return nullptr;
276 IProtocol* otherSide = InProcessChild::ParentActorFor(this);
277 return do_AddRef(static_cast<WindowGlobalParent*>(otherSide));
280 already_AddRefed<BrowserChild> WindowGlobalChild::GetBrowserChild() {
281 if (IsInProcess() || !CanSend()) {
282 return nullptr;
284 return do_AddRef(static_cast<BrowserChild*>(Manager()));
287 uint64_t WindowGlobalChild::ContentParentId() {
288 if (XRE_IsParentProcess()) {
289 return 0;
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()) {
298 return true;
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
330 // us as well.
331 RefPtr<BrowserChild> browserChild = GetBrowserChild();
332 if (!browserChild || !browserChild->IsDestroyed()) {
333 SendDestroy();
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())) {
346 return IPC_OK();
348 dom::BrowsingContext* frameContext = aFrameContext.get();
350 RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
351 if (NS_WARN_IF(!embedderElt)) {
352 return IPC_OK();
355 if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
356 return IPC_OK();
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());
368 return IPC_OK();
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
385 // at that point.
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();
401 if (NS_WARN_IF(
402 !manager->BindPBrowserBridgeEndpoint(std::move(aEndpoint), bridge))) {
403 return IPC_OK();
406 // Synchronously delete de actor here rather than using SendBeginDestroy(), as
407 // we haven't initialized it yet.
408 auto deleteBridge =
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())) {
413 return IPC_OK();
416 RefPtr<Element> embedderElt = frameContext->GetEmbedderElement();
417 if (NS_WARN_IF(!embedderElt)) {
418 return IPC_OK();
421 if (NS_WARN_IF(embedderElt->GetOwnerGlobal() != GetWindowGlobal())) {
422 return IPC_OK();
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())) {
432 return IPC_OK();
435 // Everything succeeded, so don't delete the bridge.
436 deleteBridge.release();
438 return IPC_OK();
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,
446 aBackgroundColor,
447 (gfx::CrossProcessPaintFlags)aFlags));
448 return IPC_OK();
451 mozilla::ipc::IPCResult
452 WindowGlobalChild::RecvSaveStorageAccessPermissionGranted() {
453 nsCOMPtr<nsPIDOMWindowInner> inner = GetWindowGlobal();
454 if (inner) {
455 inner->SaveStorageAccessPermissionGranted();
458 return IPC_OK();
461 mozilla::ipc::IPCResult WindowGlobalChild::RecvDispatchSecurityPolicyViolation(
462 const nsString& aViolationEventJSON) {
463 nsGlobalWindowInner* window = GetWindowGlobal();
464 if (!window) {
465 return IPC_OK();
468 Document* doc = window->GetDocument();
469 if (!doc) {
470 return IPC_OK();
473 SecurityPolicyViolationEventInit violationEvent;
474 if (!violationEvent.Init(aViolationEventJSON)) {
475 return IPC_OK();
478 RefPtr<Event> event = SecurityPolicyViolationEvent::Constructor(
479 doc, u"securitypolicyviolation"_ns, violationEvent);
480 event->SetTrusted(true);
481 doc->DispatchEvent(*event, IgnoreErrors());
482 return IPC_OK();
485 mozilla::ipc::IPCResult WindowGlobalChild::RecvAddBlockedFrameNodeByClassifier(
486 const MaybeDiscardedBrowsingContext& aNode) {
487 if (aNode.IsNullOrDiscarded()) {
488 return IPC_OK();
491 nsGlobalWindowInner* window = GetWindowGlobal();
492 if (!window) {
493 return IPC_OK();
496 Document* doc = window->GetDocument();
497 if (!doc) {
498 return IPC_OK();
501 MOZ_ASSERT(aNode.get()->GetEmbedderElement()->OwnerDoc() == doc);
502 doc->AddBlockedNodeByClassifier(aNode.get()->GetEmbedderElement());
503 return IPC_OK();
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);
513 return IPC_OK();
516 mozilla::ipc::IPCResult WindowGlobalChild::RecvSetContainerFeaturePolicy(
517 dom::FeaturePolicy* aContainerFeaturePolicy) {
518 mContainerFeaturePolicy = aContainerFeaturePolicy;
519 return IPC_OK();
522 mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreDocShellState(
523 const dom::sessionstore::DocShellRestoreState& aState,
524 RestoreDocShellStateResolver&& aResolve) {
525 if (mWindowGlobal) {
526 SessionStoreUtils::RestoreDocShellState(mWindowGlobal->GetDocShell(),
527 aState);
529 aResolve(true);
530 return IPC_OK();
533 mozilla::ipc::IPCResult WindowGlobalChild::RecvRestoreTabContent(
534 dom::SessionStoreRestoreData* aData, RestoreTabContentResolver&& aResolve) {
535 aData->RestoreInto(BrowsingContext());
536 aResolve(true);
537 return IPC_OK();
540 IPCResult WindowGlobalChild::RecvRawMessage(
541 const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
542 const Maybe<ClonedMessageData>& aStack) {
543 Maybe<StructuredCloneData> data;
544 if (aData) {
545 data.emplace();
546 data->BorrowFromClonedMessageData(*aData);
548 Maybe<StructuredCloneData> stack;
549 if (aStack) {
550 stack.emplace();
551 stack->BorrowFromClonedMessageData(*aStack);
553 ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
554 return IPC_OK();
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();
573 return IPC_OK();
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();
595 if (precursor) {
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,
640 ErrorResult& aRv) {
641 RefPtr<JSWindowActorChild> actor;
642 if (aMaybeActor.get()) {
643 aRv = UNWRAP_OBJECT(JSWindowActorChild, aMaybeActor.get(), actor);
644 if (aRv.Failed()) {
645 return nullptr;
647 } else {
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.
668 JSActorDidDestroy();
671 bool WindowGlobalChild::IsSameOriginWith(
672 const dom::WindowContext* aOther) const {
673 if (aOther == WindowContext()) {
674 return true;
677 MOZ_DIAGNOSTIC_ASSERT(WindowContext()->Group() == aOther->Group());
678 if (nsGlobalWindowInner* otherWin = aOther->GetInnerWindow()) {
679 return mDocumentPrincipal->Equals(otherWin->GetPrincipal());
681 return false;
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)) &&
712 scheme == "file"_ns;
715 // A frame can navigate itself and its own root.
716 if (aTarget == BrowsingContext() || aTarget == BrowsingContext()->Top()) {
717 return true;
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();
724 if (!initialWc) {
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();
733 if (!wgc) {
734 continue; // out-of process, so not same-origin.
737 if (DocumentPrincipal()->Equals(wgc->DocumentPrincipal())) {
738 return true;
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())) {
746 return true;
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);
758 return 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
768 // 3) The parent
769 // 4) Siblings and their children, both in insertion order
770 // 5) After this we iteratively follow the parent chain, repeating 3
771 // and 4 until
772 // 6) If there is no parent, consider all other top level browsing
773 // contexts and their children, both in insertion order
775 // See
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();
784 } else {
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.
795 found = nullptr;
796 } else if (aName.LowerCaseEqualsLiteral("_blank")) {
797 // Just return null. Caller must handle creating a new window with
798 // a blank name.
799 found = nullptr;
800 } else if (nsContentUtils::IsSpecialName(aName)) {
801 found = BrowsingContext()->FindWithSpecialName(aName, *requestingContext);
802 } else if (dom::BrowsingContext* child =
803 BrowsingContext()->FindWithNameInSubtree(aName,
804 requestingContext)) {
805 found = child;
806 } else {
807 dom::WindowContext* current = WindowContext();
809 do {
810 Span<RefPtr<dom::BrowsingContext>> siblings;
811 dom::WindowContext* parent = current->GetParentWindowContext();
813 if (!parent) {
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()) {
820 found = bc;
821 break;
822 } else {
823 siblings = parent->NonSyntheticChildren();
826 for (dom::BrowsingContext* sibling : siblings) {
827 if (sibling == current->GetBrowsingContext()) {
828 continue;
831 if (dom::BrowsingContext* relative =
832 sibling->FindWithNameInSubtree(aName, requestingContext)) {
833 found = relative;
834 // Breaks the outer loop
835 parent = nullptr;
836 break;
840 current = parent;
841 } while (current);
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));
848 return 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,
872 mWindowContext)
874 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalChild)
875 NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
876 NS_INTERFACE_MAP_ENTRY(nsISupports)
877 NS_INTERFACE_MAP_END
879 NS_IMPL_CYCLE_COLLECTING_ADDREF(WindowGlobalChild)
880 NS_IMPL_CYCLE_COLLECTING_RELEASE(WindowGlobalChild)
882 } // namespace mozilla::dom