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/WindowGlobalParent.h"
11 #include "mozilla/AntiTrackingUtils.h"
12 #include "mozilla/AsyncEventDispatcher.h"
13 #include "mozilla/ClearOnShutdown.h"
14 #include "mozilla/ContentBlockingAllowList.h"
15 #include "mozilla/dom/InProcessParent.h"
16 #include "mozilla/dom/BrowserBridgeParent.h"
17 #include "mozilla/dom/BrowsingContextGroup.h"
18 #include "mozilla/dom/CanonicalBrowsingContext.h"
19 #include "mozilla/dom/ClientInfo.h"
20 #include "mozilla/dom/ClientIPCTypes.h"
21 #include "mozilla/dom/ContentChild.h"
22 #include "mozilla/dom/ContentParent.h"
23 #include "mozilla/dom/BrowserHost.h"
24 #include "mozilla/dom/BrowserParent.h"
25 #include "mozilla/dom/IdentityCredential.h"
26 #include "mozilla/dom/MediaController.h"
27 #include "mozilla/dom/WindowGlobalChild.h"
28 #include "mozilla/dom/ChromeUtils.h"
29 #include "mozilla/dom/UseCounterMetrics.h"
30 #include "mozilla/dom/ipc/IdType.h"
31 #include "mozilla/dom/ipc/StructuredCloneData.h"
32 #include "mozilla/glean/GleanMetrics.h"
33 #include "mozilla/Components.h"
34 #include "mozilla/ScopeExit.h"
35 #include "mozilla/ServoCSSParser.h"
36 #include "mozilla/ServoStyleSet.h"
37 #include "mozilla/StaticPrefs_dom.h"
38 #include "mozilla/StaticPrefs_network.h"
39 #include "mozilla/Telemetry.h"
40 #include "mozilla/Variant.h"
41 #include "mozilla/ipc/ProtocolUtils.h"
42 #include "MMPrinter.h"
43 #include "nsContentUtils.h"
44 #include "nsDocShell.h"
45 #include "nsDocShellLoadState.h"
47 #include "nsFrameLoader.h"
48 #include "nsFrameLoaderOwner.h"
49 #include "nsICookieManager.h"
50 #include "nsICookieService.h"
51 #include "nsQueryObject.h"
52 #include "nsNetUtil.h"
53 #include "nsSandboxFlags.h"
54 #include "nsSerializationHelper.h"
55 #include "nsIBrowser.h"
56 #include "nsIEffectiveTLDService.h"
57 #include "nsIHttpsOnlyModePermission.h"
58 #include "nsIPromptCollection.h"
60 #include "nsITransportSecurityInfo.h"
61 #include "nsISharePicker.h"
62 #include "nsIURIMutator.h"
63 #include "nsIWebProgressListener.h"
64 #include "nsScriptSecurityManager.h"
65 #include "nsIOService.h"
67 #include "mozilla/dom/DOMException.h"
68 #include "mozilla/dom/DOMExceptionBinding.h"
70 #include "mozilla/dom/JSActorService.h"
71 #include "mozilla/dom/JSWindowActorBinding.h"
72 #include "mozilla/dom/JSWindowActorParent.h"
74 #include "mozilla/net/NeckoParent.h"
75 #include "mozilla/net/PCookieServiceParent.h"
76 #include "mozilla/net/CookieServiceParent.h"
78 #include "SessionStoreFunctions.h"
79 #include "nsIXPConnect.h"
80 #include "nsImportModule.h"
81 #include "nsIXULRuntime.h"
83 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
85 using namespace mozilla::ipc
;
86 using namespace mozilla::dom::ipc
;
88 extern mozilla::LazyLogModule gSHIPBFCacheLog
;
89 extern mozilla::LazyLogModule gUseCountersLog
;
91 namespace mozilla::dom
{
93 WindowGlobalParent::WindowGlobalParent(
94 CanonicalBrowsingContext
* aBrowsingContext
, uint64_t aInnerWindowId
,
95 uint64_t aOuterWindowId
, FieldValues
&& aInit
)
96 : WindowContext(aBrowsingContext
, aInnerWindowId
, aOuterWindowId
,
99 mDocumentHasLoaded(false),
100 mDocumentHasUserInteracted(false),
101 mBlockAllMixedContent(false),
102 mUpgradeInsecureRequests(false) {
103 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only");
106 already_AddRefed
<WindowGlobalParent
> WindowGlobalParent::CreateDisconnected(
107 const WindowGlobalInit
& aInit
) {
108 RefPtr
<CanonicalBrowsingContext
> browsingContext
=
109 CanonicalBrowsingContext::Get(aInit
.context().mBrowsingContextId
);
110 if (NS_WARN_IF(!browsingContext
)) {
114 RefPtr
<WindowGlobalParent
> wgp
=
115 GetByInnerWindowId(aInit
.context().mInnerWindowId
);
116 MOZ_RELEASE_ASSERT(!wgp
, "Creating duplicate WindowGlobalParent");
118 FieldValues
fields(aInit
.context().mFields
);
120 new WindowGlobalParent(browsingContext
, aInit
.context().mInnerWindowId
,
121 aInit
.context().mOuterWindowId
, std::move(fields
));
122 wgp
->mDocumentPrincipal
= aInit
.principal();
123 wgp
->mDocumentURI
= aInit
.documentURI();
124 wgp
->mIsInitialDocument
= Some(aInit
.isInitialDocument());
125 wgp
->mBlockAllMixedContent
= aInit
.blockAllMixedContent();
126 wgp
->mUpgradeInsecureRequests
= aInit
.upgradeInsecureRequests();
127 wgp
->mSandboxFlags
= aInit
.sandboxFlags();
128 wgp
->mHttpsOnlyStatus
= aInit
.httpsOnlyStatus();
129 wgp
->mSecurityInfo
= aInit
.securityInfo();
130 net::CookieJarSettings::Deserialize(aInit
.cookieJarSettings(),
131 getter_AddRefs(wgp
->mCookieJarSettings
));
132 MOZ_RELEASE_ASSERT(wgp
->mDocumentPrincipal
, "Must have a valid principal");
134 nsresult rv
= wgp
->SetDocumentStoragePrincipal(aInit
.storagePrincipal());
135 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv
),
136 "Must succeed in setting storage principal");
141 void WindowGlobalParent::Init() {
142 MOZ_ASSERT(Manager(), "Should have a manager!");
144 // Invoke our base class' `Init` method. This will register us in
145 // `gWindowContexts`.
146 WindowContext::Init();
148 // Determine which content process the window global is coming from.
149 dom::ContentParentId
processId(0);
150 ContentParent
* cp
= nullptr;
151 if (!IsInProcess()) {
152 cp
= static_cast<ContentParent
*>(Manager()->Manager());
153 processId
= cp
->ChildID();
155 // Ensure the content process has permissions for this principal.
156 cp
->TransmitPermissionsForPrincipal(mDocumentPrincipal
);
159 MOZ_DIAGNOSTIC_ASSERT(
160 !BrowsingContext()->GetParent() ||
161 BrowsingContext()->GetEmbedderInnerWindowId(),
162 "When creating a non-root WindowGlobalParent, the WindowGlobalParent "
163 "for our embedder should've already been created.");
165 // Ensure we have a document URI
167 NS_NewURI(getter_AddRefs(mDocumentURI
), "about:blank");
170 // NOTE: `cp` may be nullptr, but that's OK, we need to tell every other
171 // process in our group in that case.
172 IPCInitializer ipcinit
= GetIPCInitializer();
173 Group()->EachOtherParent(cp
, [&](ContentParent
* otherContent
) {
174 Unused
<< otherContent
->SendCreateWindowContext(ipcinit
);
177 if (!BrowsingContext()->IsDiscarded()) {
179 BrowsingContext()->SetCurrentInnerWindowId(InnerWindowId()));
181 Unused
<< SendSetContainerFeaturePolicy(
182 BrowsingContext()->GetContainerFeaturePolicy());
185 if (BrowsingContext()->IsTopContent()) {
186 // For top level sandboxed documents we need to create a new principal
187 // from URI + OriginAttributes, since the document principal will be a
188 // NullPrincipal. See Bug 1654546.
189 if (mSandboxFlags
& SANDBOXED_ORIGIN
) {
190 ContentBlockingAllowList::RecomputePrincipal(
191 mDocumentURI
, mDocumentPrincipal
->OriginAttributesRef(),
192 getter_AddRefs(mDocContentBlockingAllowListPrincipal
));
194 ContentBlockingAllowList::ComputePrincipal(
196 getter_AddRefs(mDocContentBlockingAllowListPrincipal
));
200 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
202 obs
->NotifyObservers(ToSupports(this), "window-global-created", nullptr);
205 if (!BrowsingContext()->IsDiscarded() && ShouldTrackSiteOriginTelemetry()) {
206 mOriginCounter
.emplace();
207 mOriginCounter
->UpdateSiteOriginsFrom(this, /* aIncrease = */ true);
211 void WindowGlobalParent::OriginCounter::UpdateSiteOriginsFrom(
212 WindowGlobalParent
* aParent
, bool aIncrease
) {
213 MOZ_RELEASE_ASSERT(aParent
);
215 if (aParent
->DocumentPrincipal()->GetIsContentPrincipal()) {
216 nsAutoCString origin
;
217 aParent
->DocumentPrincipal()->GetSiteOrigin(origin
);
220 int32_t& count
= mOriginMap
.LookupOrInsert(origin
);
222 mMaxOrigins
= std::max(mMaxOrigins
, mOriginMap
.Count());
223 } else if (auto entry
= mOriginMap
.Lookup(origin
)) {
226 if (entry
.Data() == 0) {
233 void WindowGlobalParent::OriginCounter::Accumulate() {
234 mozilla::glean::geckoview::per_document_site_origins
.AccumulateSingleSample(
242 already_AddRefed
<WindowGlobalParent
> WindowGlobalParent::GetByInnerWindowId(
243 uint64_t aInnerWindowId
) {
244 if (!XRE_IsParentProcess()) {
248 return WindowContext::GetById(aInnerWindowId
).downcast
<WindowGlobalParent
>();
251 already_AddRefed
<WindowGlobalChild
> WindowGlobalParent::GetChildActor() {
255 IProtocol
* otherSide
= InProcessParent::ChildActorFor(this);
256 return do_AddRef(static_cast<WindowGlobalChild
*>(otherSide
));
259 BrowserParent
* WindowGlobalParent::GetBrowserParent() {
260 if (IsInProcess() || !CanSend()) {
263 return static_cast<BrowserParent
*>(Manager());
266 ContentParent
* WindowGlobalParent::GetContentParent() {
267 if (IsInProcess() || !CanSend()) {
270 return static_cast<ContentParent
*>(Manager()->Manager());
273 already_AddRefed
<nsFrameLoader
> WindowGlobalParent::GetRootFrameLoader() {
274 dom::BrowsingContext
* top
= BrowsingContext()->Top();
276 RefPtr
<nsFrameLoaderOwner
> frameLoaderOwner
=
277 do_QueryObject(top
->GetEmbedderElement());
278 if (frameLoaderOwner
) {
279 return frameLoaderOwner
->GetFrameLoader();
284 uint64_t WindowGlobalParent::ContentParentId() {
285 RefPtr
<BrowserParent
> browserParent
= GetBrowserParent();
286 return browserParent
? browserParent
->Manager()->ChildID() : 0;
289 int32_t WindowGlobalParent::OsPid() {
290 RefPtr
<BrowserParent
> browserParent
= GetBrowserParent();
291 return browserParent
? browserParent
->Manager()->Pid() : -1;
294 // A WindowGlobalPaernt is the root in its process if it has no parent, or its
295 // embedder is in a different process.
296 bool WindowGlobalParent::IsProcessRoot() {
297 if (!BrowsingContext()->GetParent()) {
301 RefPtr
<WindowGlobalParent
> embedder
=
302 BrowsingContext()->GetEmbedderWindowGlobal();
303 if (NS_WARN_IF(!embedder
)) {
307 return ContentParentId() != embedder
->ContentParentId();
310 uint32_t WindowGlobalParent::ContentBlockingEvents() {
311 return GetContentBlockingLog()->GetContentBlockingEventsInLog();
314 void WindowGlobalParent::GetContentBlockingLog(nsAString
& aLog
) {
315 NS_ConvertUTF8toUTF16
log(GetContentBlockingLog()->Stringify());
316 aLog
.Assign(std::move(log
));
319 mozilla::ipc::IPCResult
WindowGlobalParent::RecvLoadURI(
320 const MaybeDiscarded
<dom::BrowsingContext
>& aTargetBC
,
321 nsDocShellLoadState
* aLoadState
, bool aSetNavigating
) {
322 if (aTargetBC
.IsNullOrDiscarded()) {
324 BrowsingContext::GetLog(), LogLevel::Debug
,
325 ("ParentIPC: Trying to send a message with dead or detached context"));
329 if (net::SchemeIsJavascript(aLoadState
->URI())) {
330 return IPC_FAIL(this, "Illegal cross-process javascript: load attempt");
333 RefPtr
<CanonicalBrowsingContext
> targetBC
= aTargetBC
.get_canonical();
335 // FIXME: For cross-process loads, we should double check CanAccess() for the
336 // source browsing context in the parent process.
338 if (targetBC
->Group() != BrowsingContext()->Group()) {
339 return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
342 // FIXME: We should really initiate the load in the parent before bouncing
343 // back down to the child.
345 targetBC
->LoadURI(aLoadState
, aSetNavigating
);
349 mozilla::ipc::IPCResult
WindowGlobalParent::RecvInternalLoad(
350 nsDocShellLoadState
* aLoadState
) {
351 if (!aLoadState
->Target().IsEmpty() ||
352 aLoadState
->TargetBrowsingContext().IsNull()) {
353 return IPC_FAIL(this, "must already be retargeted");
355 if (aLoadState
->TargetBrowsingContext().IsDiscarded()) {
357 BrowsingContext::GetLog(), LogLevel::Debug
,
358 ("ParentIPC: Trying to send a message with dead or detached context"));
362 if (net::SchemeIsJavascript(aLoadState
->URI())) {
363 return IPC_FAIL(this, "Illegal cross-process javascript: load attempt");
366 RefPtr
<CanonicalBrowsingContext
> targetBC
=
367 aLoadState
->TargetBrowsingContext().get_canonical();
369 // FIXME: For cross-process loads, we should double check CanAccess() for the
370 // source browsing context in the parent process.
372 if (targetBC
->Group() != BrowsingContext()->Group()) {
373 return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
376 // FIXME: We should really initiate the load in the parent before bouncing
377 // back down to the child.
379 targetBC
->InternalLoad(aLoadState
);
383 IPCResult
WindowGlobalParent::RecvUpdateDocumentURI(NotNull
<nsIURI
*> aURI
) {
384 // XXX(nika): Assert that the URI change was one which makes sense (either
385 // about:blank -> a real URI, or a legal push/popstate URI change):
386 if (StaticPrefs::dom_security_setdocumenturi()) {
387 nsAutoCString scheme
;
388 if (NS_FAILED(aURI
->GetScheme(scheme
))) {
389 return IPC_FAIL(this, "Setting DocumentURI without scheme.");
392 nsCOMPtr
<nsIIOService
> ios
= do_GetIOService();
394 return IPC_FAIL(this, "Cannot get IOService");
396 nsCOMPtr
<nsIProtocolHandler
> handler
;
397 ios
->GetProtocolHandler(scheme
.get(), getter_AddRefs(handler
));
399 return IPC_FAIL(this, "Setting DocumentURI with unknown protocol.");
402 auto isLoadableViaInternet
= [](nsIURI
* uri
) {
403 return (uri
&& (net::SchemeIsHTTP(uri
) || net::SchemeIsHTTPS(uri
)));
406 if (isLoadableViaInternet(aURI
)) {
407 nsCOMPtr
<nsIURI
> principalURI
= mDocumentPrincipal
->GetURI();
408 if (mDocumentPrincipal
->GetIsNullPrincipal()) {
409 nsCOMPtr
<nsIPrincipal
> precursor
=
410 mDocumentPrincipal
->GetPrecursorPrincipal();
412 principalURI
= precursor
->GetURI();
416 if (isLoadableViaInternet(principalURI
) &&
417 !nsScriptSecurityManager::SecurityCompareURIs(principalURI
, aURI
)) {
418 return IPC_FAIL(this,
419 "Setting DocumentURI with a different Origin than "
429 nsresult
WindowGlobalParent::SetDocumentStoragePrincipal(
430 nsIPrincipal
* aNewDocumentStoragePrincipal
) {
431 if (mDocumentPrincipal
->Equals(aNewDocumentStoragePrincipal
)) {
432 mDocumentStoragePrincipal
= mDocumentPrincipal
;
436 // Compare originNoSuffix to ensure it's equal.
438 nsresult rv
= mDocumentPrincipal
->GetOriginNoSuffix(noSuffix
);
443 nsCString storageNoSuffix
;
444 rv
= aNewDocumentStoragePrincipal
->GetOriginNoSuffix(storageNoSuffix
);
449 if (noSuffix
!= storageNoSuffix
) {
450 return NS_ERROR_FAILURE
;
453 if (!mDocumentPrincipal
->OriginAttributesRef().EqualsIgnoringPartitionKey(
454 aNewDocumentStoragePrincipal
->OriginAttributesRef())) {
455 return NS_ERROR_FAILURE
;
458 mDocumentStoragePrincipal
= aNewDocumentStoragePrincipal
;
462 IPCResult
WindowGlobalParent::RecvUpdateDocumentPrincipal(
463 nsIPrincipal
* aNewDocumentPrincipal
,
464 nsIPrincipal
* aNewDocumentStoragePrincipal
) {
465 if (!mDocumentPrincipal
->Equals(aNewDocumentPrincipal
)) {
466 return IPC_FAIL(this,
467 "Trying to reuse WindowGlobalParent but the principal of "
468 "the new document does not match the old one");
470 mDocumentPrincipal
= aNewDocumentPrincipal
;
472 if (NS_FAILED(SetDocumentStoragePrincipal(aNewDocumentStoragePrincipal
))) {
473 return IPC_FAIL(this,
474 "Trying to reuse WindowGlobalParent but the principal of "
475 "the new document does not match the storage principal");
480 mozilla::ipc::IPCResult
WindowGlobalParent::RecvUpdateDocumentTitle(
481 const nsString
& aTitle
) {
482 if (mDocumentTitle
.isSome() && mDocumentTitle
.value() == aTitle
) {
486 mDocumentTitle
= Some(aTitle
);
488 // Send a pagetitlechanged event only for changes to the title
489 // for top-level frames.
490 if (!BrowsingContext()->IsTop()) {
494 // Notify media controller in order to update its default metadata.
495 if (BrowsingContext()->HasCreatedMediaController()) {
496 BrowsingContext()->GetMediaController()->NotifyPageTitleChanged();
499 Element
* frameElement
= BrowsingContext()->GetEmbedderElement();
504 AsyncEventDispatcher::RunDOMEventWhenSafe(
505 *frameElement
, u
"pagetitlechanged"_ns
, CanBubble::eYes
,
506 ChromeOnlyDispatch::eYes
);
511 mozilla::ipc::IPCResult
WindowGlobalParent::RecvUpdateHttpsOnlyStatus(
512 uint32_t aHttpsOnlyStatus
) {
513 mHttpsOnlyStatus
= aHttpsOnlyStatus
;
517 IPCResult
WindowGlobalParent::RecvUpdateDocumentHasLoaded(
518 bool aDocumentHasLoaded
) {
519 mDocumentHasLoaded
= aDocumentHasLoaded
;
523 IPCResult
WindowGlobalParent::RecvUpdateDocumentHasUserInteracted(
524 bool aDocumentHasUserInteracted
) {
525 mDocumentHasUserInteracted
= aDocumentHasUserInteracted
;
529 IPCResult
WindowGlobalParent::RecvUpdateSandboxFlags(uint32_t aSandboxFlags
) {
530 mSandboxFlags
= aSandboxFlags
;
534 IPCResult
WindowGlobalParent::RecvUpdateDocumentCspSettings(
535 bool aBlockAllMixedContent
, bool aUpgradeInsecureRequests
) {
536 mBlockAllMixedContent
= aBlockAllMixedContent
;
537 mUpgradeInsecureRequests
= aUpgradeInsecureRequests
;
541 mozilla::ipc::IPCResult
WindowGlobalParent::RecvSetClientInfo(
542 const IPCClientInfo
& aIPCClientInfo
) {
543 mClientInfo
= Some(ClientInfo(aIPCClientInfo
));
547 IPCResult
WindowGlobalParent::RecvDestroy() {
548 // Make a copy so that we can avoid potential iterator invalidation when
549 // calling the user-provided Destroy() methods.
550 JSActorWillDestroy();
553 RefPtr
<BrowserParent
> browserParent
= GetBrowserParent();
554 if (!browserParent
|| !browserParent
->IsDestroyed()) {
555 Unused
<< Send__delete__(this);
561 IPCResult
WindowGlobalParent::RecvRawMessage(
562 const JSActorMessageMeta
& aMeta
, const Maybe
<ClonedMessageData
>& aData
,
563 const Maybe
<ClonedMessageData
>& aStack
) {
564 Maybe
<StructuredCloneData
> data
;
567 data
->BorrowFromClonedMessageData(*aData
);
569 Maybe
<StructuredCloneData
> stack
;
572 stack
->BorrowFromClonedMessageData(*aStack
);
574 MMPrinter::Print("WindowGlobalParent::RecvRawMessage", aMeta
.actorName(),
575 aMeta
.messageName(), *aData
);
576 ReceiveRawMessage(aMeta
, std::move(data
), std::move(stack
));
580 const nsACString
& WindowGlobalParent::GetRemoteType() {
581 if (RefPtr
<BrowserParent
> browserParent
= GetBrowserParent()) {
582 return browserParent
->Manager()->GetRemoteType();
585 return NOT_REMOTE_TYPE
;
588 void WindowGlobalParent::NotifyContentBlockingEvent(
589 uint32_t aEvent
, nsIRequest
* aRequest
, bool aBlocked
,
590 const nsACString
& aTrackingOrigin
,
591 const nsTArray
<nsCString
>& aTrackingFullHashes
,
592 const Maybe
<ContentBlockingNotifier::StorageAccessPermissionGrantedReason
>&
594 const Maybe
<ContentBlockingNotifier::CanvasFingerprinter
>&
595 aCanvasFingerprinter
,
596 const Maybe
<bool> aCanvasFingerprinterKnownText
) {
597 MOZ_ASSERT(NS_IsMainThread());
598 DebugOnly
<bool> isCookiesBlocked
=
599 aEvent
== nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER
||
600 aEvent
== nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER
;
601 MOZ_ASSERT_IF(aBlocked
, aReason
.isNothing());
602 MOZ_ASSERT_IF(!isCookiesBlocked
, aReason
.isNothing());
603 MOZ_ASSERT_IF(isCookiesBlocked
&& !aBlocked
, aReason
.isSome());
604 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
605 // TODO: temporarily remove this until we find the root case of Bug 1609144
606 // MOZ_DIAGNOSTIC_ASSERT_IF(XRE_IsE10sParentProcess(), !IsInProcess());
608 // Return early if this WindowGlobalParent is in process.
613 Maybe
<uint32_t> event
= GetContentBlockingLog()->RecordLogParent(
614 aTrackingOrigin
, aEvent
, aBlocked
, aReason
, aTrackingFullHashes
,
615 aCanvasFingerprinter
, aCanvasFingerprinterKnownText
);
617 // Notify the OnContentBlockingEvent if necessary.
619 if (auto* webProgress
= GetBrowsingContext()->GetWebProgress()) {
620 webProgress
->OnContentBlockingEvent(webProgress
, aRequest
, event
.value());
625 already_AddRefed
<JSWindowActorParent
> WindowGlobalParent::GetActor(
626 JSContext
* aCx
, const nsACString
& aName
, ErrorResult
& aRv
) {
627 return JSActorManager::GetActor(aCx
, aName
, aRv
)
628 .downcast
<JSWindowActorParent
>();
631 already_AddRefed
<JSWindowActorParent
> WindowGlobalParent::GetExistingActor(
632 const nsACString
& aName
) {
633 return JSActorManager::GetExistingActor(aName
)
634 .downcast
<JSWindowActorParent
>();
637 already_AddRefed
<JSActor
> WindowGlobalParent::InitJSActor(
638 JS::Handle
<JSObject
*> aMaybeActor
, const nsACString
& aName
,
640 RefPtr
<JSWindowActorParent
> actor
;
641 if (aMaybeActor
.get()) {
642 aRv
= UNWRAP_OBJECT(JSWindowActorParent
, aMaybeActor
.get(), actor
);
647 actor
= new JSWindowActorParent();
650 MOZ_RELEASE_ASSERT(!actor
->GetManager(),
651 "mManager was already initialized once!");
652 actor
->Init(aName
, this);
653 return actor
.forget();
656 bool WindowGlobalParent::IsCurrentGlobal() {
657 if (mozilla::SessionHistoryInParent() && BrowsingContext() &&
658 BrowsingContext()->IsInBFCache()) {
662 return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() == this;
665 bool WindowGlobalParent::IsActiveInTab() {
670 CanonicalBrowsingContext
* bc
= BrowsingContext();
671 if (!bc
|| bc
->GetCurrentWindowGlobal() != this) {
675 // We check the top BC so we don't need to worry about getting a stale value.
676 // That may not be necessary.
677 MOZ_ASSERT(bc
->Top()->IsInBFCache() == bc
->IsInBFCache(),
678 "BFCache bit out of sync?");
679 return bc
->AncestorsAreCurrent() && !bc
->Top()->IsInBFCache();
684 class ShareHandler final
: public PromiseNativeHandler
{
686 explicit ShareHandler(
687 mozilla::dom::WindowGlobalParent::ShareResolver
&& aResolver
)
688 : mResolver(std::move(aResolver
)) {}
693 virtual void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
694 ErrorResult
& aRv
) override
{
698 virtual void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
699 ErrorResult
& aRv
) override
{
700 if (NS_WARN_IF(!aValue
.isObject())) {
701 mResolver(NS_ERROR_FAILURE
);
705 // nsresult is stored as Exception internally in Promise
706 JS::Rooted
<JSObject
*> obj(aCx
, &aValue
.toObject());
707 RefPtr
<DOMException
> unwrapped
;
708 nsresult rv
= UNWRAP_OBJECT(DOMException
, &obj
, unwrapped
);
709 if (NS_WARN_IF(NS_FAILED(rv
))) {
710 mResolver(NS_ERROR_FAILURE
);
714 mResolver(unwrapped
->GetResult());
718 ~ShareHandler() = default;
720 mozilla::dom::WindowGlobalParent::ShareResolver mResolver
;
723 NS_IMPL_ISUPPORTS0(ShareHandler
)
727 mozilla::ipc::IPCResult
WindowGlobalParent::RecvGetContentBlockingEvents(
728 WindowGlobalParent::GetContentBlockingEventsResolver
&& aResolver
) {
729 uint32_t events
= GetContentBlockingLog()->GetContentBlockingEventsInLog();
735 mozilla::ipc::IPCResult
WindowGlobalParent::RecvUpdateCookieJarSettings(
736 const CookieJarSettingsArgs
& aCookieJarSettingsArgs
) {
737 net::CookieJarSettings::Deserialize(aCookieJarSettingsArgs
,
738 getter_AddRefs(mCookieJarSettings
));
742 mozilla::ipc::IPCResult
WindowGlobalParent::RecvUpdateDocumentSecurityInfo(
743 nsITransportSecurityInfo
* aSecurityInfo
) {
744 mSecurityInfo
= aSecurityInfo
;
748 mozilla::ipc::IPCResult
WindowGlobalParent::RecvShare(
749 IPCWebShareData
&& aData
, WindowGlobalParent::ShareResolver
&& aResolver
) {
750 // Widget Layer handoff...
751 nsCOMPtr
<nsISharePicker
> sharePicker
=
752 do_GetService("@mozilla.org/sharepicker;1");
754 aResolver(NS_ERROR_DOM_NOT_SUPPORTED_ERR
);
758 // Initialize the ShareWidget
759 RefPtr
<BrowserParent
> parent
= GetBrowserParent();
760 nsCOMPtr
<mozIDOMWindowProxy
> openerWindow
;
762 openerWindow
= parent
->GetParentWindowOuter();
764 aResolver(NS_ERROR_FAILURE
);
768 sharePicker
->Init(openerWindow
);
770 // And finally share the data...
771 RefPtr
<Promise
> promise
;
772 nsresult rv
= sharePicker
->Share(aData
.title(), aData
.text(), aData
.url(),
773 getter_AddRefs(promise
));
779 // Handler finally awaits response...
780 RefPtr
<ShareHandler
> handler
= new ShareHandler(std::move(aResolver
));
781 promise
->AppendNativeHandler(handler
);
788 class CheckPermitUnloadRequest final
: public PromiseNativeHandler
,
789 public nsITimerCallback
{
791 CheckPermitUnloadRequest(WindowGlobalParent
* aWGP
, bool aHasInProcessBlocker
,
792 nsIDocumentViewer::PermitUnloadAction aAction
,
793 std::function
<void(bool)>&& aResolver
)
794 : mResolver(std::move(aResolver
)),
797 mFoundBlocker(aHasInProcessBlocker
) {}
799 void Run(ContentParent
* aIgnoreProcess
= nullptr, uint32_t aTimeout
= 0) {
800 MOZ_ASSERT(mState
== State::UNINITIALIZED
);
801 mState
= State::WAITING
;
803 RefPtr
<CheckPermitUnloadRequest
> self(this);
805 AutoTArray
<ContentParent
*, 8> seen
;
806 if (aIgnoreProcess
) {
807 seen
.AppendElement(aIgnoreProcess
);
810 BrowsingContext
* bc
= mWGP
->GetBrowsingContext();
811 bc
->PreOrderWalk([&](dom::BrowsingContext
* aBC
) {
812 if (WindowGlobalParent
* wgp
=
813 aBC
->Canonical()->GetCurrentWindowGlobal()) {
814 ContentParent
* cp
= wgp
->GetContentParent();
815 if (wgp
->HasBeforeUnload() && !seen
.ContainsSorted(cp
)) {
816 seen
.InsertElementSorted(cp
);
818 auto resolve
= [self
](bool blockNavigation
) {
819 if (blockNavigation
) {
820 self
->mFoundBlocker
= true;
822 self
->ResolveRequest();
825 cp
->SendDispatchBeforeUnloadToSubtree(
826 bc
, resolve
, [self
](auto) { self
->ResolveRequest(); });
828 ContentChild::DispatchBeforeUnloadToSubtree(bc
, resolve
);
834 if (mPendingRequests
&& aTimeout
) {
835 Unused
<< NS_NewTimerWithCallback(getter_AddRefs(mTimer
), this, aTimeout
,
836 nsITimer::TYPE_ONE_SHOT
);
842 void ResolveRequest() {
847 NS_IMETHODIMP
Notify(nsITimer
* aTimer
) override
{
848 MOZ_ASSERT(aTimer
== mTimer
);
849 if (mState
== State::WAITING
) {
850 mState
= State::TIMED_OUT
;
856 void CheckDoneWaiting() {
857 // If we've found a blocker, we prompt immediately without waiting for
858 // further responses. The user's response applies to the entire navigation
859 // attempt, regardless of how many "beforeunload" listeners we call.
860 if (mState
!= State::TIMED_OUT
&&
861 (mState
!= State::WAITING
|| (mPendingRequests
&& !mFoundBlocker
))) {
865 mState
= State::PROMPTING
;
867 // Clearing our reference to the timer will automatically cancel it if it's
871 if (!mFoundBlocker
) {
876 auto action
= mAction
;
877 if (StaticPrefs::dom_disable_beforeunload()) {
878 action
= nsIDocumentViewer::eDontPromptAndUnload
;
880 if (action
!= nsIDocumentViewer::ePrompt
) {
881 SendReply(action
== nsIDocumentViewer::eDontPromptAndUnload
);
885 // Handle any failure in prompting by aborting the navigation. See comment
886 // in nsContentViewer::PermitUnload for reasoning.
887 auto cleanup
= MakeScopeExit([&]() { SendReply(false); });
889 if (nsCOMPtr
<nsIPromptCollection
> prompt
=
890 do_GetService("@mozilla.org/embedcomp/prompt-collection;1")) {
891 RefPtr
<Promise
> promise
;
892 prompt
->AsyncBeforeUnloadCheck(mWGP
->GetBrowsingContext(),
893 getter_AddRefs(promise
));
899 promise
->AppendNativeHandler(this);
904 void SendReply(bool aAllow
) {
905 MOZ_ASSERT(mState
!= State::REPLIED
);
907 mState
= State::REPLIED
;
910 void ResolvedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
911 ErrorResult
& aRv
) override
{
912 MOZ_ASSERT(mState
== State::PROMPTING
);
914 SendReply(JS::ToBoolean(aValue
));
917 void RejectedCallback(JSContext
* aCx
, JS::Handle
<JS::Value
> aValue
,
918 ErrorResult
& aRv
) override
{
919 MOZ_ASSERT(mState
== State::PROMPTING
);
927 ~CheckPermitUnloadRequest() {
928 // We may get here without having sent a reply if the promise we're waiting
929 // on is destroyed without being resolved or rejected.
930 if (mState
!= State::REPLIED
) {
935 enum class State
: uint8_t {
943 std::function
<void(bool)> mResolver
;
945 RefPtr
<WindowGlobalParent
> mWGP
;
946 nsCOMPtr
<nsITimer
> mTimer
;
948 uint32_t mPendingRequests
= 0;
950 nsIDocumentViewer::PermitUnloadAction mAction
;
952 State mState
= State::UNINITIALIZED
;
954 bool mFoundBlocker
= false;
957 NS_IMPL_ISUPPORTS(CheckPermitUnloadRequest
, nsITimerCallback
)
961 mozilla::ipc::IPCResult
WindowGlobalParent::RecvCheckPermitUnload(
962 bool aHasInProcessBlocker
, XPCOMPermitUnloadAction aAction
,
963 CheckPermitUnloadResolver
&& aResolver
) {
964 if (!IsCurrentGlobal()) {
969 auto request
= MakeRefPtr
<CheckPermitUnloadRequest
>(
970 this, aHasInProcessBlocker
, aAction
, std::move(aResolver
));
971 request
->Run(/* aIgnoreProcess */ GetContentParent());
976 already_AddRefed
<Promise
> WindowGlobalParent::PermitUnload(
977 PermitUnloadAction aAction
, uint32_t aTimeout
, mozilla::ErrorResult
& aRv
) {
978 nsIGlobalObject
* global
= GetParentObject();
979 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
980 if (NS_WARN_IF(aRv
.Failed())) {
984 auto request
= MakeRefPtr
<CheckPermitUnloadRequest
>(
985 this, /* aHasInProcessBlocker */ false,
986 nsIDocumentViewer::PermitUnloadAction(aAction
),
987 [promise
](bool aAllow
) { promise
->MaybeResolve(aAllow
); });
988 request
->Run(/* aIgnoreProcess */ nullptr, aTimeout
);
990 return promise
.forget();
993 void WindowGlobalParent::PermitUnload(std::function
<void(bool)>&& aResolver
) {
994 RefPtr
<CheckPermitUnloadRequest
> request
= new CheckPermitUnloadRequest(
995 this, /* aHasInProcessBlocker */ false,
996 nsIDocumentViewer::PermitUnloadAction::ePrompt
, std::move(aResolver
));
1000 already_AddRefed
<mozilla::dom::Promise
> WindowGlobalParent::DrawSnapshot(
1001 const DOMRect
* aRect
, double aScale
, const nsACString
& aBackgroundColor
,
1002 bool aResetScrollPosition
, mozilla::ErrorResult
& aRv
) {
1003 nsIGlobalObject
* global
= GetParentObject();
1004 RefPtr
<Promise
> promise
= Promise::Create(global
, aRv
);
1005 if (NS_WARN_IF(aRv
.Failed())) {
1010 if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0),
1011 aBackgroundColor
, &color
,
1012 nullptr, nullptr))) {
1013 aRv
= NS_ERROR_FAILURE
;
1017 gfx::CrossProcessPaintFlags flags
=
1018 gfx::CrossProcessPaintFlags::UseHighQualityScaling
;
1020 // If no explicit Rect was passed, we want the currently visible viewport.
1021 flags
|= gfx::CrossProcessPaintFlags::DrawView
;
1022 } else if (aResetScrollPosition
) {
1023 flags
|= gfx::CrossProcessPaintFlags::ResetScrollPosition
;
1026 if (!gfx::CrossProcessPaint::Start(this, aRect
, (float)aScale
, color
, flags
,
1028 aRv
= NS_ERROR_FAILURE
;
1031 return promise
.forget();
1034 void WindowGlobalParent::DrawSnapshotInternal(gfx::CrossProcessPaint
* aPaint
,
1035 const Maybe
<IntRect
>& aRect
,
1037 nscolor aBackgroundColor
,
1039 auto promise
= SendDrawSnapshot(aRect
, aScale
, aBackgroundColor
, aFlags
);
1041 RefPtr
<gfx::CrossProcessPaint
> paint(aPaint
);
1042 RefPtr
<WindowGlobalParent
> wgp(this);
1044 GetMainThreadSerialEventTarget(), __func__
,
1045 [paint
, wgp
](PaintFragment
&& aFragment
) {
1046 paint
->ReceiveFragment(wgp
, std::move(aFragment
));
1048 [paint
, wgp
](ResponseRejectReason
&& aReason
) {
1049 paint
->LostFragment(wgp
);
1054 * Accumulated page use counter data for a given top-level content document.
1056 struct PageUseCounters
{
1057 // The number of page use counter data messages we are still waiting for.
1058 uint32_t mWaiting
= 0;
1060 // Whether we have received any page use counter data.
1061 bool mReceivedAny
= false;
1063 // The accumulated page use counters.
1064 UseCounters mUseCounters
;
1067 mozilla::ipc::IPCResult
WindowGlobalParent::RecvExpectPageUseCounters(
1068 const MaybeDiscarded
<WindowContext
>& aTop
) {
1069 if (aTop
.IsNull()) {
1070 return IPC_FAIL(this, "aTop must not be null");
1073 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1074 ("Expect page use counters: WindowContext %" PRIu64
" -> %" PRIu64
,
1075 InnerWindowId(), aTop
.ContextId()));
1077 // We've been called to indicate that the document in our window intends
1078 // to send use counter data to accumulate towards the top-level document's
1079 // page use counters. This causes us to wait for this window to go away
1080 // (in WindowGlobalParent::ActorDestroy) before reporting the page use
1081 // counters via Telemetry.
1082 RefPtr
<WindowGlobalParent
> page
=
1083 static_cast<WindowGlobalParent
*>(aTop
.GetMaybeDiscarded());
1084 if (!page
|| page
->mSentPageUseCounters
) {
1085 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1086 (" > too late, won't report page use counters for this straggler"));
1090 if (mPageUseCountersWindow
) {
1091 if (mPageUseCountersWindow
!= page
) {
1092 return IPC_FAIL(this,
1093 "ExpectPageUseCounters called on the same "
1094 "WindowContext with a different aTop value");
1097 // We can get called with the same aTop value more than once, e.g. for
1098 // initial about:blank documents and then subsequent "real" documents loaded
1099 // into the same window. We must note each source window only once.
1103 // Note that the top-level document must wait for one more window's use
1104 // counters before reporting via Telemetry.
1105 mPageUseCountersWindow
= page
;
1106 if (!page
->mPageUseCounters
) {
1107 page
->mPageUseCounters
= MakeUnique
<PageUseCounters
>();
1109 ++page
->mPageUseCounters
->mWaiting
;
1112 gUseCountersLog
, LogLevel::Debug
,
1113 (" > top-level now waiting on %d\n", page
->mPageUseCounters
->mWaiting
));
1118 mozilla::ipc::IPCResult
WindowGlobalParent::RecvAccumulatePageUseCounters(
1119 const UseCounters
& aUseCounters
) {
1120 // We've been called to accumulate use counter data into the page use counters
1121 // for the document in mPageUseCountersWindow.
1124 gUseCountersLog
, LogLevel::Debug
,
1125 ("Accumulate page use counters: WindowContext %" PRIu64
" -> %" PRIu64
,
1127 mPageUseCountersWindow
? mPageUseCountersWindow
->InnerWindowId() : 0));
1129 if (!mPageUseCountersWindow
|| mPageUseCountersWindow
->mSentPageUseCounters
) {
1130 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1131 (" > too late, won't report page use counters for this straggler"));
1135 MOZ_ASSERT(mPageUseCountersWindow
->mPageUseCounters
);
1136 MOZ_ASSERT(mPageUseCountersWindow
->mPageUseCounters
->mWaiting
> 0);
1138 mPageUseCountersWindow
->mPageUseCounters
->mUseCounters
|= aUseCounters
;
1139 mPageUseCountersWindow
->mPageUseCounters
->mReceivedAny
= true;
1143 // This is called on the top-level WindowGlobal, i.e. the one that is
1144 // accumulating the page use counters, not the (potentially descendant) window
1145 // that has finished providing use counter data.
1146 void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
1147 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1148 ("Stop expecting page use counters: -> WindowContext %" PRIu64
,
1151 if (!mPageUseCounters
) {
1152 MOZ_ASSERT_UNREACHABLE("Not expecting page use counter data");
1153 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1154 (" > not expecting page use counter data"));
1158 MOZ_ASSERT(mPageUseCounters
->mWaiting
> 0);
1159 --mPageUseCounters
->mWaiting
;
1161 if (mPageUseCounters
->mWaiting
> 0) {
1162 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1163 (" > now waiting on %d", mPageUseCounters
->mWaiting
));
1167 if (mPageUseCounters
->mReceivedAny
) {
1168 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1169 (" > reporting [%s]",
1170 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
).get()));
1172 Maybe
<nsCString
> urlForLogging
;
1173 const bool dumpCounters
= StaticPrefs::dom_use_counters_dump_page();
1175 urlForLogging
.emplace(
1176 nsContentUtils::TruncatedURLForDisplay(mDocumentURI
));
1179 glean::use_counter::top_level_content_documents_destroyed
.Add();
1182 for (int32_t c
= 0; c
< eUseCounter_Count
; ++c
) {
1183 auto uc
= static_cast<UseCounter
>(c
);
1184 if (!mPageUseCounters
->mUseCounters
[uc
]) {
1188 const char* metricName
= IncrementUseCounter(uc
, /* aIsPage = */ true);
1190 printf_stderr("USE_COUNTER_PAGE: %s - %s\n", metricName
,
1191 urlForLogging
->get());
1196 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1197 (" > page use counter data was received, but was empty"));
1200 MOZ_LOG(gUseCountersLog
, LogLevel::Debug
,
1201 (" > no page use counter data was received"));
1204 mSentPageUseCounters
= true;
1205 mPageUseCounters
= nullptr;
1208 Element
* WindowGlobalParent::GetRootOwnerElement() {
1209 WindowGlobalParent
* top
= TopWindowContext();
1214 if (IsInProcess()) {
1215 return top
->BrowsingContext()->GetEmbedderElement();
1218 if (BrowserParent
* parent
= top
->GetBrowserParent()) {
1219 return parent
->GetOwnerElement();
1225 void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element
* aEmbedder
) {
1227 aEmbedder
= GetRootOwnerElement();
1230 if (nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService()) {
1231 obs
->NotifyWhenScriptSafe(ToSupports(aEmbedder
),
1232 "browser-shutdown-tabstate-updated", nullptr);
1237 mozilla::ipc::IPCResult
WindowGlobalParent::RecvRequestRestoreTabContent() {
1238 CanonicalBrowsingContext
* bc
= BrowsingContext();
1239 if (bc
&& bc
->AncestorsAreCurrent()) {
1240 bc
->Top()->RequestRestoreTabContent(this);
1245 nsCString
BFCacheStatusToString(uint32_t aFlags
) {
1251 #define ADD_BFCACHESTATUS_TO_STRING(_flag) \
1252 if (aFlags & BFCacheStatus::_flag) { \
1253 if (!flags.IsEmpty()) { \
1254 flags.Append('|'); \
1256 flags.AppendLiteral(#_flag); \
1257 aFlags &= ~BFCacheStatus::_flag; \
1260 ADD_BFCACHESTATUS_TO_STRING(NOT_ALLOWED
);
1261 ADD_BFCACHESTATUS_TO_STRING(EVENT_HANDLING_SUPPRESSED
);
1262 ADD_BFCACHESTATUS_TO_STRING(SUSPENDED
);
1263 ADD_BFCACHESTATUS_TO_STRING(UNLOAD_LISTENER
);
1264 ADD_BFCACHESTATUS_TO_STRING(REQUEST
);
1265 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_GET_USER_MEDIA
);
1266 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_PEER_CONNECTION
);
1267 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_EME_CONTENT
);
1268 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_MSE_CONTENT
);
1269 ADD_BFCACHESTATUS_TO_STRING(HAS_ACTIVE_SPEECH_SYNTHESIS
);
1270 ADD_BFCACHESTATUS_TO_STRING(HAS_USED_VR
);
1271 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_REMOTE_SUBFRAMES
);
1272 ADD_BFCACHESTATUS_TO_STRING(NOT_ONLY_TOPLEVEL_IN_BCG
);
1273 ADD_BFCACHESTATUS_TO_STRING(BEFOREUNLOAD_LISTENER
);
1274 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_LOCK
);
1276 #undef ADD_BFCACHESTATUS_TO_STRING
1278 MOZ_ASSERT(aFlags
== 0,
1279 "Missing stringification for enum value in BFCacheStatus.");
1283 mozilla::ipc::IPCResult
WindowGlobalParent::RecvUpdateBFCacheStatus(
1284 const uint32_t& aOnFlags
, const uint32_t& aOffFlags
) {
1285 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog
, LogLevel::Debug
))) {
1286 nsAutoCString
uri("[no uri]");
1288 uri
= mDocumentURI
->GetSpecOrDefault();
1290 MOZ_LOG(gSHIPBFCacheLog
, LogLevel::Debug
,
1291 ("Setting BFCache flags for %s +(%s) -(%s)", uri
.get(),
1292 BFCacheStatusToString(aOnFlags
).get(),
1293 BFCacheStatusToString(aOffFlags
).get()));
1295 mBFCacheStatus
|= aOnFlags
;
1296 mBFCacheStatus
&= ~aOffFlags
;
1300 mozilla::ipc::IPCResult
1301 WindowGlobalParent::RecvUpdateActivePeerConnectionStatus(bool aIsAdded
) {
1303 RecvUpdateBFCacheStatus(BFCacheStatus::ACTIVE_PEER_CONNECTION
, 0);
1305 RecvUpdateBFCacheStatus(0, BFCacheStatus::ACTIVE_PEER_CONNECTION
);
1308 if (WindowGlobalParent
* top
= TopWindowContext()) {
1309 CheckedUint32
newValue(top
->mNumOfProcessesWithActivePeerConnections
);
1315 if (!newValue
.isValid()) {
1316 return IPC_FAIL(this,
1317 "mNumOfProcessesWithActivePeerConnections overflowed");
1320 top
->mNumOfProcessesWithActivePeerConnections
= newValue
.value();
1321 Unused
<< top
->SetHasActivePeerConnections(newValue
.value() > 0);
1327 mozilla::ipc::IPCResult
WindowGlobalParent::RecvSetSingleChannelId(
1328 const Maybe
<uint64_t>& aSingleChannelId
) {
1329 mSingleChannelId
= aSingleChannelId
;
1333 mozilla::ipc::IPCResult
WindowGlobalParent::RecvSetDocumentDomain(
1334 NotNull
<nsIURI
*> aDomain
) {
1335 if (mSandboxFlags
& SANDBOXED_DOMAIN
) {
1336 // We're sandboxed; disallow setting domain
1337 return IPC_FAIL(this, "Sandbox disallows domain setting.");
1340 // Might need to do a featurepolicy check here, like we currently do in the
1343 nsCOMPtr
<nsIURI
> uri
;
1344 mDocumentPrincipal
->GetDomain(getter_AddRefs(uri
));
1346 uri
= mDocumentPrincipal
->GetURI();
1352 if (!Document::IsValidDomain(uri
, aDomain
)) {
1353 // Error: illegal domain
1355 this, "Setting domain that's not a suffix of existing domain value.");
1358 if (Group()->IsPotentiallyCrossOriginIsolated()) {
1359 return IPC_FAIL(this, "Setting domain in a cross-origin isolated BC.");
1362 mDocumentPrincipal
->SetDomain(aDomain
);
1366 mozilla::ipc::IPCResult
WindowGlobalParent::RecvReloadWithHttpsOnlyException() {
1368 nsCOMPtr
<nsIURI
> currentUri
= BrowsingContext()->Top()->GetCurrentURI();
1371 return IPC_FAIL(this, "HTTPS-only mode: Failed to get current URI");
1374 bool isViewSource
= currentUri
->SchemeIs("view-source");
1376 nsCOMPtr
<nsINestedURI
> nestedURI
= do_QueryInterface(currentUri
);
1377 nsCOMPtr
<nsIURI
> innerURI
;
1379 nestedURI
->GetInnerURI(getter_AddRefs(innerURI
));
1381 innerURI
= currentUri
;
1384 if (!innerURI
->SchemeIs("https") && !innerURI
->SchemeIs("http")) {
1385 return IPC_FAIL(this, "HTTPS-only mode: Illegal state");
1388 // If the error page is within an iFrame, we create an exception for whatever
1389 // scheme the top-level site is currently on, because the user wants to
1390 // unbreak the iFrame and not the top-level page. When the error page shows up
1391 // on a top-level request, then we replace the scheme with http, because the
1392 // user wants to unbreak the whole page.
1393 nsCOMPtr
<nsIURI
> newURI
;
1394 if (!BrowsingContext()->IsTop()) {
1397 Unused
<< NS_MutateURI(innerURI
).SetScheme("http"_ns
).Finalize(
1398 getter_AddRefs(newURI
));
1401 OriginAttributes originAttributes
=
1402 TopWindowContext()->DocumentPrincipal()->OriginAttributesRef();
1404 originAttributes
.SetFirstPartyDomain(true, newURI
);
1406 nsCOMPtr
<nsIPermissionManager
> permMgr
=
1407 components::PermissionManager::Service();
1410 this, "HTTPS-only mode: Failed to get Permission Manager service");
1413 nsCOMPtr
<nsIPrincipal
> principal
=
1414 BasePrincipal::CreateContentPrincipal(newURI
, originAttributes
);
1416 rv
= permMgr
->AddFromPrincipal(
1417 principal
, "https-only-load-insecure"_ns
,
1418 nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION
,
1419 nsIPermissionManager::EXPIRE_SESSION
, 0);
1421 if (NS_FAILED(rv
)) {
1423 this, "HTTPS-only mode: Failed to add permission to the principal");
1426 nsCOMPtr
<nsIURI
> insecureURI
= newURI
;
1429 MOZ_ALWAYS_SUCCEEDS(newURI
->GetSpec(spec
));
1431 NS_NewURI(getter_AddRefs(insecureURI
), "view-source:"_ns
+ spec
))) {
1433 this, "HTTPS-only mode: Failed to re-construct view-source URI");
1437 RefPtr
<nsDocShellLoadState
> loadState
= new nsDocShellLoadState(insecureURI
);
1438 loadState
->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
1439 loadState
->SetLoadType(LOAD_NORMAL_REPLACE
);
1441 RefPtr
<CanonicalBrowsingContext
> topBC
= BrowsingContext()->Top();
1442 topBC
->LoadURI(loadState
, /* setNavigating */ true);
1447 IPCResult
WindowGlobalParent::RecvDiscoverIdentityCredentialFromExternalSource(
1448 const IdentityCredentialRequestOptions
& aOptions
,
1449 const DiscoverIdentityCredentialFromExternalSourceResolver
& aResolver
) {
1450 IdentityCredential::DiscoverFromExternalSourceInMainProcess(
1451 DocumentPrincipal(), this->BrowsingContext(), aOptions
)
1453 GetCurrentSerialEventTarget(), __func__
,
1454 [aResolver
](const IPCIdentityCredential
& aResult
) {
1455 return aResolver(Some(aResult
));
1457 [aResolver
](nsresult aErr
) { aResolver(Nothing()); });
1461 IPCResult
WindowGlobalParent::RecvGetStorageAccessPermission(
1462 GetStorageAccessPermissionResolver
&& aResolve
) {
1463 WindowGlobalParent
* top
= TopWindowContext();
1465 return IPC_FAIL_NO_REASON(this);
1467 nsIPrincipal
* topPrincipal
= top
->DocumentPrincipal();
1468 nsIPrincipal
* principal
= DocumentPrincipal();
1470 nsresult rv
= AntiTrackingUtils::TestStoragePermissionInParent(
1471 topPrincipal
, principal
, &result
);
1472 if (NS_WARN_IF(NS_FAILED(rv
))) {
1473 aResolve(nsIPermissionManager::UNKNOWN_ACTION
);
1481 void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy
) {
1482 if (GetBrowsingContext()->IsTopContent()) {
1483 Telemetry::Accumulate(Telemetry::ORB_DID_EVER_BLOCK_RESPONSE
,
1484 mShouldReportHasBlockedOpaqueResponse
);
1487 if (mPageUseCountersWindow
) {
1488 mPageUseCountersWindow
->FinishAccumulatingPageUseCounters();
1489 mPageUseCountersWindow
= nullptr;
1492 if (GetBrowsingContext()->IsTopContent() &&
1493 !mDocumentPrincipal
->SchemeIs("about")) {
1494 // Record the page load
1495 uint32_t pageLoaded
= 1;
1496 Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER
, pageLoaded
);
1498 // Record the mixed content status of the docshell in Telemetry
1500 NO_MIXED_CONTENT
= 0, // There is no Mixed Content on the page
1501 MIXED_DISPLAY_CONTENT
=
1502 1, // The page attempted to load Mixed Display Content
1503 MIXED_ACTIVE_CONTENT
=
1504 2, // The page attempted to load Mixed Active Content
1505 MIXED_DISPLAY_AND_ACTIVE_CONTENT
= 3 // The page attempted to load Mixed
1506 // Display & Mixed Active Content
1509 bool hasMixedDisplay
=
1511 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT
|
1512 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT
);
1513 bool hasMixedActive
=
1515 (nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT
|
1516 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT
);
1518 uint32_t mixedContentLevel
= NO_MIXED_CONTENT
;
1519 if (hasMixedDisplay
&& hasMixedActive
) {
1520 mixedContentLevel
= MIXED_DISPLAY_AND_ACTIVE_CONTENT
;
1521 } else if (hasMixedActive
) {
1522 mixedContentLevel
= MIXED_ACTIVE_CONTENT
;
1523 } else if (hasMixedDisplay
) {
1524 mixedContentLevel
= MIXED_DISPLAY_CONTENT
;
1526 Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD
, mixedContentLevel
);
1528 if (GetDocTreeHadMedia()) {
1529 ScalarAdd(Telemetry::ScalarID::MEDIA_ELEMENT_IN_PAGE_COUNT
, 1);
1533 ContentParent
* cp
= nullptr;
1534 if (!IsInProcess()) {
1535 cp
= static_cast<ContentParent
*>(Manager()->Manager());
1538 Group()->EachOtherParent(cp
, [&](ContentParent
* otherContent
) {
1539 // Keep the WindowContext and our BrowsingContextGroup alive until other
1540 // processes have acknowledged it has been discarded.
1541 Group()->AddKeepAlive();
1542 auto callback
= [self
= RefPtr
{this}](auto) {
1543 self
->Group()->RemoveKeepAlive();
1545 otherContent
->SendDiscardWindowContext(InnerWindowId(), callback
, callback
);
1548 // Note that our WindowContext has become discarded.
1549 WindowContext::Discard();
1551 // Report content blocking log when destroyed.
1552 // There shouldn't have any content blocking log when a document is loaded in
1553 // the parent process(See NotifyContentBlockingEvent), so we could skip
1554 // reporting log when it is in-process.
1555 if (!IsInProcess()) {
1556 RefPtr
<BrowserParent
> browserParent
=
1557 static_cast<BrowserParent
*>(Manager());
1558 if (browserParent
) {
1559 nsCOMPtr
<nsILoadContext
> loadContext
= browserParent
->GetLoadContext();
1560 if (loadContext
&& !loadContext
->UsePrivateBrowsing() &&
1561 BrowsingContext()->IsTopContent()) {
1562 GetContentBlockingLog()->ReportLog(DocumentPrincipal());
1564 if (mDocumentURI
&& (net::SchemeIsHTTP(mDocumentURI
) ||
1565 net::SchemeIsHTTPS(mDocumentURI
))) {
1566 GetContentBlockingLog()->ReportCanvasFingerprintingLog(
1567 DocumentPrincipal());
1568 GetContentBlockingLog()->ReportFontFingerprintingLog(
1569 DocumentPrincipal());
1570 GetContentBlockingLog()->ReportEmailTrackingLog(DocumentPrincipal());
1576 // Destroy our JSWindowActors, and reject any pending queries.
1577 JSActorDidDestroy();
1579 nsCOMPtr
<nsIObserverService
> obs
= services::GetObserverService();
1581 obs
->NotifyObservers(ToSupports(this), "window-global-destroyed", nullptr);
1584 if (mOriginCounter
) {
1585 mOriginCounter
->Accumulate();
1589 WindowGlobalParent::~WindowGlobalParent() = default;
1591 JSObject
* WindowGlobalParent::WrapObject(JSContext
* aCx
,
1592 JS::Handle
<JSObject
*> aGivenProto
) {
1593 return WindowGlobalParent_Binding::Wrap(aCx
, this, aGivenProto
);
1596 nsIGlobalObject
* WindowGlobalParent::GetParentObject() {
1597 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
1600 nsIDOMProcessParent
* WindowGlobalParent::GetDomProcess() {
1601 if (RefPtr
<BrowserParent
> browserParent
= GetBrowserParent()) {
1602 return browserParent
->Manager();
1604 return InProcessParent::Singleton();
1607 void WindowGlobalParent::DidBecomeCurrentWindowGlobal(bool aCurrent
) {
1608 WindowGlobalParent
* top
= BrowsingContext()->GetTopWindowContext();
1609 if (top
&& top
->mOriginCounter
) {
1610 top
->mOriginCounter
->UpdateSiteOriginsFrom(this,
1611 /* aIncrease = */ aCurrent
);
1614 if (!aCurrent
&& Fullscreen()) {
1615 ExitTopChromeDocumentFullscreen();
1619 bool WindowGlobalParent::ShouldTrackSiteOriginTelemetry() {
1620 CanonicalBrowsingContext
* bc
= BrowsingContext();
1622 if (!bc
->IsTopContent()) {
1626 RefPtr
<BrowserParent
> browserParent
= GetBrowserParent();
1627 if (!browserParent
||
1628 !IsWebRemoteType(browserParent
->Manager()->GetRemoteType())) {
1632 return DocumentPrincipal()->GetIsContentPrincipal();
1635 void WindowGlobalParent::AddSecurityState(uint32_t aStateFlags
) {
1636 MOZ_ASSERT(TopWindowContext() == this);
1637 MOZ_ASSERT((aStateFlags
&
1638 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT
|
1639 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT
|
1640 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT
|
1641 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT
|
1642 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED
|
1643 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED
|
1644 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED_FIRST
)) ==
1646 "Invalid flags specified!");
1648 if ((mSecurityState
& aStateFlags
) == aStateFlags
) {
1652 mSecurityState
|= aStateFlags
;
1654 if (GetBrowsingContext()->GetCurrentWindowGlobal() == this) {
1655 GetBrowsingContext()->UpdateSecurityState();
1659 bool WindowGlobalParent::HasActivePeerConnections() {
1660 MOZ_ASSERT(TopWindowContext() == this,
1661 "mNumOfProcessesWithActivePeerConnections is set only "
1662 "in the top window context");
1663 return mNumOfProcessesWithActivePeerConnections
> 0;
1666 void WindowGlobalParent::ExitTopChromeDocumentFullscreen() {
1667 RefPtr
<CanonicalBrowsingContext
> chromeTop
=
1668 BrowsingContext()->TopCrossChromeBoundary();
1669 if (Document
* chromeDoc
= chromeTop
->GetDocument()) {
1670 Document::ClearPendingFullscreenRequests(chromeDoc
);
1671 if (chromeDoc
->Fullscreen()) {
1672 // This only clears the DOM fullscreen, will not exit from browser UI
1674 Document::AsyncExitFullscreen(chromeDoc
);
1679 void WindowGlobalParent::SetShouldReportHasBlockedOpaqueResponse(
1680 nsContentPolicyType aContentPolicy
) {
1681 // It's always okay to block TYPE_BEACON, TYPE_PING and TYPE_CSP_REPORT in
1682 // the parent process because content processes can do nothing to their
1683 // responses. Hence excluding them from the telemetry as blocking
1684 // them have no webcompat concerns.
1685 if (aContentPolicy
!= nsIContentPolicy::TYPE_BEACON
&&
1686 aContentPolicy
!= nsIContentPolicy::TYPE_PING
&&
1687 aContentPolicy
!= nsIContentPolicy::TYPE_CSP_REPORT
) {
1689 mShouldReportHasBlockedOpaqueResponse
= true;
1694 IPCResult
WindowGlobalParent::RecvSetCookies(
1695 const nsCString
& aBaseDomain
, const OriginAttributes
& aOriginAttributes
,
1696 nsIURI
* aHost
, bool aFromHttp
, const nsTArray
<CookieStruct
>& aCookies
) {
1697 // Get CookieServiceParent via
1698 // ContentParent->NeckoParent->CookieServiceParent.
1699 ContentParent
* contentParent
= GetContentParent();
1700 NS_ENSURE_TRUE(contentParent
, IPC_OK());
1702 net::PNeckoParent
* neckoParent
=
1703 LoneManagedOrNullAsserts(contentParent
->ManagedPNeckoParent());
1704 NS_ENSURE_TRUE(neckoParent
, IPC_OK());
1705 net::PCookieServiceParent
* csParent
=
1706 LoneManagedOrNullAsserts(neckoParent
->ManagedPCookieServiceParent());
1707 NS_ENSURE_TRUE(csParent
, IPC_OK());
1708 auto* cs
= static_cast<net::CookieServiceParent
*>(csParent
);
1710 return cs
->SetCookies(aBaseDomain
, aOriginAttributes
, aHost
, aFromHttp
,
1711 aCookies
, GetBrowsingContext());
1714 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowGlobalParent
, WindowContext
,
1715 mPageUseCountersWindow
)
1717 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalParent
,
1719 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1721 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalParent
)
1722 NS_INTERFACE_MAP_END_INHERITING(WindowContext
)
1724 NS_IMPL_ADDREF_INHERITED(WindowGlobalParent
, WindowContext
)
1725 NS_IMPL_RELEASE_INHERITED(WindowGlobalParent
, WindowContext
)
1727 } // namespace mozilla::dom