Backed out changeset 7b83373f7a9e (bug 1883860) for causing build bustages @ caps...
[gecko.git] / dom / ipc / WindowGlobalParent.cpp
blob0c703f79510edfb2e6d423d884bd8c2da9a7821a
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/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"
46 #include "nsError.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"
59 #include "nsITimer.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,
97 std::move(aInit)),
98 mSandboxFlags(0),
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)) {
111 return nullptr;
114 RefPtr<WindowGlobalParent> wgp =
115 GetByInnerWindowId(aInit.context().mInnerWindowId);
116 MOZ_RELEASE_ASSERT(!wgp, "Creating duplicate WindowGlobalParent");
118 FieldValues fields(aInit.context().mFields);
119 wgp =
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");
138 return wgp.forget();
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
166 if (!mDocumentURI) {
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()) {
178 MOZ_ALWAYS_SUCCEEDS(
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));
193 } else {
194 ContentBlockingAllowList::ComputePrincipal(
195 mDocumentPrincipal,
196 getter_AddRefs(mDocContentBlockingAllowListPrincipal));
200 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
201 if (obs) {
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);
219 if (aIncrease) {
220 int32_t& count = mOriginMap.LookupOrInsert(origin);
221 count += 1;
222 mMaxOrigins = std::max(mMaxOrigins, mOriginMap.Count());
223 } else if (auto entry = mOriginMap.Lookup(origin)) {
224 entry.Data() -= 1;
226 if (entry.Data() == 0) {
227 entry.Remove();
233 void WindowGlobalParent::OriginCounter::Accumulate() {
234 mozilla::glean::geckoview::per_document_site_origins.AccumulateSingleSample(
235 mMaxOrigins);
237 mMaxOrigins = 0;
238 mOriginMap.Clear();
241 /* static */
242 already_AddRefed<WindowGlobalParent> WindowGlobalParent::GetByInnerWindowId(
243 uint64_t aInnerWindowId) {
244 if (!XRE_IsParentProcess()) {
245 return nullptr;
248 return WindowContext::GetById(aInnerWindowId).downcast<WindowGlobalParent>();
251 already_AddRefed<WindowGlobalChild> WindowGlobalParent::GetChildActor() {
252 if (!CanSend()) {
253 return nullptr;
255 IProtocol* otherSide = InProcessParent::ChildActorFor(this);
256 return do_AddRef(static_cast<WindowGlobalChild*>(otherSide));
259 BrowserParent* WindowGlobalParent::GetBrowserParent() {
260 if (IsInProcess() || !CanSend()) {
261 return nullptr;
263 return static_cast<BrowserParent*>(Manager());
266 ContentParent* WindowGlobalParent::GetContentParent() {
267 if (IsInProcess() || !CanSend()) {
268 return nullptr;
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();
281 return nullptr;
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()) {
298 return true;
301 RefPtr<WindowGlobalParent> embedder =
302 BrowsingContext()->GetEmbedderWindowGlobal();
303 if (NS_WARN_IF(!embedder)) {
304 return false;
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()) {
323 MOZ_LOG(
324 BrowsingContext::GetLog(), LogLevel::Debug,
325 ("ParentIPC: Trying to send a message with dead or detached context"));
326 return IPC_OK();
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);
346 return IPC_OK();
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()) {
356 MOZ_LOG(
357 BrowsingContext::GetLog(), LogLevel::Debug,
358 ("ParentIPC: Trying to send a message with dead or detached context"));
359 return IPC_OK();
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);
380 return IPC_OK();
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();
393 if (!ios) {
394 return IPC_FAIL(this, "Cannot get IOService");
396 nsCOMPtr<nsIProtocolHandler> handler;
397 ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
398 if (!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();
411 if (precursor) {
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 "
420 "principal URI");
425 mDocumentURI = aURI;
426 return IPC_OK();
429 nsresult WindowGlobalParent::SetDocumentStoragePrincipal(
430 nsIPrincipal* aNewDocumentStoragePrincipal) {
431 if (mDocumentPrincipal->Equals(aNewDocumentStoragePrincipal)) {
432 mDocumentStoragePrincipal = mDocumentPrincipal;
433 return NS_OK;
436 // Compare originNoSuffix to ensure it's equal.
437 nsCString noSuffix;
438 nsresult rv = mDocumentPrincipal->GetOriginNoSuffix(noSuffix);
439 if (NS_FAILED(rv)) {
440 return rv;
443 nsCString storageNoSuffix;
444 rv = aNewDocumentStoragePrincipal->GetOriginNoSuffix(storageNoSuffix);
445 if (NS_FAILED(rv)) {
446 return rv;
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;
459 return NS_OK;
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");
478 return IPC_OK();
480 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentTitle(
481 const nsString& aTitle) {
482 if (mDocumentTitle.isSome() && mDocumentTitle.value() == aTitle) {
483 return IPC_OK();
486 mDocumentTitle = Some(aTitle);
488 // Send a pagetitlechanged event only for changes to the title
489 // for top-level frames.
490 if (!BrowsingContext()->IsTop()) {
491 return IPC_OK();
494 // Notify media controller in order to update its default metadata.
495 if (BrowsingContext()->HasCreatedMediaController()) {
496 BrowsingContext()->GetMediaController()->NotifyPageTitleChanged();
499 Element* frameElement = BrowsingContext()->GetEmbedderElement();
500 if (!frameElement) {
501 return IPC_OK();
504 AsyncEventDispatcher::RunDOMEventWhenSafe(
505 *frameElement, u"pagetitlechanged"_ns, CanBubble::eYes,
506 ChromeOnlyDispatch::eYes);
508 return IPC_OK();
511 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateHttpsOnlyStatus(
512 uint32_t aHttpsOnlyStatus) {
513 mHttpsOnlyStatus = aHttpsOnlyStatus;
514 return IPC_OK();
517 IPCResult WindowGlobalParent::RecvUpdateDocumentHasLoaded(
518 bool aDocumentHasLoaded) {
519 mDocumentHasLoaded = aDocumentHasLoaded;
520 return IPC_OK();
523 IPCResult WindowGlobalParent::RecvUpdateDocumentHasUserInteracted(
524 bool aDocumentHasUserInteracted) {
525 mDocumentHasUserInteracted = aDocumentHasUserInteracted;
526 return IPC_OK();
529 IPCResult WindowGlobalParent::RecvUpdateSandboxFlags(uint32_t aSandboxFlags) {
530 mSandboxFlags = aSandboxFlags;
531 return IPC_OK();
534 IPCResult WindowGlobalParent::RecvUpdateDocumentCspSettings(
535 bool aBlockAllMixedContent, bool aUpgradeInsecureRequests) {
536 mBlockAllMixedContent = aBlockAllMixedContent;
537 mUpgradeInsecureRequests = aUpgradeInsecureRequests;
538 return IPC_OK();
541 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetClientInfo(
542 const IPCClientInfo& aIPCClientInfo) {
543 mClientInfo = Some(ClientInfo(aIPCClientInfo));
544 return IPC_OK();
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();
552 if (CanSend()) {
553 RefPtr<BrowserParent> browserParent = GetBrowserParent();
554 if (!browserParent || !browserParent->IsDestroyed()) {
555 Unused << Send__delete__(this);
558 return IPC_OK();
561 IPCResult WindowGlobalParent::RecvRawMessage(
562 const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
563 const Maybe<ClonedMessageData>& aStack) {
564 Maybe<StructuredCloneData> data;
565 if (aData) {
566 data.emplace();
567 data->BorrowFromClonedMessageData(*aData);
569 Maybe<StructuredCloneData> stack;
570 if (aStack) {
571 stack.emplace();
572 stack->BorrowFromClonedMessageData(*aStack);
574 MMPrinter::Print("WindowGlobalParent::RecvRawMessage", aMeta.actorName(),
575 aMeta.messageName(), *aData);
576 ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
577 return IPC_OK();
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>&
593 aReason,
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.
609 if (IsInProcess()) {
610 return;
613 Maybe<uint32_t> event = GetContentBlockingLog()->RecordLogParent(
614 aTrackingOrigin, aEvent, aBlocked, aReason, aTrackingFullHashes,
615 aCanvasFingerprinter, aCanvasFingerprinterKnownText);
617 // Notify the OnContentBlockingEvent if necessary.
618 if (event) {
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,
639 ErrorResult& aRv) {
640 RefPtr<JSWindowActorParent> actor;
641 if (aMaybeActor.get()) {
642 aRv = UNWRAP_OBJECT(JSWindowActorParent, aMaybeActor.get(), actor);
643 if (aRv.Failed()) {
644 return nullptr;
646 } else {
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()) {
659 return false;
662 return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() == this;
665 bool WindowGlobalParent::IsActiveInTab() {
666 if (!CanSend()) {
667 return false;
670 CanonicalBrowsingContext* bc = BrowsingContext();
671 if (!bc || bc->GetCurrentWindowGlobal() != this) {
672 return false;
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();
682 namespace {
684 class ShareHandler final : public PromiseNativeHandler {
685 public:
686 explicit ShareHandler(
687 mozilla::dom::WindowGlobalParent::ShareResolver&& aResolver)
688 : mResolver(std::move(aResolver)) {}
690 NS_DECL_ISUPPORTS
692 public:
693 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
694 ErrorResult& aRv) override {
695 mResolver(NS_OK);
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);
702 return;
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);
711 return;
714 mResolver(unwrapped->GetResult());
717 private:
718 ~ShareHandler() = default;
720 mozilla::dom::WindowGlobalParent::ShareResolver mResolver;
723 NS_IMPL_ISUPPORTS0(ShareHandler)
725 } // namespace
727 mozilla::ipc::IPCResult WindowGlobalParent::RecvGetContentBlockingEvents(
728 WindowGlobalParent::GetContentBlockingEventsResolver&& aResolver) {
729 uint32_t events = GetContentBlockingLog()->GetContentBlockingEventsInLog();
730 aResolver(events);
732 return IPC_OK();
735 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateCookieJarSettings(
736 const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
737 net::CookieJarSettings::Deserialize(aCookieJarSettingsArgs,
738 getter_AddRefs(mCookieJarSettings));
739 return IPC_OK();
742 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentSecurityInfo(
743 nsITransportSecurityInfo* aSecurityInfo) {
744 mSecurityInfo = aSecurityInfo;
745 return IPC_OK();
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");
753 if (!sharePicker) {
754 aResolver(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
755 return IPC_OK();
758 // Initialize the ShareWidget
759 RefPtr<BrowserParent> parent = GetBrowserParent();
760 nsCOMPtr<mozIDOMWindowProxy> openerWindow;
761 if (parent) {
762 openerWindow = parent->GetParentWindowOuter();
763 if (!openerWindow) {
764 aResolver(NS_ERROR_FAILURE);
765 return IPC_OK();
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));
774 if (NS_FAILED(rv)) {
775 aResolver(rv);
776 return IPC_OK();
779 // Handler finally awaits response...
780 RefPtr<ShareHandler> handler = new ShareHandler(std::move(aResolver));
781 promise->AppendNativeHandler(handler);
783 return IPC_OK();
786 namespace {
788 class CheckPermitUnloadRequest final : public PromiseNativeHandler,
789 public nsITimerCallback {
790 public:
791 CheckPermitUnloadRequest(WindowGlobalParent* aWGP, bool aHasInProcessBlocker,
792 nsIDocumentViewer::PermitUnloadAction aAction,
793 std::function<void(bool)>&& aResolver)
794 : mResolver(std::move(aResolver)),
795 mWGP(aWGP),
796 mAction(aAction),
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);
817 mPendingRequests++;
818 auto resolve = [self](bool blockNavigation) {
819 if (blockNavigation) {
820 self->mFoundBlocker = true;
822 self->ResolveRequest();
824 if (cp) {
825 cp->SendDispatchBeforeUnloadToSubtree(
826 bc, resolve, [self](auto) { self->ResolveRequest(); });
827 } else {
828 ContentChild::DispatchBeforeUnloadToSubtree(bc, resolve);
834 if (mPendingRequests && aTimeout) {
835 Unused << NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aTimeout,
836 nsITimer::TYPE_ONE_SHOT);
839 CheckDoneWaiting();
842 void ResolveRequest() {
843 mPendingRequests--;
844 CheckDoneWaiting();
847 NS_IMETHODIMP Notify(nsITimer* aTimer) override {
848 MOZ_ASSERT(aTimer == mTimer);
849 if (mState == State::WAITING) {
850 mState = State::TIMED_OUT;
851 CheckDoneWaiting();
853 return NS_OK;
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))) {
862 return;
865 mState = State::PROMPTING;
867 // Clearing our reference to the timer will automatically cancel it if it's
868 // still running.
869 mTimer = nullptr;
871 if (!mFoundBlocker) {
872 SendReply(true);
873 return;
876 auto action = mAction;
877 if (StaticPrefs::dom_disable_beforeunload()) {
878 action = nsIDocumentViewer::eDontPromptAndUnload;
880 if (action != nsIDocumentViewer::ePrompt) {
881 SendReply(action == nsIDocumentViewer::eDontPromptAndUnload);
882 return;
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));
895 if (!promise) {
896 return;
899 promise->AppendNativeHandler(this);
900 cleanup.release();
904 void SendReply(bool aAllow) {
905 MOZ_ASSERT(mState != State::REPLIED);
906 mResolver(aAllow);
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);
921 SendReply(false);
924 NS_DECL_ISUPPORTS
926 private:
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) {
931 SendReply(false);
935 enum class State : uint8_t {
936 UNINITIALIZED,
937 WAITING,
938 TIMED_OUT,
939 PROMPTING,
940 REPLIED,
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)
959 } // namespace
961 mozilla::ipc::IPCResult WindowGlobalParent::RecvCheckPermitUnload(
962 bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
963 CheckPermitUnloadResolver&& aResolver) {
964 if (!IsCurrentGlobal()) {
965 aResolver(false);
966 return IPC_OK();
969 auto request = MakeRefPtr<CheckPermitUnloadRequest>(
970 this, aHasInProcessBlocker, aAction, std::move(aResolver));
971 request->Run(/* aIgnoreProcess */ GetContentParent());
973 return IPC_OK();
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())) {
981 return nullptr;
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));
997 request->Run();
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())) {
1006 return nullptr;
1009 nscolor color;
1010 if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0),
1011 aBackgroundColor, &color,
1012 nullptr, nullptr))) {
1013 aRv = NS_ERROR_FAILURE;
1014 return nullptr;
1017 gfx::CrossProcessPaintFlags flags =
1018 gfx::CrossProcessPaintFlags::UseHighQualityScaling;
1019 if (!aRect) {
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,
1027 promise)) {
1028 aRv = NS_ERROR_FAILURE;
1029 return nullptr;
1031 return promise.forget();
1034 void WindowGlobalParent::DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
1035 const Maybe<IntRect>& aRect,
1036 float aScale,
1037 nscolor aBackgroundColor,
1038 uint32_t aFlags) {
1039 auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor, aFlags);
1041 RefPtr<gfx::CrossProcessPaint> paint(aPaint);
1042 RefPtr<WindowGlobalParent> wgp(this);
1043 promise->Then(
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"));
1087 return IPC_OK();
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.
1100 return IPC_OK();
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;
1111 MOZ_LOG(
1112 gUseCountersLog, LogLevel::Debug,
1113 (" > top-level now waiting on %d\n", page->mPageUseCounters->mWaiting));
1115 return IPC_OK();
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.
1123 MOZ_LOG(
1124 gUseCountersLog, LogLevel::Debug,
1125 ("Accumulate page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
1126 InnerWindowId(),
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"));
1132 return IPC_OK();
1135 MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters);
1136 MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters->mWaiting > 0);
1138 mPageUseCountersWindow->mPageUseCounters->mUseCounters |= aUseCounters;
1139 mPageUseCountersWindow->mPageUseCounters->mReceivedAny = true;
1140 return IPC_OK();
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,
1149 InnerWindowId()));
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"));
1155 return;
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));
1164 return;
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();
1174 if (dumpCounters) {
1175 urlForLogging.emplace(
1176 nsContentUtils::TruncatedURLForDisplay(mDocumentURI));
1179 glean::use_counter::top_level_content_documents_destroyed.Add();
1181 bool any = false;
1182 for (int32_t c = 0; c < eUseCounter_Count; ++c) {
1183 auto uc = static_cast<UseCounter>(c);
1184 if (!mPageUseCounters->mUseCounters[uc]) {
1185 continue;
1187 any = true;
1188 const char* metricName = IncrementUseCounter(uc, /* aIsPage = */ true);
1189 if (dumpCounters) {
1190 printf_stderr("USE_COUNTER_PAGE: %s - %s\n", metricName,
1191 urlForLogging->get());
1195 if (!any) {
1196 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1197 (" > page use counter data was received, but was empty"));
1199 } else {
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();
1210 if (!top) {
1211 return nullptr;
1214 if (IsInProcess()) {
1215 return top->BrowsingContext()->GetEmbedderElement();
1218 if (BrowserParent* parent = top->GetBrowserParent()) {
1219 return parent->GetOwnerElement();
1222 return nullptr;
1225 void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) {
1226 if (!aEmbedder) {
1227 aEmbedder = GetRootOwnerElement();
1229 if (aEmbedder) {
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);
1242 return IPC_OK();
1245 nsCString BFCacheStatusToString(uint32_t aFlags) {
1246 if (aFlags == 0) {
1247 return "0"_ns;
1250 nsCString flags;
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.");
1280 return flags;
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]");
1287 if (mDocumentURI) {
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;
1297 return IPC_OK();
1300 mozilla::ipc::IPCResult
1301 WindowGlobalParent::RecvUpdateActivePeerConnectionStatus(bool aIsAdded) {
1302 if (aIsAdded) {
1303 RecvUpdateBFCacheStatus(BFCacheStatus::ACTIVE_PEER_CONNECTION, 0);
1304 } else {
1305 RecvUpdateBFCacheStatus(0, BFCacheStatus::ACTIVE_PEER_CONNECTION);
1308 if (WindowGlobalParent* top = TopWindowContext()) {
1309 CheckedUint32 newValue(top->mNumOfProcessesWithActivePeerConnections);
1310 if (aIsAdded) {
1311 ++newValue;
1312 } else {
1313 --newValue;
1315 if (!newValue.isValid()) {
1316 return IPC_FAIL(this,
1317 "mNumOfProcessesWithActivePeerConnections overflowed");
1320 top->mNumOfProcessesWithActivePeerConnections = newValue.value();
1321 Unused << top->SetHasActivePeerConnections(newValue.value() > 0);
1324 return IPC_OK();
1327 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetSingleChannelId(
1328 const Maybe<uint64_t>& aSingleChannelId) {
1329 mSingleChannelId = aSingleChannelId;
1330 return IPC_OK();
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
1341 // child process?
1343 nsCOMPtr<nsIURI> uri;
1344 mDocumentPrincipal->GetDomain(getter_AddRefs(uri));
1345 if (!uri) {
1346 uri = mDocumentPrincipal->GetURI();
1347 if (!uri) {
1348 return IPC_OK();
1352 if (!Document::IsValidDomain(uri, aDomain)) {
1353 // Error: illegal domain
1354 return IPC_FAIL(
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);
1363 return IPC_OK();
1366 mozilla::ipc::IPCResult WindowGlobalParent::RecvReloadWithHttpsOnlyException() {
1367 nsresult rv;
1368 nsCOMPtr<nsIURI> currentUri = BrowsingContext()->Top()->GetCurrentURI();
1370 if (!currentUri) {
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;
1378 if (isViewSource) {
1379 nestedURI->GetInnerURI(getter_AddRefs(innerURI));
1380 } else {
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()) {
1395 newURI = innerURI;
1396 } else {
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();
1408 if (!permMgr) {
1409 return IPC_FAIL(
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)) {
1422 return IPC_FAIL(
1423 this, "HTTPS-only mode: Failed to add permission to the principal");
1426 nsCOMPtr<nsIURI> insecureURI = newURI;
1427 if (isViewSource) {
1428 nsAutoCString spec;
1429 MOZ_ALWAYS_SUCCEEDS(newURI->GetSpec(spec));
1430 if (NS_FAILED(
1431 NS_NewURI(getter_AddRefs(insecureURI), "view-source:"_ns + spec))) {
1432 return IPC_FAIL(
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);
1444 return IPC_OK();
1447 IPCResult WindowGlobalParent::RecvDiscoverIdentityCredentialFromExternalSource(
1448 const IdentityCredentialRequestOptions& aOptions,
1449 const DiscoverIdentityCredentialFromExternalSourceResolver& aResolver) {
1450 IdentityCredential::DiscoverFromExternalSourceInMainProcess(
1451 DocumentPrincipal(), this->BrowsingContext(), aOptions)
1452 ->Then(
1453 GetCurrentSerialEventTarget(), __func__,
1454 [aResolver](const IPCIdentityCredential& aResult) {
1455 return aResolver(Some(aResult));
1457 [aResolver](nsresult aErr) { aResolver(Nothing()); });
1458 return IPC_OK();
1461 IPCResult WindowGlobalParent::RecvGetStorageAccessPermission(
1462 GetStorageAccessPermissionResolver&& aResolve) {
1463 WindowGlobalParent* top = TopWindowContext();
1464 if (!top) {
1465 return IPC_FAIL_NO_REASON(this);
1467 nsIPrincipal* topPrincipal = top->DocumentPrincipal();
1468 nsIPrincipal* principal = DocumentPrincipal();
1469 uint32_t result;
1470 nsresult rv = AntiTrackingUtils::TestStoragePermissionInParent(
1471 topPrincipal, principal, &result);
1472 if (NS_WARN_IF(NS_FAILED(rv))) {
1473 aResolve(nsIPermissionManager::UNKNOWN_ACTION);
1474 return IPC_OK();
1477 aResolve(result);
1478 return IPC_OK();
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
1499 enum {
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 =
1510 mSecurityState &
1511 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
1512 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
1513 bool hasMixedActive =
1514 mSecurityState &
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();
1580 if (obs) {
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()) {
1623 return false;
1626 RefPtr<BrowserParent> browserParent = GetBrowserParent();
1627 if (!browserParent ||
1628 !IsWebRemoteType(browserParent->Manager()->GetRemoteType())) {
1629 return false;
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)) ==
1645 aStateFlags,
1646 "Invalid flags specified!");
1648 if ((mSecurityState & aStateFlags) == aStateFlags) {
1649 return;
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
1673 // fullscreen mode.
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) {
1688 if (IsTop()) {
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,
1718 WindowContext)
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