Bug 1897989 - Add Wasm exception-handling tests for JSPI. r=rhunt
[gecko.git] / dom / ipc / WindowGlobalParent.cpp
blobd5c6dc98de563f476c21178a8095cf44d404b67f
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"
9 #include <algorithm>
11 #include "mozilla/AntiTrackingUtils.h"
12 #include "mozilla/AsyncEventDispatcher.h"
13 #include "mozilla/BounceTrackingStorageObserver.h"
14 #include "mozilla/BounceTrackingProtection.h"
15 #include "mozilla/ClearOnShutdown.h"
16 #include "mozilla/ContentBlockingAllowList.h"
17 #include "mozilla/dom/InProcessParent.h"
18 #include "mozilla/dom/BrowserBridgeParent.h"
19 #include "mozilla/dom/BrowsingContextGroup.h"
20 #include "mozilla/dom/CanonicalBrowsingContext.h"
21 #include "mozilla/dom/ClientInfo.h"
22 #include "mozilla/dom/ClientIPCTypes.h"
23 #include "mozilla/dom/ContentChild.h"
24 #include "mozilla/dom/ContentParent.h"
25 #include "mozilla/dom/BrowserHost.h"
26 #include "mozilla/dom/BrowserParent.h"
27 #include "mozilla/dom/IdentityCredential.h"
28 #include "mozilla/dom/MediaController.h"
29 #include "mozilla/dom/WindowGlobalChild.h"
30 #include "mozilla/dom/ChromeUtils.h"
31 #include "mozilla/dom/UseCounterMetrics.h"
32 #include "mozilla/dom/ipc/IdType.h"
33 #include "mozilla/dom/ipc/StructuredCloneData.h"
34 #include "mozilla/glean/GleanMetrics.h"
35 #include "mozilla/Components.h"
36 #include "mozilla/IdentityCredentialRequestManager.h"
37 #include "mozilla/ScopeExit.h"
38 #include "mozilla/ServoCSSParser.h"
39 #include "mozilla/ServoStyleSet.h"
40 #include "mozilla/StaticPrefs_dom.h"
41 #include "mozilla/StaticPrefs_network.h"
42 #include "mozilla/Telemetry.h"
43 #include "mozilla/Variant.h"
44 #include "mozilla/ipc/ProtocolUtils.h"
45 #include "MMPrinter.h"
46 #include "nsContentUtils.h"
47 #include "nsDocShell.h"
48 #include "nsDocShellLoadState.h"
49 #include "nsError.h"
50 #include "nsFrameLoader.h"
51 #include "nsFrameLoaderOwner.h"
52 #include "nsICookieManager.h"
53 #include "nsICookieService.h"
54 #include "nsQueryObject.h"
55 #include "nsNetUtil.h"
56 #include "nsSandboxFlags.h"
57 #include "nsSerializationHelper.h"
58 #include "nsIBrowser.h"
59 #include "nsIEffectiveTLDService.h"
60 #include "nsIHttpsOnlyModePermission.h"
61 #include "nsIPromptCollection.h"
62 #include "nsITimer.h"
63 #include "nsITransportSecurityInfo.h"
64 #include "nsISharePicker.h"
65 #include "nsIURIMutator.h"
66 #include "nsIWebProgressListener.h"
67 #include "nsScriptSecurityManager.h"
68 #include "nsIOService.h"
70 #include "mozilla/dom/DOMException.h"
71 #include "mozilla/dom/DOMExceptionBinding.h"
73 #include "mozilla/dom/JSActorService.h"
74 #include "mozilla/dom/JSWindowActorBinding.h"
75 #include "mozilla/dom/JSWindowActorParent.h"
77 #include "mozilla/net/NeckoParent.h"
78 #include "mozilla/net/PCookieServiceParent.h"
79 #include "mozilla/net/CookieServiceParent.h"
81 #include "nsISessionStoreFunctions.h"
82 #include "nsIXPConnect.h"
83 #include "nsImportModule.h"
84 #include "nsIXULRuntime.h"
86 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
88 using namespace mozilla::ipc;
89 using namespace mozilla::dom::ipc;
91 extern mozilla::LazyLogModule gSHIPBFCacheLog;
92 extern mozilla::LazyLogModule gUseCountersLog;
94 namespace mozilla::dom {
96 WindowGlobalParent::WindowGlobalParent(
97 CanonicalBrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
98 uint64_t aOuterWindowId, FieldValues&& aInit)
99 : WindowContext(aBrowsingContext, aInnerWindowId, aOuterWindowId,
100 std::move(aInit)),
101 mSandboxFlags(0),
102 mDocumentHasLoaded(false),
103 mDocumentHasUserInteracted(false),
104 mBlockAllMixedContent(false),
105 mUpgradeInsecureRequests(false) {
106 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only");
109 already_AddRefed<WindowGlobalParent> WindowGlobalParent::CreateDisconnected(
110 const WindowGlobalInit& aInit) {
111 RefPtr<CanonicalBrowsingContext> browsingContext =
112 CanonicalBrowsingContext::Get(aInit.context().mBrowsingContextId);
113 if (NS_WARN_IF(!browsingContext)) {
114 return nullptr;
117 RefPtr<WindowGlobalParent> wgp =
118 GetByInnerWindowId(aInit.context().mInnerWindowId);
119 MOZ_RELEASE_ASSERT(!wgp, "Creating duplicate WindowGlobalParent");
121 FieldValues fields(aInit.context().mFields);
122 wgp =
123 new WindowGlobalParent(browsingContext, aInit.context().mInnerWindowId,
124 aInit.context().mOuterWindowId, std::move(fields));
125 wgp->mDocumentPrincipal = aInit.principal();
126 wgp->mDocumentURI = aInit.documentURI();
127 wgp->mIsInitialDocument = Some(aInit.isInitialDocument());
128 wgp->mBlockAllMixedContent = aInit.blockAllMixedContent();
129 wgp->mUpgradeInsecureRequests = aInit.upgradeInsecureRequests();
130 wgp->mSandboxFlags = aInit.sandboxFlags();
131 wgp->mHttpsOnlyStatus = aInit.httpsOnlyStatus();
132 wgp->mSecurityInfo = aInit.securityInfo();
133 net::CookieJarSettings::Deserialize(aInit.cookieJarSettings(),
134 getter_AddRefs(wgp->mCookieJarSettings));
135 MOZ_RELEASE_ASSERT(wgp->mDocumentPrincipal, "Must have a valid principal");
137 nsresult rv = wgp->SetDocumentStoragePrincipal(aInit.storagePrincipal());
138 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
139 "Must succeed in setting storage principal");
141 return wgp.forget();
144 void WindowGlobalParent::Init() {
145 MOZ_ASSERT(Manager(), "Should have a manager!");
147 // Invoke our base class' `Init` method. This will register us in
148 // `gWindowContexts`.
149 WindowContext::Init();
151 // Determine which content process the window global is coming from.
152 dom::ContentParentId processId(0);
153 ContentParent* cp = nullptr;
154 if (!IsInProcess()) {
155 cp = static_cast<ContentParent*>(Manager()->Manager());
156 processId = cp->ChildID();
158 // Ensure the content process has permissions for this principal.
159 cp->TransmitPermissionsForPrincipal(mDocumentPrincipal);
162 MOZ_DIAGNOSTIC_ASSERT(
163 !BrowsingContext()->GetParent() ||
164 BrowsingContext()->GetEmbedderInnerWindowId(),
165 "When creating a non-root WindowGlobalParent, the WindowGlobalParent "
166 "for our embedder should've already been created.");
168 // Ensure we have a document URI
169 if (!mDocumentURI) {
170 NS_NewURI(getter_AddRefs(mDocumentURI), "about:blank");
173 // NOTE: `cp` may be nullptr, but that's OK, we need to tell every other
174 // process in our group in that case.
175 IPCInitializer ipcinit = GetIPCInitializer();
176 Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
177 Unused << otherContent->SendCreateWindowContext(ipcinit);
180 if (!BrowsingContext()->IsDiscarded()) {
181 MOZ_ALWAYS_SUCCEEDS(
182 BrowsingContext()->SetCurrentInnerWindowId(InnerWindowId()));
184 Unused << SendSetContainerFeaturePolicy(
185 BrowsingContext()->GetContainerFeaturePolicy());
188 if (BrowsingContext()->IsTopContent()) {
189 // For top level sandboxed documents we need to create a new principal
190 // from URI + OriginAttributes, since the document principal will be a
191 // NullPrincipal. See Bug 1654546.
192 if (mSandboxFlags & SANDBOXED_ORIGIN) {
193 ContentBlockingAllowList::RecomputePrincipal(
194 mDocumentURI, mDocumentPrincipal->OriginAttributesRef(),
195 getter_AddRefs(mDocContentBlockingAllowListPrincipal));
196 } else {
197 ContentBlockingAllowList::ComputePrincipal(
198 mDocumentPrincipal,
199 getter_AddRefs(mDocContentBlockingAllowListPrincipal));
203 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
204 if (obs) {
205 obs->NotifyObservers(ToSupports(this), "window-global-created", nullptr);
208 if (!BrowsingContext()->IsDiscarded() && ShouldTrackSiteOriginTelemetry()) {
209 mOriginCounter.emplace();
210 mOriginCounter->UpdateSiteOriginsFrom(this, /* aIncrease = */ true);
214 void WindowGlobalParent::OriginCounter::UpdateSiteOriginsFrom(
215 WindowGlobalParent* aParent, bool aIncrease) {
216 MOZ_RELEASE_ASSERT(aParent);
218 if (aParent->DocumentPrincipal()->GetIsContentPrincipal()) {
219 nsAutoCString origin;
220 aParent->DocumentPrincipal()->GetSiteOrigin(origin);
222 if (aIncrease) {
223 int32_t& count = mOriginMap.LookupOrInsert(origin);
224 count += 1;
225 mMaxOrigins = std::max(mMaxOrigins, mOriginMap.Count());
226 } else if (auto entry = mOriginMap.Lookup(origin)) {
227 entry.Data() -= 1;
229 if (entry.Data() == 0) {
230 entry.Remove();
236 void WindowGlobalParent::OriginCounter::Accumulate() {
237 mozilla::glean::geckoview::per_document_site_origins.AccumulateSingleSample(
238 mMaxOrigins);
240 mMaxOrigins = 0;
241 mOriginMap.Clear();
244 /* static */
245 already_AddRefed<WindowGlobalParent> WindowGlobalParent::GetByInnerWindowId(
246 uint64_t aInnerWindowId) {
247 if (!XRE_IsParentProcess()) {
248 return nullptr;
251 return WindowContext::GetById(aInnerWindowId).downcast<WindowGlobalParent>();
254 already_AddRefed<WindowGlobalChild> WindowGlobalParent::GetChildActor() {
255 if (!CanSend()) {
256 return nullptr;
258 IProtocol* otherSide = InProcessParent::ChildActorFor(this);
259 return do_AddRef(static_cast<WindowGlobalChild*>(otherSide));
262 BrowserParent* WindowGlobalParent::GetBrowserParent() {
263 if (IsInProcess() || !CanSend()) {
264 return nullptr;
266 return static_cast<BrowserParent*>(Manager());
269 ContentParent* WindowGlobalParent::GetContentParent() {
270 if (IsInProcess() || !CanSend()) {
271 return nullptr;
273 return static_cast<ContentParent*>(Manager()->Manager());
276 already_AddRefed<nsFrameLoader> WindowGlobalParent::GetRootFrameLoader() {
277 dom::BrowsingContext* top = BrowsingContext()->Top();
279 RefPtr<nsFrameLoaderOwner> frameLoaderOwner =
280 do_QueryObject(top->GetEmbedderElement());
281 if (frameLoaderOwner) {
282 return frameLoaderOwner->GetFrameLoader();
284 return nullptr;
287 uint64_t WindowGlobalParent::ContentParentId() {
288 RefPtr<BrowserParent> browserParent = GetBrowserParent();
289 return browserParent ? browserParent->Manager()->ChildID() : 0;
292 int32_t WindowGlobalParent::OsPid() {
293 RefPtr<BrowserParent> browserParent = GetBrowserParent();
294 return browserParent ? browserParent->Manager()->Pid() : -1;
297 // A WindowGlobalPaernt is the root in its process if it has no parent, or its
298 // embedder is in a different process.
299 bool WindowGlobalParent::IsProcessRoot() {
300 if (!BrowsingContext()->GetParent()) {
301 return true;
304 RefPtr<WindowGlobalParent> embedder =
305 BrowsingContext()->GetEmbedderWindowGlobal();
306 if (NS_WARN_IF(!embedder)) {
307 return false;
310 return ContentParentId() != embedder->ContentParentId();
313 uint32_t WindowGlobalParent::ContentBlockingEvents() {
314 return GetContentBlockingLog()->GetContentBlockingEventsInLog();
317 void WindowGlobalParent::GetContentBlockingLog(nsAString& aLog) {
318 NS_ConvertUTF8toUTF16 log(GetContentBlockingLog()->Stringify());
319 aLog.Assign(std::move(log));
322 mozilla::ipc::IPCResult WindowGlobalParent::RecvLoadURI(
323 const MaybeDiscarded<dom::BrowsingContext>& aTargetBC,
324 nsDocShellLoadState* aLoadState, bool aSetNavigating) {
325 if (aTargetBC.IsNullOrDiscarded()) {
326 MOZ_LOG(
327 BrowsingContext::GetLog(), LogLevel::Debug,
328 ("ParentIPC: Trying to send a message with dead or detached context"));
329 return IPC_OK();
332 if (net::SchemeIsJavascript(aLoadState->URI())) {
333 return IPC_FAIL(this, "Illegal cross-process javascript: load attempt");
336 RefPtr<CanonicalBrowsingContext> targetBC = aTargetBC.get_canonical();
338 // FIXME: For cross-process loads, we should double check CanAccess() for the
339 // source browsing context in the parent process.
341 if (targetBC->Group() != BrowsingContext()->Group()) {
342 return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
345 // FIXME: We should really initiate the load in the parent before bouncing
346 // back down to the child.
348 targetBC->LoadURI(aLoadState, aSetNavigating);
349 return IPC_OK();
352 mozilla::ipc::IPCResult WindowGlobalParent::RecvInternalLoad(
353 nsDocShellLoadState* aLoadState) {
354 if (!aLoadState->Target().IsEmpty() ||
355 aLoadState->TargetBrowsingContext().IsNull()) {
356 return IPC_FAIL(this, "must already be retargeted");
358 if (aLoadState->TargetBrowsingContext().IsDiscarded()) {
359 MOZ_LOG(
360 BrowsingContext::GetLog(), LogLevel::Debug,
361 ("ParentIPC: Trying to send a message with dead or detached context"));
362 return IPC_OK();
365 if (net::SchemeIsJavascript(aLoadState->URI())) {
366 return IPC_FAIL(this, "Illegal cross-process javascript: load attempt");
369 RefPtr<CanonicalBrowsingContext> targetBC =
370 aLoadState->TargetBrowsingContext().get_canonical();
372 // FIXME: For cross-process loads, we should double check CanAccess() for the
373 // source browsing context in the parent process.
375 if (targetBC->Group() != BrowsingContext()->Group()) {
376 return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
379 // FIXME: We should really initiate the load in the parent before bouncing
380 // back down to the child.
382 targetBC->InternalLoad(aLoadState);
383 return IPC_OK();
386 IPCResult WindowGlobalParent::RecvUpdateDocumentURI(NotNull<nsIURI*> aURI) {
387 // XXX(nika): Assert that the URI change was one which makes sense (either
388 // about:blank -> a real URI, or a legal push/popstate URI change):
389 if (StaticPrefs::dom_security_setdocumenturi()) {
390 nsAutoCString scheme;
391 if (NS_FAILED(aURI->GetScheme(scheme))) {
392 return IPC_FAIL(this, "Setting DocumentURI without scheme.");
395 nsCOMPtr<nsIIOService> ios = do_GetIOService();
396 if (!ios) {
397 return IPC_FAIL(this, "Cannot get IOService");
399 nsCOMPtr<nsIProtocolHandler> handler;
400 ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
401 if (!handler) {
402 return IPC_FAIL(this, "Setting DocumentURI with unknown protocol.");
405 nsCOMPtr<nsIURI> principalURI = mDocumentPrincipal->GetURI();
406 if (mDocumentPrincipal->GetIsNullPrincipal()) {
407 nsCOMPtr<nsIPrincipal> precursor =
408 mDocumentPrincipal->GetPrecursorPrincipal();
409 if (precursor) {
410 principalURI = precursor->GetURI();
414 if (nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI,
415 aURI)) {
416 return IPC_FAIL(this,
417 "Setting DocumentURI with a different Origin than "
418 "principal URI");
422 mDocumentURI = aURI;
423 return IPC_OK();
426 nsresult WindowGlobalParent::SetDocumentStoragePrincipal(
427 nsIPrincipal* aNewDocumentStoragePrincipal) {
428 if (mDocumentPrincipal->Equals(aNewDocumentStoragePrincipal)) {
429 mDocumentStoragePrincipal = mDocumentPrincipal;
430 return NS_OK;
433 // Compare originNoSuffix to ensure it's equal.
434 nsCString noSuffix;
435 nsresult rv = mDocumentPrincipal->GetOriginNoSuffix(noSuffix);
436 if (NS_FAILED(rv)) {
437 return rv;
440 nsCString storageNoSuffix;
441 rv = aNewDocumentStoragePrincipal->GetOriginNoSuffix(storageNoSuffix);
442 if (NS_FAILED(rv)) {
443 return rv;
446 if (noSuffix != storageNoSuffix) {
447 return NS_ERROR_FAILURE;
450 if (!mDocumentPrincipal->OriginAttributesRef().EqualsIgnoringPartitionKey(
451 aNewDocumentStoragePrincipal->OriginAttributesRef())) {
452 return NS_ERROR_FAILURE;
455 mDocumentStoragePrincipal = aNewDocumentStoragePrincipal;
456 return NS_OK;
459 IPCResult WindowGlobalParent::RecvUpdateDocumentPrincipal(
460 nsIPrincipal* aNewDocumentPrincipal,
461 nsIPrincipal* aNewDocumentStoragePrincipal) {
462 if (!mDocumentPrincipal->Equals(aNewDocumentPrincipal)) {
463 return IPC_FAIL(this,
464 "Trying to reuse WindowGlobalParent but the principal of "
465 "the new document does not match the old one");
467 mDocumentPrincipal = aNewDocumentPrincipal;
469 if (NS_FAILED(SetDocumentStoragePrincipal(aNewDocumentStoragePrincipal))) {
470 return IPC_FAIL(this,
471 "Trying to reuse WindowGlobalParent but the principal of "
472 "the new document does not match the storage principal");
475 return IPC_OK();
477 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentTitle(
478 const nsString& aTitle) {
479 if (mDocumentTitle.isSome() && mDocumentTitle.value() == aTitle) {
480 return IPC_OK();
483 mDocumentTitle = Some(aTitle);
485 // Send a pagetitlechanged event only for changes to the title
486 // for top-level frames.
487 if (!BrowsingContext()->IsTop()) {
488 return IPC_OK();
491 // Notify media controller in order to update its default metadata.
492 if (BrowsingContext()->HasCreatedMediaController()) {
493 BrowsingContext()->GetMediaController()->NotifyPageTitleChanged();
496 Element* frameElement = BrowsingContext()->GetEmbedderElement();
497 if (!frameElement) {
498 return IPC_OK();
501 AsyncEventDispatcher::RunDOMEventWhenSafe(
502 *frameElement, u"pagetitlechanged"_ns, CanBubble::eYes,
503 ChromeOnlyDispatch::eYes);
505 return IPC_OK();
508 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateHttpsOnlyStatus(
509 uint32_t aHttpsOnlyStatus) {
510 mHttpsOnlyStatus = aHttpsOnlyStatus;
511 return IPC_OK();
514 IPCResult WindowGlobalParent::RecvUpdateDocumentHasLoaded(
515 bool aDocumentHasLoaded) {
516 mDocumentHasLoaded = aDocumentHasLoaded;
517 return IPC_OK();
520 IPCResult WindowGlobalParent::RecvUpdateDocumentHasUserInteracted(
521 bool aDocumentHasUserInteracted) {
522 mDocumentHasUserInteracted = aDocumentHasUserInteracted;
523 return IPC_OK();
526 IPCResult WindowGlobalParent::RecvUpdateSandboxFlags(uint32_t aSandboxFlags) {
527 mSandboxFlags = aSandboxFlags;
528 return IPC_OK();
531 IPCResult WindowGlobalParent::RecvUpdateDocumentCspSettings(
532 bool aBlockAllMixedContent, bool aUpgradeInsecureRequests) {
533 mBlockAllMixedContent = aBlockAllMixedContent;
534 mUpgradeInsecureRequests = aUpgradeInsecureRequests;
535 return IPC_OK();
538 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetClientInfo(
539 const IPCClientInfo& aIPCClientInfo) {
540 mClientInfo = Some(ClientInfo(aIPCClientInfo));
541 return IPC_OK();
544 IPCResult WindowGlobalParent::RecvDestroy() {
545 // Make a copy so that we can avoid potential iterator invalidation when
546 // calling the user-provided Destroy() methods.
547 JSActorWillDestroy();
549 if (CanSend()) {
550 RefPtr<BrowserParent> browserParent = GetBrowserParent();
551 if (!browserParent || !browserParent->IsDestroyed()) {
552 Unused << Send__delete__(this);
555 return IPC_OK();
558 IPCResult WindowGlobalParent::RecvRawMessage(
559 const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
560 const Maybe<ClonedMessageData>& aStack) {
561 Maybe<StructuredCloneData> data;
562 if (aData) {
563 data.emplace();
564 data->BorrowFromClonedMessageData(*aData);
566 Maybe<StructuredCloneData> stack;
567 if (aStack) {
568 stack.emplace();
569 stack->BorrowFromClonedMessageData(*aStack);
571 MMPrinter::Print("WindowGlobalParent::RecvRawMessage", aMeta.actorName(),
572 aMeta.messageName(), aData);
573 ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
574 return IPC_OK();
577 const nsACString& WindowGlobalParent::GetRemoteType() {
578 if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
579 return browserParent->Manager()->GetRemoteType();
582 return NOT_REMOTE_TYPE;
585 void WindowGlobalParent::NotifyContentBlockingEvent(
586 uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
587 const nsACString& aTrackingOrigin,
588 const nsTArray<nsCString>& aTrackingFullHashes,
589 const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
590 aReason,
591 const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
592 aCanvasFingerprinter,
593 const Maybe<bool> aCanvasFingerprinterKnownText) {
594 MOZ_ASSERT(NS_IsMainThread());
595 DebugOnly<bool> isCookiesBlocked =
596 aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
597 aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
598 MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
599 MOZ_ASSERT_IF(!isCookiesBlocked, aReason.isNothing());
600 MOZ_ASSERT_IF(isCookiesBlocked && !aBlocked, aReason.isSome());
601 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
602 // TODO: temporarily remove this until we find the root case of Bug 1609144
603 // MOZ_DIAGNOSTIC_ASSERT_IF(XRE_IsE10sParentProcess(), !IsInProcess());
605 // Return early if this WindowGlobalParent is in process.
606 if (IsInProcess()) {
607 return;
610 Maybe<uint32_t> event = GetContentBlockingLog()->RecordLogParent(
611 aTrackingOrigin, aEvent, aBlocked, aReason, aTrackingFullHashes,
612 aCanvasFingerprinter, aCanvasFingerprinterKnownText);
614 // Notify the OnContentBlockingEvent if necessary.
615 if (event) {
616 if (auto* webProgress = GetBrowsingContext()->GetWebProgress()) {
617 webProgress->OnContentBlockingEvent(webProgress, aRequest, event.value());
622 already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetActor(
623 JSContext* aCx, const nsACString& aName, ErrorResult& aRv) {
624 return JSActorManager::GetActor(aCx, aName, aRv)
625 .downcast<JSWindowActorParent>();
628 already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetExistingActor(
629 const nsACString& aName) {
630 return JSActorManager::GetExistingActor(aName)
631 .downcast<JSWindowActorParent>();
634 already_AddRefed<JSActor> WindowGlobalParent::InitJSActor(
635 JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
636 ErrorResult& aRv) {
637 RefPtr<JSWindowActorParent> actor;
638 if (aMaybeActor.get()) {
639 aRv = UNWRAP_OBJECT(JSWindowActorParent, aMaybeActor.get(), actor);
640 if (aRv.Failed()) {
641 return nullptr;
643 } else {
644 actor = new JSWindowActorParent();
647 MOZ_RELEASE_ASSERT(!actor->GetManager(),
648 "mManager was already initialized once!");
649 actor->Init(aName, this);
650 return actor.forget();
653 bool WindowGlobalParent::IsCurrentGlobal() {
654 if (mozilla::SessionHistoryInParent() && BrowsingContext() &&
655 BrowsingContext()->IsInBFCache()) {
656 return false;
659 return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() == this;
662 bool WindowGlobalParent::IsActiveInTab() {
663 if (!CanSend()) {
664 return false;
667 CanonicalBrowsingContext* bc = BrowsingContext();
668 if (!bc || bc->GetCurrentWindowGlobal() != this) {
669 return false;
672 // We check the top BC so we don't need to worry about getting a stale value.
673 // That may not be necessary.
674 MOZ_ASSERT(bc->Top()->IsInBFCache() == bc->IsInBFCache(),
675 "BFCache bit out of sync?");
676 return bc->AncestorsAreCurrent() && !bc->Top()->IsInBFCache();
679 namespace {
681 class ShareHandler final : public PromiseNativeHandler {
682 public:
683 explicit ShareHandler(
684 mozilla::dom::WindowGlobalParent::ShareResolver&& aResolver)
685 : mResolver(std::move(aResolver)) {}
687 NS_DECL_ISUPPORTS
689 public:
690 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
691 ErrorResult& aRv) override {
692 mResolver(NS_OK);
695 virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
696 ErrorResult& aRv) override {
697 if (NS_WARN_IF(!aValue.isObject())) {
698 mResolver(NS_ERROR_FAILURE);
699 return;
702 // nsresult is stored as Exception internally in Promise
703 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
704 RefPtr<DOMException> unwrapped;
705 nsresult rv = UNWRAP_OBJECT(DOMException, &obj, unwrapped);
706 if (NS_WARN_IF(NS_FAILED(rv))) {
707 mResolver(NS_ERROR_FAILURE);
708 return;
711 mResolver(unwrapped->GetResult());
714 private:
715 ~ShareHandler() = default;
717 mozilla::dom::WindowGlobalParent::ShareResolver mResolver;
720 NS_IMPL_ISUPPORTS0(ShareHandler)
722 } // namespace
724 mozilla::ipc::IPCResult WindowGlobalParent::RecvGetContentBlockingEvents(
725 WindowGlobalParent::GetContentBlockingEventsResolver&& aResolver) {
726 uint32_t events = GetContentBlockingLog()->GetContentBlockingEventsInLog();
727 aResolver(events);
729 return IPC_OK();
732 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateCookieJarSettings(
733 const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
734 net::CookieJarSettings::Deserialize(aCookieJarSettingsArgs,
735 getter_AddRefs(mCookieJarSettings));
736 return IPC_OK();
739 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentSecurityInfo(
740 nsITransportSecurityInfo* aSecurityInfo) {
741 mSecurityInfo = aSecurityInfo;
742 return IPC_OK();
745 mozilla::ipc::IPCResult WindowGlobalParent::RecvShare(
746 IPCWebShareData&& aData, WindowGlobalParent::ShareResolver&& aResolver) {
747 // Widget Layer handoff...
748 nsCOMPtr<nsISharePicker> sharePicker =
749 do_GetService("@mozilla.org/sharepicker;1");
750 if (!sharePicker) {
751 aResolver(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
752 return IPC_OK();
755 // Initialize the ShareWidget
756 RefPtr<BrowserParent> parent = GetBrowserParent();
757 nsCOMPtr<mozIDOMWindowProxy> openerWindow;
758 if (parent) {
759 openerWindow = parent->GetParentWindowOuter();
760 if (!openerWindow) {
761 aResolver(NS_ERROR_FAILURE);
762 return IPC_OK();
765 sharePicker->Init(openerWindow);
767 // And finally share the data...
768 RefPtr<Promise> promise;
769 nsresult rv = sharePicker->Share(aData.title(), aData.text(), aData.url(),
770 getter_AddRefs(promise));
771 if (NS_FAILED(rv)) {
772 aResolver(rv);
773 return IPC_OK();
776 // Handler finally awaits response...
777 RefPtr<ShareHandler> handler = new ShareHandler(std::move(aResolver));
778 promise->AppendNativeHandler(handler);
780 return IPC_OK();
783 namespace {
785 class CheckPermitUnloadRequest final : public PromiseNativeHandler,
786 public nsITimerCallback {
787 public:
788 CheckPermitUnloadRequest(WindowGlobalParent* aWGP, bool aHasInProcessBlocker,
789 nsIDocumentViewer::PermitUnloadAction aAction,
790 std::function<void(bool)>&& aResolver)
791 : mResolver(std::move(aResolver)),
792 mWGP(aWGP),
793 mAction(aAction),
794 mFoundBlocker(aHasInProcessBlocker) {}
796 void Run(ContentParent* aIgnoreProcess = nullptr, uint32_t aTimeout = 0) {
797 MOZ_ASSERT(mState == State::UNINITIALIZED);
798 mState = State::WAITING;
800 RefPtr<CheckPermitUnloadRequest> self(this);
802 AutoTArray<ContentParent*, 8> seen;
803 if (aIgnoreProcess) {
804 seen.AppendElement(aIgnoreProcess);
807 BrowsingContext* bc = mWGP->GetBrowsingContext();
808 bc->PreOrderWalk([&](dom::BrowsingContext* aBC) {
809 if (WindowGlobalParent* wgp =
810 aBC->Canonical()->GetCurrentWindowGlobal()) {
811 ContentParent* cp = wgp->GetContentParent();
812 if (wgp->HasBeforeUnload() && !seen.ContainsSorted(cp)) {
813 seen.InsertElementSorted(cp);
814 mPendingRequests++;
815 auto resolve = [self](bool blockNavigation) {
816 if (blockNavigation) {
817 self->mFoundBlocker = true;
819 self->ResolveRequest();
821 if (cp) {
822 cp->SendDispatchBeforeUnloadToSubtree(
823 bc, resolve, [self](auto) { self->ResolveRequest(); });
824 } else {
825 ContentChild::DispatchBeforeUnloadToSubtree(bc, resolve);
831 if (mPendingRequests && aTimeout) {
832 Unused << NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aTimeout,
833 nsITimer::TYPE_ONE_SHOT);
836 CheckDoneWaiting();
839 void ResolveRequest() {
840 mPendingRequests--;
841 CheckDoneWaiting();
844 NS_IMETHODIMP Notify(nsITimer* aTimer) override {
845 MOZ_ASSERT(aTimer == mTimer);
846 if (mState == State::WAITING) {
847 mState = State::TIMED_OUT;
848 CheckDoneWaiting();
850 return NS_OK;
853 void CheckDoneWaiting() {
854 // If we've found a blocker, we prompt immediately without waiting for
855 // further responses. The user's response applies to the entire navigation
856 // attempt, regardless of how many "beforeunload" listeners we call.
857 if (mState != State::TIMED_OUT &&
858 (mState != State::WAITING || (mPendingRequests && !mFoundBlocker))) {
859 return;
862 mState = State::PROMPTING;
864 // Clearing our reference to the timer will automatically cancel it if it's
865 // still running.
866 mTimer = nullptr;
868 if (!mFoundBlocker) {
869 SendReply(true);
870 return;
873 auto action = mAction;
874 if (StaticPrefs::dom_disable_beforeunload()) {
875 action = nsIDocumentViewer::eDontPromptAndUnload;
877 if (action != nsIDocumentViewer::ePrompt) {
878 SendReply(action == nsIDocumentViewer::eDontPromptAndUnload);
879 return;
882 // Handle any failure in prompting by aborting the navigation. See comment
883 // in nsContentViewer::PermitUnload for reasoning.
884 auto cleanup = MakeScopeExit([&]() { SendReply(false); });
886 if (nsCOMPtr<nsIPromptCollection> prompt =
887 do_GetService("@mozilla.org/embedcomp/prompt-collection;1")) {
888 RefPtr<Promise> promise;
889 prompt->AsyncBeforeUnloadCheck(mWGP->GetBrowsingContext(),
890 getter_AddRefs(promise));
892 if (!promise) {
893 return;
896 promise->AppendNativeHandler(this);
897 cleanup.release();
901 void SendReply(bool aAllow) {
902 MOZ_ASSERT(mState != State::REPLIED);
903 mResolver(aAllow);
904 mState = State::REPLIED;
907 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
908 ErrorResult& aRv) override {
909 MOZ_ASSERT(mState == State::PROMPTING);
911 SendReply(JS::ToBoolean(aValue));
914 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
915 ErrorResult& aRv) override {
916 MOZ_ASSERT(mState == State::PROMPTING);
918 SendReply(false);
921 NS_DECL_ISUPPORTS
923 private:
924 ~CheckPermitUnloadRequest() {
925 // We may get here without having sent a reply if the promise we're waiting
926 // on is destroyed without being resolved or rejected.
927 if (mState != State::REPLIED) {
928 SendReply(false);
932 enum class State : uint8_t {
933 UNINITIALIZED,
934 WAITING,
935 TIMED_OUT,
936 PROMPTING,
937 REPLIED,
940 std::function<void(bool)> mResolver;
942 RefPtr<WindowGlobalParent> mWGP;
943 nsCOMPtr<nsITimer> mTimer;
945 uint32_t mPendingRequests = 0;
947 nsIDocumentViewer::PermitUnloadAction mAction;
949 State mState = State::UNINITIALIZED;
951 bool mFoundBlocker = false;
954 NS_IMPL_ISUPPORTS(CheckPermitUnloadRequest, nsITimerCallback)
956 } // namespace
958 mozilla::ipc::IPCResult WindowGlobalParent::RecvCheckPermitUnload(
959 bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
960 CheckPermitUnloadResolver&& aResolver) {
961 if (!IsCurrentGlobal()) {
962 aResolver(false);
963 return IPC_OK();
966 auto request = MakeRefPtr<CheckPermitUnloadRequest>(
967 this, aHasInProcessBlocker, aAction, std::move(aResolver));
968 request->Run(/* aIgnoreProcess */ GetContentParent());
970 return IPC_OK();
973 already_AddRefed<Promise> WindowGlobalParent::PermitUnload(
974 PermitUnloadAction aAction, uint32_t aTimeout, mozilla::ErrorResult& aRv) {
975 nsIGlobalObject* global = GetParentObject();
976 RefPtr<Promise> promise = Promise::Create(global, aRv);
977 if (NS_WARN_IF(aRv.Failed())) {
978 return nullptr;
981 auto request = MakeRefPtr<CheckPermitUnloadRequest>(
982 this, /* aHasInProcessBlocker */ false,
983 nsIDocumentViewer::PermitUnloadAction(aAction),
984 [promise](bool aAllow) { promise->MaybeResolve(aAllow); });
985 request->Run(/* aIgnoreProcess */ nullptr, aTimeout);
987 return promise.forget();
990 void WindowGlobalParent::PermitUnload(std::function<void(bool)>&& aResolver) {
991 RefPtr<CheckPermitUnloadRequest> request = new CheckPermitUnloadRequest(
992 this, /* aHasInProcessBlocker */ false,
993 nsIDocumentViewer::PermitUnloadAction::ePrompt, std::move(aResolver));
994 request->Run();
997 already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
998 const DOMRect* aRect, double aScale, const nsACString& aBackgroundColor,
999 bool aResetScrollPosition, mozilla::ErrorResult& aRv) {
1000 nsIGlobalObject* global = GetParentObject();
1001 RefPtr<Promise> promise = Promise::Create(global, aRv);
1002 if (NS_WARN_IF(aRv.Failed())) {
1003 return nullptr;
1006 nscolor color;
1007 if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0),
1008 aBackgroundColor, &color,
1009 nullptr, nullptr))) {
1010 aRv = NS_ERROR_FAILURE;
1011 return nullptr;
1014 gfx::CrossProcessPaintFlags flags =
1015 gfx::CrossProcessPaintFlags::UseHighQualityScaling;
1016 if (!aRect) {
1017 // If no explicit Rect was passed, we want the currently visible viewport.
1018 flags |= gfx::CrossProcessPaintFlags::DrawView;
1019 } else if (aResetScrollPosition) {
1020 flags |= gfx::CrossProcessPaintFlags::ResetScrollPosition;
1023 if (!gfx::CrossProcessPaint::Start(this, aRect, (float)aScale, color, flags,
1024 promise)) {
1025 aRv = NS_ERROR_FAILURE;
1026 return nullptr;
1028 return promise.forget();
1031 void WindowGlobalParent::DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
1032 const Maybe<IntRect>& aRect,
1033 float aScale,
1034 nscolor aBackgroundColor,
1035 uint32_t aFlags) {
1036 auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor, aFlags);
1038 RefPtr<gfx::CrossProcessPaint> paint(aPaint);
1039 RefPtr<WindowGlobalParent> wgp(this);
1040 promise->Then(
1041 GetMainThreadSerialEventTarget(), __func__,
1042 [paint, wgp](PaintFragment&& aFragment) {
1043 paint->ReceiveFragment(wgp, std::move(aFragment));
1045 [paint, wgp](ResponseRejectReason&& aReason) {
1046 paint->LostFragment(wgp);
1051 * Accumulated page use counter data for a given top-level content document.
1053 struct PageUseCounters {
1054 // The number of page use counter data messages we are still waiting for.
1055 uint32_t mWaiting = 0;
1057 // Whether we have received any page use counter data.
1058 bool mReceivedAny = false;
1060 // The accumulated page use counters.
1061 UseCounters mUseCounters;
1064 mozilla::ipc::IPCResult WindowGlobalParent::RecvExpectPageUseCounters(
1065 const MaybeDiscarded<WindowContext>& aTop) {
1066 if (aTop.IsNull()) {
1067 return IPC_FAIL(this, "aTop must not be null");
1070 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1071 ("Expect page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
1072 InnerWindowId(), aTop.ContextId()));
1074 // We've been called to indicate that the document in our window intends
1075 // to send use counter data to accumulate towards the top-level document's
1076 // page use counters. This causes us to wait for this window to go away
1077 // (in WindowGlobalParent::ActorDestroy) before reporting the page use
1078 // counters via Telemetry.
1079 RefPtr<WindowGlobalParent> page =
1080 static_cast<WindowGlobalParent*>(aTop.GetMaybeDiscarded());
1081 if (!page || page->mSentPageUseCounters) {
1082 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1083 (" > too late, won't report page use counters for this straggler"));
1084 return IPC_OK();
1087 if (mPageUseCountersWindow) {
1088 if (mPageUseCountersWindow != page) {
1089 return IPC_FAIL(this,
1090 "ExpectPageUseCounters called on the same "
1091 "WindowContext with a different aTop value");
1094 // We can get called with the same aTop value more than once, e.g. for
1095 // initial about:blank documents and then subsequent "real" documents loaded
1096 // into the same window. We must note each source window only once.
1097 return IPC_OK();
1100 // Note that the top-level document must wait for one more window's use
1101 // counters before reporting via Telemetry.
1102 mPageUseCountersWindow = page;
1103 if (!page->mPageUseCounters) {
1104 page->mPageUseCounters = MakeUnique<PageUseCounters>();
1106 ++page->mPageUseCounters->mWaiting;
1108 MOZ_LOG(
1109 gUseCountersLog, LogLevel::Debug,
1110 (" > top-level now waiting on %d\n", page->mPageUseCounters->mWaiting));
1112 return IPC_OK();
1115 mozilla::ipc::IPCResult WindowGlobalParent::RecvAccumulatePageUseCounters(
1116 const UseCounters& aUseCounters) {
1117 // We've been called to accumulate use counter data into the page use counters
1118 // for the document in mPageUseCountersWindow.
1120 MOZ_LOG(
1121 gUseCountersLog, LogLevel::Debug,
1122 ("Accumulate page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
1123 InnerWindowId(),
1124 mPageUseCountersWindow ? mPageUseCountersWindow->InnerWindowId() : 0));
1126 if (!mPageUseCountersWindow || mPageUseCountersWindow->mSentPageUseCounters) {
1127 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1128 (" > too late, won't report page use counters for this straggler"));
1129 return IPC_OK();
1132 MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters);
1133 MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters->mWaiting > 0);
1135 mPageUseCountersWindow->mPageUseCounters->mUseCounters |= aUseCounters;
1136 mPageUseCountersWindow->mPageUseCounters->mReceivedAny = true;
1137 return IPC_OK();
1140 // This is called on the top-level WindowGlobal, i.e. the one that is
1141 // accumulating the page use counters, not the (potentially descendant) window
1142 // that has finished providing use counter data.
1143 void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
1144 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1145 ("Stop expecting page use counters: -> WindowContext %" PRIu64,
1146 InnerWindowId()));
1148 if (!mPageUseCounters) {
1149 MOZ_ASSERT_UNREACHABLE("Not expecting page use counter data");
1150 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1151 (" > not expecting page use counter data"));
1152 return;
1155 MOZ_ASSERT(mPageUseCounters->mWaiting > 0);
1156 --mPageUseCounters->mWaiting;
1158 if (mPageUseCounters->mWaiting > 0) {
1159 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1160 (" > now waiting on %d", mPageUseCounters->mWaiting));
1161 return;
1164 if (mPageUseCounters->mReceivedAny) {
1165 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1166 (" > reporting [%s]",
1167 nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
1169 Maybe<nsCString> urlForLogging;
1170 const bool dumpCounters = StaticPrefs::dom_use_counters_dump_page();
1171 if (dumpCounters) {
1172 urlForLogging.emplace(
1173 nsContentUtils::TruncatedURLForDisplay(mDocumentURI));
1176 glean::use_counter::top_level_content_documents_destroyed.Add();
1178 bool any = false;
1179 for (int32_t c = 0; c < eUseCounter_Count; ++c) {
1180 auto uc = static_cast<UseCounter>(c);
1181 if (!mPageUseCounters->mUseCounters[uc]) {
1182 continue;
1184 any = true;
1185 const char* metricName = IncrementUseCounter(uc, /* aIsPage = */ true);
1186 if (dumpCounters) {
1187 printf_stderr("USE_COUNTER_PAGE: %s - %s\n", metricName,
1188 urlForLogging->get());
1192 if (!any) {
1193 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1194 (" > page use counter data was received, but was empty"));
1196 } else {
1197 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1198 (" > no page use counter data was received"));
1201 mSentPageUseCounters = true;
1202 mPageUseCounters = nullptr;
1205 Element* WindowGlobalParent::GetRootOwnerElement() {
1206 WindowGlobalParent* top = TopWindowContext();
1207 if (!top) {
1208 return nullptr;
1211 if (IsInProcess()) {
1212 return top->BrowsingContext()->GetEmbedderElement();
1215 if (BrowserParent* parent = top->GetBrowserParent()) {
1216 return parent->GetOwnerElement();
1219 return nullptr;
1222 void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) {
1223 if (!aEmbedder) {
1224 aEmbedder = GetRootOwnerElement();
1226 if (aEmbedder) {
1227 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1228 obs->NotifyWhenScriptSafe(ToSupports(aEmbedder),
1229 "browser-shutdown-tabstate-updated", nullptr);
1234 mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() {
1235 CanonicalBrowsingContext* bc = BrowsingContext();
1236 if (bc && bc->AncestorsAreCurrent()) {
1237 bc->Top()->RequestRestoreTabContent(this);
1239 return IPC_OK();
1242 nsCString BFCacheStatusToString(uint32_t aFlags) {
1243 if (aFlags == 0) {
1244 return "0"_ns;
1247 nsCString flags;
1248 #define ADD_BFCACHESTATUS_TO_STRING(_flag) \
1249 if (aFlags & BFCacheStatus::_flag) { \
1250 if (!flags.IsEmpty()) { \
1251 flags.Append('|'); \
1253 flags.AppendLiteral(#_flag); \
1254 aFlags &= ~BFCacheStatus::_flag; \
1257 ADD_BFCACHESTATUS_TO_STRING(NOT_ALLOWED);
1258 ADD_BFCACHESTATUS_TO_STRING(EVENT_HANDLING_SUPPRESSED);
1259 ADD_BFCACHESTATUS_TO_STRING(SUSPENDED);
1260 ADD_BFCACHESTATUS_TO_STRING(UNLOAD_LISTENER);
1261 ADD_BFCACHESTATUS_TO_STRING(REQUEST);
1262 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_GET_USER_MEDIA);
1263 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_PEER_CONNECTION);
1264 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_EME_CONTENT);
1265 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_MSE_CONTENT);
1266 ADD_BFCACHESTATUS_TO_STRING(HAS_ACTIVE_SPEECH_SYNTHESIS);
1267 ADD_BFCACHESTATUS_TO_STRING(HAS_USED_VR);
1268 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_REMOTE_SUBFRAMES);
1269 ADD_BFCACHESTATUS_TO_STRING(NOT_ONLY_TOPLEVEL_IN_BCG);
1270 ADD_BFCACHESTATUS_TO_STRING(BEFOREUNLOAD_LISTENER);
1271 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_LOCK);
1273 #undef ADD_BFCACHESTATUS_TO_STRING
1275 MOZ_ASSERT(aFlags == 0,
1276 "Missing stringification for enum value in BFCacheStatus.");
1277 return flags;
1280 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateBFCacheStatus(
1281 const uint32_t& aOnFlags, const uint32_t& aOffFlags) {
1282 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) {
1283 nsAutoCString uri("[no uri]");
1284 if (mDocumentURI) {
1285 uri = mDocumentURI->GetSpecOrDefault();
1287 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
1288 ("Setting BFCache flags for %s +(%s) -(%s)", uri.get(),
1289 BFCacheStatusToString(aOnFlags).get(),
1290 BFCacheStatusToString(aOffFlags).get()));
1292 mBFCacheStatus |= aOnFlags;
1293 mBFCacheStatus &= ~aOffFlags;
1294 return IPC_OK();
1297 mozilla::ipc::IPCResult
1298 WindowGlobalParent::RecvUpdateActivePeerConnectionStatus(bool aIsAdded) {
1299 if (aIsAdded) {
1300 RecvUpdateBFCacheStatus(BFCacheStatus::ACTIVE_PEER_CONNECTION, 0);
1301 } else {
1302 RecvUpdateBFCacheStatus(0, BFCacheStatus::ACTIVE_PEER_CONNECTION);
1305 if (WindowGlobalParent* top = TopWindowContext()) {
1306 CheckedUint32 newValue(top->mNumOfProcessesWithActivePeerConnections);
1307 if (aIsAdded) {
1308 ++newValue;
1309 } else {
1310 --newValue;
1312 if (!newValue.isValid()) {
1313 return IPC_FAIL(this,
1314 "mNumOfProcessesWithActivePeerConnections overflowed");
1317 top->mNumOfProcessesWithActivePeerConnections = newValue.value();
1318 Unused << top->SetHasActivePeerConnections(newValue.value() > 0);
1321 return IPC_OK();
1324 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetSingleChannelId(
1325 const Maybe<uint64_t>& aSingleChannelId) {
1326 mSingleChannelId = aSingleChannelId;
1327 return IPC_OK();
1330 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetDocumentDomain(
1331 NotNull<nsIURI*> aDomain) {
1332 if (mSandboxFlags & SANDBOXED_DOMAIN) {
1333 // We're sandboxed; disallow setting domain
1334 return IPC_FAIL(this, "Sandbox disallows domain setting.");
1337 // Might need to do a featurepolicy check here, like we currently do in the
1338 // child process?
1340 nsCOMPtr<nsIURI> uri;
1341 mDocumentPrincipal->GetDomain(getter_AddRefs(uri));
1342 if (!uri) {
1343 uri = mDocumentPrincipal->GetURI();
1344 if (!uri) {
1345 return IPC_OK();
1349 if (!Document::IsValidDomain(uri, aDomain)) {
1350 // Error: illegal domain
1351 return IPC_FAIL(
1352 this, "Setting domain that's not a suffix of existing domain value.");
1355 if (Group()->IsPotentiallyCrossOriginIsolated()) {
1356 return IPC_FAIL(this, "Setting domain in a cross-origin isolated BC.");
1359 mDocumentPrincipal->SetDomain(aDomain);
1360 return IPC_OK();
1363 mozilla::ipc::IPCResult WindowGlobalParent::RecvReloadWithHttpsOnlyException() {
1364 nsresult rv;
1365 nsCOMPtr<nsIURI> currentUri = BrowsingContext()->Top()->GetCurrentURI();
1367 if (!currentUri) {
1368 return IPC_FAIL(this, "HTTPS-only mode: Failed to get current URI");
1371 bool isViewSource = currentUri->SchemeIs("view-source");
1373 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentUri);
1374 nsCOMPtr<nsIURI> innerURI;
1375 if (isViewSource) {
1376 nestedURI->GetInnerURI(getter_AddRefs(innerURI));
1377 } else {
1378 innerURI = currentUri;
1381 if (!innerURI->SchemeIs("https") && !innerURI->SchemeIs("http")) {
1382 return IPC_FAIL(this, "HTTPS-only mode: Illegal state");
1385 // If the error page is within an iFrame, we create an exception for whatever
1386 // scheme the top-level site is currently on, because the user wants to
1387 // unbreak the iFrame and not the top-level page. When the error page shows up
1388 // on a top-level request, then we replace the scheme with http, because the
1389 // user wants to unbreak the whole page.
1390 nsCOMPtr<nsIURI> newURI;
1391 if (!BrowsingContext()->IsTop()) {
1392 newURI = innerURI;
1393 } else {
1394 Unused << NS_MutateURI(innerURI).SetScheme("http"_ns).Finalize(
1395 getter_AddRefs(newURI));
1398 OriginAttributes originAttributes =
1399 TopWindowContext()->DocumentPrincipal()->OriginAttributesRef();
1401 originAttributes.SetFirstPartyDomain(true, newURI);
1403 nsCOMPtr<nsIPermissionManager> permMgr =
1404 components::PermissionManager::Service();
1405 if (!permMgr) {
1406 return IPC_FAIL(
1407 this, "HTTPS-only mode: Failed to get Permission Manager service");
1410 nsCOMPtr<nsIPrincipal> principal =
1411 BasePrincipal::CreateContentPrincipal(newURI, originAttributes);
1413 rv = permMgr->AddFromPrincipal(
1414 principal, "https-only-load-insecure"_ns,
1415 nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION,
1416 nsIPermissionManager::EXPIRE_SESSION, 0);
1418 if (NS_FAILED(rv)) {
1419 return IPC_FAIL(
1420 this, "HTTPS-only mode: Failed to add permission to the principal");
1423 nsCOMPtr<nsIURI> insecureURI = newURI;
1424 if (isViewSource) {
1425 nsAutoCString spec;
1426 MOZ_ALWAYS_SUCCEEDS(newURI->GetSpec(spec));
1427 if (NS_FAILED(
1428 NS_NewURI(getter_AddRefs(insecureURI), "view-source:"_ns + spec))) {
1429 return IPC_FAIL(
1430 this, "HTTPS-only mode: Failed to re-construct view-source URI");
1434 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(insecureURI);
1435 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
1436 loadState->SetLoadType(LOAD_NORMAL_REPLACE);
1438 RefPtr<CanonicalBrowsingContext> topBC = BrowsingContext()->Top();
1439 topBC->LoadURI(loadState, /* setNavigating */ true);
1441 return IPC_OK();
1444 IPCResult WindowGlobalParent::RecvDiscoverIdentityCredentialFromExternalSource(
1445 const IdentityCredentialRequestOptions& aOptions,
1446 const DiscoverIdentityCredentialFromExternalSourceResolver& aResolver) {
1447 IdentityCredential::DiscoverFromExternalSourceInMainProcess(
1448 DocumentPrincipal(), this->BrowsingContext(), aOptions)
1449 ->Then(
1450 GetCurrentSerialEventTarget(), __func__,
1451 [aResolver](const IPCIdentityCredential& aResult) {
1452 return aResolver(Some(aResult));
1454 [aResolver](nsresult aErr) { aResolver(Nothing()); });
1455 return IPC_OK();
1458 IPCResult WindowGlobalParent::RecvCollectIdentityCredentialFromCredentialStore(
1459 const IdentityCredentialRequestOptions& aOptions,
1460 const CollectIdentityCredentialFromCredentialStoreResolver& aResolver) {
1461 IdentityCredential::CollectFromCredentialStoreInMainProcess(
1462 DocumentPrincipal(), BrowsingContext(), aOptions)
1463 ->Then(
1464 GetCurrentSerialEventTarget(), __func__,
1465 [aResolver](const nsTArray<IPCIdentityCredential>& aResult) {
1466 aResolver(aResult);
1468 [aResolver](nsresult aErr) {
1469 aResolver(nsTArray<IPCIdentityCredential>());
1471 return IPC_OK();
1474 IPCResult WindowGlobalParent::RecvStoreIdentityCredential(
1475 const IPCIdentityCredential& aCredential,
1476 const StoreIdentityCredentialResolver& aResolver) {
1477 IdentityCredential::StoreInMainProcess(DocumentPrincipal(), aCredential)
1478 ->Then(
1479 GetCurrentSerialEventTarget(), __func__,
1480 [aResolver](const bool& aResult) { aResolver(NS_OK); },
1481 [aResolver](nsresult aErr) { aResolver(aErr); });
1482 return IPC_OK();
1485 IPCResult WindowGlobalParent::RecvNotifyPendingIdentityCredentialDiscovery(
1486 const IdentityCredentialRequestOptions& aOptions,
1487 const NotifyPendingIdentityCredentialDiscoveryResolver& aResolver) {
1488 IdentityCredentialRequestManager* icrm =
1489 IdentityCredentialRequestManager::GetInstance();
1490 if (!icrm) {
1491 aResolver(NS_ERROR_NOT_AVAILABLE);
1492 return IPC_OK();
1494 nsresult rv =
1495 icrm->StorePendingRequest(this->TopWindowContext()->DocumentPrincipal(),
1496 aOptions, this->InnerWindowId());
1497 aResolver(rv);
1498 return IPC_OK();
1501 IPCResult WindowGlobalParent::RecvGetStorageAccessPermission(
1502 GetStorageAccessPermissionResolver&& aResolve) {
1503 WindowGlobalParent* top = TopWindowContext();
1504 if (!top) {
1505 return IPC_FAIL_NO_REASON(this);
1507 nsIPrincipal* topPrincipal = top->DocumentPrincipal();
1508 nsIPrincipal* principal = DocumentPrincipal();
1509 uint32_t result;
1510 nsresult rv = AntiTrackingUtils::TestStoragePermissionInParent(
1511 topPrincipal, principal, &result);
1512 if (NS_WARN_IF(NS_FAILED(rv))) {
1513 aResolve(nsIPermissionManager::UNKNOWN_ACTION);
1514 return IPC_OK();
1517 aResolve(result);
1518 return IPC_OK();
1521 void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
1522 if (GetBrowsingContext()->IsTopContent()) {
1523 Telemetry::Accumulate(Telemetry::ORB_DID_EVER_BLOCK_RESPONSE,
1524 mShouldReportHasBlockedOpaqueResponse);
1527 if (mPageUseCountersWindow) {
1528 mPageUseCountersWindow->FinishAccumulatingPageUseCounters();
1529 mPageUseCountersWindow = nullptr;
1532 if (GetBrowsingContext()->IsTopContent() &&
1533 !mDocumentPrincipal->SchemeIs("about")) {
1534 // Record the page load
1535 uint32_t pageLoaded = 1;
1536 Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1538 // Record the mixed content status of the docshell in Telemetry
1539 enum {
1540 NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1541 MIXED_DISPLAY_CONTENT =
1542 1, // The page attempted to load Mixed Display Content
1543 MIXED_ACTIVE_CONTENT =
1544 2, // The page attempted to load Mixed Active Content
1545 MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed
1546 // Display & Mixed Active Content
1549 bool hasMixedDisplay =
1550 mSecurityState &
1551 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
1552 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
1553 bool hasMixedActive =
1554 mSecurityState &
1555 (nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
1556 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
1558 uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1559 if (hasMixedDisplay && hasMixedActive) {
1560 mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1561 } else if (hasMixedActive) {
1562 mixedContentLevel = MIXED_ACTIVE_CONTENT;
1563 } else if (hasMixedDisplay) {
1564 mixedContentLevel = MIXED_DISPLAY_CONTENT;
1566 Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1568 if (GetDocTreeHadMedia()) {
1569 ScalarAdd(Telemetry::ScalarID::MEDIA_ELEMENT_IN_PAGE_COUNT, 1);
1573 ContentParent* cp = nullptr;
1574 if (!IsInProcess()) {
1575 cp = static_cast<ContentParent*>(Manager()->Manager());
1578 Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
1579 // Keep the WindowContext and our BrowsingContextGroup alive until other
1580 // processes have acknowledged it has been discarded.
1581 Group()->AddKeepAlive();
1582 auto callback = [self = RefPtr{this}](auto) {
1583 self->Group()->RemoveKeepAlive();
1585 otherContent->SendDiscardWindowContext(InnerWindowId(), callback, callback);
1588 // Note that our WindowContext has become discarded.
1589 WindowContext::Discard();
1591 // Report content blocking log when destroyed.
1592 // There shouldn't have any content blocking log when a document is loaded in
1593 // the parent process(See NotifyContentBlockingEvent), so we could skip
1594 // reporting log when it is in-process.
1595 if (!IsInProcess()) {
1596 RefPtr<BrowserParent> browserParent =
1597 static_cast<BrowserParent*>(Manager());
1598 if (browserParent) {
1599 nsCOMPtr<nsILoadContext> loadContext = browserParent->GetLoadContext();
1600 if (loadContext && !loadContext->UsePrivateBrowsing() &&
1601 BrowsingContext()->IsTopContent()) {
1602 GetContentBlockingLog()->ReportLog();
1604 if (mDocumentURI && (net::SchemeIsHTTP(mDocumentURI) ||
1605 net::SchemeIsHTTPS(mDocumentURI))) {
1606 GetContentBlockingLog()->ReportCanvasFingerprintingLog(
1607 DocumentPrincipal());
1608 GetContentBlockingLog()->ReportFontFingerprintingLog(
1609 DocumentPrincipal());
1610 GetContentBlockingLog()->ReportEmailTrackingLog(DocumentPrincipal());
1616 // Destroy our JSWindowActors, and reject any pending queries.
1617 JSActorDidDestroy();
1619 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1620 if (obs) {
1621 obs->NotifyObservers(ToSupports(this), "window-global-destroyed", nullptr);
1624 if (mOriginCounter) {
1625 mOriginCounter->Accumulate();
1629 WindowGlobalParent::~WindowGlobalParent() = default;
1631 JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
1632 JS::Handle<JSObject*> aGivenProto) {
1633 return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
1636 nsIGlobalObject* WindowGlobalParent::GetParentObject() {
1637 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
1640 nsIDOMProcessParent* WindowGlobalParent::GetDomProcess() {
1641 if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
1642 return browserParent->Manager();
1644 return InProcessParent::Singleton();
1647 void WindowGlobalParent::DidBecomeCurrentWindowGlobal(bool aCurrent) {
1648 WindowGlobalParent* top = BrowsingContext()->GetTopWindowContext();
1649 if (top && top->mOriginCounter) {
1650 top->mOriginCounter->UpdateSiteOriginsFrom(this,
1651 /* aIncrease = */ aCurrent);
1654 if (!aCurrent && Fullscreen()) {
1655 ExitTopChromeDocumentFullscreen();
1659 bool WindowGlobalParent::ShouldTrackSiteOriginTelemetry() {
1660 CanonicalBrowsingContext* bc = BrowsingContext();
1662 if (!bc->IsTopContent()) {
1663 return false;
1666 RefPtr<BrowserParent> browserParent = GetBrowserParent();
1667 if (!browserParent ||
1668 !IsWebRemoteType(browserParent->Manager()->GetRemoteType())) {
1669 return false;
1672 return DocumentPrincipal()->GetIsContentPrincipal();
1675 void WindowGlobalParent::AddSecurityState(uint32_t aStateFlags) {
1676 MOZ_ASSERT(TopWindowContext() == this);
1677 MOZ_ASSERT((aStateFlags &
1678 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
1679 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
1680 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT |
1681 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT |
1682 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED |
1683 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED |
1684 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED_FIRST)) ==
1685 aStateFlags,
1686 "Invalid flags specified!");
1688 if ((mSecurityState & aStateFlags) == aStateFlags) {
1689 return;
1692 mSecurityState |= aStateFlags;
1694 if (GetBrowsingContext()->GetCurrentWindowGlobal() == this) {
1695 GetBrowsingContext()->UpdateSecurityState();
1699 bool WindowGlobalParent::HasActivePeerConnections() {
1700 MOZ_ASSERT(TopWindowContext() == this,
1701 "mNumOfProcessesWithActivePeerConnections is set only "
1702 "in the top window context");
1703 return mNumOfProcessesWithActivePeerConnections > 0;
1706 void WindowGlobalParent::ExitTopChromeDocumentFullscreen() {
1707 RefPtr<CanonicalBrowsingContext> chromeTop =
1708 BrowsingContext()->TopCrossChromeBoundary();
1709 if (Document* chromeDoc = chromeTop->GetDocument()) {
1710 Document::ClearPendingFullscreenRequests(chromeDoc);
1711 if (chromeDoc->Fullscreen()) {
1712 // This only clears the DOM fullscreen, will not exit from browser UI
1713 // fullscreen mode.
1714 Document::AsyncExitFullscreen(chromeDoc);
1719 void WindowGlobalParent::SetShouldReportHasBlockedOpaqueResponse(
1720 nsContentPolicyType aContentPolicy) {
1721 // It's always okay to block TYPE_BEACON, TYPE_PING and TYPE_CSP_REPORT in
1722 // the parent process because content processes can do nothing to their
1723 // responses. Hence excluding them from the telemetry as blocking
1724 // them have no webcompat concerns.
1725 if (aContentPolicy != nsIContentPolicy::TYPE_BEACON &&
1726 aContentPolicy != nsIContentPolicy::TYPE_PING &&
1727 aContentPolicy != nsIContentPolicy::TYPE_CSP_REPORT) {
1728 if (IsTop()) {
1729 mShouldReportHasBlockedOpaqueResponse = true;
1734 IPCResult WindowGlobalParent::RecvSetCookies(
1735 const nsCString& aBaseDomain, const OriginAttributes& aOriginAttributes,
1736 nsIURI* aHost, bool aFromHttp, const nsTArray<CookieStruct>& aCookies) {
1737 // Get CookieServiceParent via
1738 // ContentParent->NeckoParent->CookieServiceParent.
1739 ContentParent* contentParent = GetContentParent();
1740 NS_ENSURE_TRUE(contentParent, IPC_OK());
1742 net::PNeckoParent* neckoParent =
1743 LoneManagedOrNullAsserts(contentParent->ManagedPNeckoParent());
1744 NS_ENSURE_TRUE(neckoParent, IPC_OK());
1745 net::PCookieServiceParent* csParent =
1746 LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
1747 NS_ENSURE_TRUE(csParent, IPC_OK());
1748 auto* cs = static_cast<net::CookieServiceParent*>(csParent);
1750 return cs->SetCookies(aBaseDomain, aOriginAttributes, aHost, aFromHttp,
1751 aCookies, GetBrowsingContext());
1754 IPCResult WindowGlobalParent::RecvOnInitialStorageAccess() {
1755 DebugOnly<nsresult> rv =
1756 BounceTrackingStorageObserver::OnInitialStorageAccess(this);
1757 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to notify storage access");
1758 return IPC_OK();
1761 IPCResult WindowGlobalParent::RecvRecordUserActivationForBTP() {
1762 WindowGlobalParent* top = TopWindowContext();
1763 if (!top) {
1764 return IPC_OK();
1766 nsIPrincipal* principal = top->DocumentPrincipal();
1767 if (!principal) {
1768 return IPC_OK();
1771 DebugOnly<nsresult> rv =
1772 BounceTrackingProtection::RecordUserActivation(principal, Some(PR_Now()));
1773 NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
1774 "Failed to record BTP user activation.");
1776 return IPC_OK();
1779 NS_IMPL_CYCLE_COLLECTION_CLASS(WindowGlobalParent)
1781 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WindowGlobalParent,
1782 WindowContext)
1783 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPageUseCountersWindow)
1784 tmp->UnlinkManager();
1785 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
1787 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WindowGlobalParent,
1788 WindowContext)
1789 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPageUseCountersWindow)
1790 if (!tmp->IsInProcess()) {
1791 CycleCollectionNoteChild(cb, static_cast<BrowserParent*>(tmp->Manager()),
1792 "Manager()");
1794 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
1796 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalParent,
1797 WindowContext)
1798 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1800 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalParent)
1801 NS_INTERFACE_MAP_END_INHERITING(WindowContext)
1803 NS_IMPL_ADDREF_INHERITED(WindowGlobalParent, WindowContext)
1804 NS_IMPL_RELEASE_INHERITED(WindowGlobalParent, WindowContext)
1806 } // namespace mozilla::dom