no bug - Bumping Firefox l10n changesets r=release a=l10n-bump DONTBUILD CLOSED TREE
[gecko.git] / dom / ipc / WindowGlobalParent.cpp
blobf60790a1555d232af9d77b7c1b80fff8a0ea1041
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 nsCOMPtr<nsIURI> principalURI = mDocumentPrincipal->GetURI();
403 if (mDocumentPrincipal->GetIsNullPrincipal()) {
404 nsCOMPtr<nsIPrincipal> precursor =
405 mDocumentPrincipal->GetPrecursorPrincipal();
406 if (precursor) {
407 principalURI = precursor->GetURI();
411 if (nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI,
412 aURI)) {
413 return IPC_FAIL(this,
414 "Setting DocumentURI with a different Origin than "
415 "principal URI");
419 mDocumentURI = aURI;
420 return IPC_OK();
423 nsresult WindowGlobalParent::SetDocumentStoragePrincipal(
424 nsIPrincipal* aNewDocumentStoragePrincipal) {
425 if (mDocumentPrincipal->Equals(aNewDocumentStoragePrincipal)) {
426 mDocumentStoragePrincipal = mDocumentPrincipal;
427 return NS_OK;
430 // Compare originNoSuffix to ensure it's equal.
431 nsCString noSuffix;
432 nsresult rv = mDocumentPrincipal->GetOriginNoSuffix(noSuffix);
433 if (NS_FAILED(rv)) {
434 return rv;
437 nsCString storageNoSuffix;
438 rv = aNewDocumentStoragePrincipal->GetOriginNoSuffix(storageNoSuffix);
439 if (NS_FAILED(rv)) {
440 return rv;
443 if (noSuffix != storageNoSuffix) {
444 return NS_ERROR_FAILURE;
447 if (!mDocumentPrincipal->OriginAttributesRef().EqualsIgnoringPartitionKey(
448 aNewDocumentStoragePrincipal->OriginAttributesRef())) {
449 return NS_ERROR_FAILURE;
452 mDocumentStoragePrincipal = aNewDocumentStoragePrincipal;
453 return NS_OK;
456 IPCResult WindowGlobalParent::RecvUpdateDocumentPrincipal(
457 nsIPrincipal* aNewDocumentPrincipal,
458 nsIPrincipal* aNewDocumentStoragePrincipal) {
459 if (!mDocumentPrincipal->Equals(aNewDocumentPrincipal)) {
460 return IPC_FAIL(this,
461 "Trying to reuse WindowGlobalParent but the principal of "
462 "the new document does not match the old one");
464 mDocumentPrincipal = aNewDocumentPrincipal;
466 if (NS_FAILED(SetDocumentStoragePrincipal(aNewDocumentStoragePrincipal))) {
467 return IPC_FAIL(this,
468 "Trying to reuse WindowGlobalParent but the principal of "
469 "the new document does not match the storage principal");
472 return IPC_OK();
474 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentTitle(
475 const nsString& aTitle) {
476 if (mDocumentTitle.isSome() && mDocumentTitle.value() == aTitle) {
477 return IPC_OK();
480 mDocumentTitle = Some(aTitle);
482 // Send a pagetitlechanged event only for changes to the title
483 // for top-level frames.
484 if (!BrowsingContext()->IsTop()) {
485 return IPC_OK();
488 // Notify media controller in order to update its default metadata.
489 if (BrowsingContext()->HasCreatedMediaController()) {
490 BrowsingContext()->GetMediaController()->NotifyPageTitleChanged();
493 Element* frameElement = BrowsingContext()->GetEmbedderElement();
494 if (!frameElement) {
495 return IPC_OK();
498 AsyncEventDispatcher::RunDOMEventWhenSafe(
499 *frameElement, u"pagetitlechanged"_ns, CanBubble::eYes,
500 ChromeOnlyDispatch::eYes);
502 return IPC_OK();
505 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateHttpsOnlyStatus(
506 uint32_t aHttpsOnlyStatus) {
507 mHttpsOnlyStatus = aHttpsOnlyStatus;
508 return IPC_OK();
511 IPCResult WindowGlobalParent::RecvUpdateDocumentHasLoaded(
512 bool aDocumentHasLoaded) {
513 mDocumentHasLoaded = aDocumentHasLoaded;
514 return IPC_OK();
517 IPCResult WindowGlobalParent::RecvUpdateDocumentHasUserInteracted(
518 bool aDocumentHasUserInteracted) {
519 mDocumentHasUserInteracted = aDocumentHasUserInteracted;
520 return IPC_OK();
523 IPCResult WindowGlobalParent::RecvUpdateSandboxFlags(uint32_t aSandboxFlags) {
524 mSandboxFlags = aSandboxFlags;
525 return IPC_OK();
528 IPCResult WindowGlobalParent::RecvUpdateDocumentCspSettings(
529 bool aBlockAllMixedContent, bool aUpgradeInsecureRequests) {
530 mBlockAllMixedContent = aBlockAllMixedContent;
531 mUpgradeInsecureRequests = aUpgradeInsecureRequests;
532 return IPC_OK();
535 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetClientInfo(
536 const IPCClientInfo& aIPCClientInfo) {
537 mClientInfo = Some(ClientInfo(aIPCClientInfo));
538 return IPC_OK();
541 IPCResult WindowGlobalParent::RecvDestroy() {
542 // Make a copy so that we can avoid potential iterator invalidation when
543 // calling the user-provided Destroy() methods.
544 JSActorWillDestroy();
546 if (CanSend()) {
547 RefPtr<BrowserParent> browserParent = GetBrowserParent();
548 if (!browserParent || !browserParent->IsDestroyed()) {
549 Unused << Send__delete__(this);
552 return IPC_OK();
555 IPCResult WindowGlobalParent::RecvRawMessage(
556 const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
557 const Maybe<ClonedMessageData>& aStack) {
558 Maybe<StructuredCloneData> data;
559 if (aData) {
560 data.emplace();
561 data->BorrowFromClonedMessageData(*aData);
563 Maybe<StructuredCloneData> stack;
564 if (aStack) {
565 stack.emplace();
566 stack->BorrowFromClonedMessageData(*aStack);
568 MMPrinter::Print("WindowGlobalParent::RecvRawMessage", aMeta.actorName(),
569 aMeta.messageName(), aData);
570 ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
571 return IPC_OK();
574 const nsACString& WindowGlobalParent::GetRemoteType() {
575 if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
576 return browserParent->Manager()->GetRemoteType();
579 return NOT_REMOTE_TYPE;
582 void WindowGlobalParent::NotifyContentBlockingEvent(
583 uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
584 const nsACString& aTrackingOrigin,
585 const nsTArray<nsCString>& aTrackingFullHashes,
586 const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
587 aReason,
588 const Maybe<ContentBlockingNotifier::CanvasFingerprinter>&
589 aCanvasFingerprinter,
590 const Maybe<bool> aCanvasFingerprinterKnownText) {
591 MOZ_ASSERT(NS_IsMainThread());
592 DebugOnly<bool> isCookiesBlocked =
593 aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
594 aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
595 MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
596 MOZ_ASSERT_IF(!isCookiesBlocked, aReason.isNothing());
597 MOZ_ASSERT_IF(isCookiesBlocked && !aBlocked, aReason.isSome());
598 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
599 // TODO: temporarily remove this until we find the root case of Bug 1609144
600 // MOZ_DIAGNOSTIC_ASSERT_IF(XRE_IsE10sParentProcess(), !IsInProcess());
602 // Return early if this WindowGlobalParent is in process.
603 if (IsInProcess()) {
604 return;
607 Maybe<uint32_t> event = GetContentBlockingLog()->RecordLogParent(
608 aTrackingOrigin, aEvent, aBlocked, aReason, aTrackingFullHashes,
609 aCanvasFingerprinter, aCanvasFingerprinterKnownText);
611 // Notify the OnContentBlockingEvent if necessary.
612 if (event) {
613 if (auto* webProgress = GetBrowsingContext()->GetWebProgress()) {
614 webProgress->OnContentBlockingEvent(webProgress, aRequest, event.value());
619 already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetActor(
620 JSContext* aCx, const nsACString& aName, ErrorResult& aRv) {
621 return JSActorManager::GetActor(aCx, aName, aRv)
622 .downcast<JSWindowActorParent>();
625 already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetExistingActor(
626 const nsACString& aName) {
627 return JSActorManager::GetExistingActor(aName)
628 .downcast<JSWindowActorParent>();
631 already_AddRefed<JSActor> WindowGlobalParent::InitJSActor(
632 JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
633 ErrorResult& aRv) {
634 RefPtr<JSWindowActorParent> actor;
635 if (aMaybeActor.get()) {
636 aRv = UNWRAP_OBJECT(JSWindowActorParent, aMaybeActor.get(), actor);
637 if (aRv.Failed()) {
638 return nullptr;
640 } else {
641 actor = new JSWindowActorParent();
644 MOZ_RELEASE_ASSERT(!actor->GetManager(),
645 "mManager was already initialized once!");
646 actor->Init(aName, this);
647 return actor.forget();
650 bool WindowGlobalParent::IsCurrentGlobal() {
651 if (mozilla::SessionHistoryInParent() && BrowsingContext() &&
652 BrowsingContext()->IsInBFCache()) {
653 return false;
656 return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() == this;
659 bool WindowGlobalParent::IsActiveInTab() {
660 if (!CanSend()) {
661 return false;
664 CanonicalBrowsingContext* bc = BrowsingContext();
665 if (!bc || bc->GetCurrentWindowGlobal() != this) {
666 return false;
669 // We check the top BC so we don't need to worry about getting a stale value.
670 // That may not be necessary.
671 MOZ_ASSERT(bc->Top()->IsInBFCache() == bc->IsInBFCache(),
672 "BFCache bit out of sync?");
673 return bc->AncestorsAreCurrent() && !bc->Top()->IsInBFCache();
676 namespace {
678 class ShareHandler final : public PromiseNativeHandler {
679 public:
680 explicit ShareHandler(
681 mozilla::dom::WindowGlobalParent::ShareResolver&& aResolver)
682 : mResolver(std::move(aResolver)) {}
684 NS_DECL_ISUPPORTS
686 public:
687 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
688 ErrorResult& aRv) override {
689 mResolver(NS_OK);
692 virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
693 ErrorResult& aRv) override {
694 if (NS_WARN_IF(!aValue.isObject())) {
695 mResolver(NS_ERROR_FAILURE);
696 return;
699 // nsresult is stored as Exception internally in Promise
700 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
701 RefPtr<DOMException> unwrapped;
702 nsresult rv = UNWRAP_OBJECT(DOMException, &obj, unwrapped);
703 if (NS_WARN_IF(NS_FAILED(rv))) {
704 mResolver(NS_ERROR_FAILURE);
705 return;
708 mResolver(unwrapped->GetResult());
711 private:
712 ~ShareHandler() = default;
714 mozilla::dom::WindowGlobalParent::ShareResolver mResolver;
717 NS_IMPL_ISUPPORTS0(ShareHandler)
719 } // namespace
721 mozilla::ipc::IPCResult WindowGlobalParent::RecvGetContentBlockingEvents(
722 WindowGlobalParent::GetContentBlockingEventsResolver&& aResolver) {
723 uint32_t events = GetContentBlockingLog()->GetContentBlockingEventsInLog();
724 aResolver(events);
726 return IPC_OK();
729 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateCookieJarSettings(
730 const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
731 net::CookieJarSettings::Deserialize(aCookieJarSettingsArgs,
732 getter_AddRefs(mCookieJarSettings));
733 return IPC_OK();
736 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentSecurityInfo(
737 nsITransportSecurityInfo* aSecurityInfo) {
738 mSecurityInfo = aSecurityInfo;
739 return IPC_OK();
742 mozilla::ipc::IPCResult WindowGlobalParent::RecvShare(
743 IPCWebShareData&& aData, WindowGlobalParent::ShareResolver&& aResolver) {
744 // Widget Layer handoff...
745 nsCOMPtr<nsISharePicker> sharePicker =
746 do_GetService("@mozilla.org/sharepicker;1");
747 if (!sharePicker) {
748 aResolver(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
749 return IPC_OK();
752 // Initialize the ShareWidget
753 RefPtr<BrowserParent> parent = GetBrowserParent();
754 nsCOMPtr<mozIDOMWindowProxy> openerWindow;
755 if (parent) {
756 openerWindow = parent->GetParentWindowOuter();
757 if (!openerWindow) {
758 aResolver(NS_ERROR_FAILURE);
759 return IPC_OK();
762 sharePicker->Init(openerWindow);
764 // And finally share the data...
765 RefPtr<Promise> promise;
766 nsresult rv = sharePicker->Share(aData.title(), aData.text(), aData.url(),
767 getter_AddRefs(promise));
768 if (NS_FAILED(rv)) {
769 aResolver(rv);
770 return IPC_OK();
773 // Handler finally awaits response...
774 RefPtr<ShareHandler> handler = new ShareHandler(std::move(aResolver));
775 promise->AppendNativeHandler(handler);
777 return IPC_OK();
780 namespace {
782 class CheckPermitUnloadRequest final : public PromiseNativeHandler,
783 public nsITimerCallback {
784 public:
785 CheckPermitUnloadRequest(WindowGlobalParent* aWGP, bool aHasInProcessBlocker,
786 nsIDocumentViewer::PermitUnloadAction aAction,
787 std::function<void(bool)>&& aResolver)
788 : mResolver(std::move(aResolver)),
789 mWGP(aWGP),
790 mAction(aAction),
791 mFoundBlocker(aHasInProcessBlocker) {}
793 void Run(ContentParent* aIgnoreProcess = nullptr, uint32_t aTimeout = 0) {
794 MOZ_ASSERT(mState == State::UNINITIALIZED);
795 mState = State::WAITING;
797 RefPtr<CheckPermitUnloadRequest> self(this);
799 AutoTArray<ContentParent*, 8> seen;
800 if (aIgnoreProcess) {
801 seen.AppendElement(aIgnoreProcess);
804 BrowsingContext* bc = mWGP->GetBrowsingContext();
805 bc->PreOrderWalk([&](dom::BrowsingContext* aBC) {
806 if (WindowGlobalParent* wgp =
807 aBC->Canonical()->GetCurrentWindowGlobal()) {
808 ContentParent* cp = wgp->GetContentParent();
809 if (wgp->HasBeforeUnload() && !seen.ContainsSorted(cp)) {
810 seen.InsertElementSorted(cp);
811 mPendingRequests++;
812 auto resolve = [self](bool blockNavigation) {
813 if (blockNavigation) {
814 self->mFoundBlocker = true;
816 self->ResolveRequest();
818 if (cp) {
819 cp->SendDispatchBeforeUnloadToSubtree(
820 bc, resolve, [self](auto) { self->ResolveRequest(); });
821 } else {
822 ContentChild::DispatchBeforeUnloadToSubtree(bc, resolve);
828 if (mPendingRequests && aTimeout) {
829 Unused << NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aTimeout,
830 nsITimer::TYPE_ONE_SHOT);
833 CheckDoneWaiting();
836 void ResolveRequest() {
837 mPendingRequests--;
838 CheckDoneWaiting();
841 NS_IMETHODIMP Notify(nsITimer* aTimer) override {
842 MOZ_ASSERT(aTimer == mTimer);
843 if (mState == State::WAITING) {
844 mState = State::TIMED_OUT;
845 CheckDoneWaiting();
847 return NS_OK;
850 void CheckDoneWaiting() {
851 // If we've found a blocker, we prompt immediately without waiting for
852 // further responses. The user's response applies to the entire navigation
853 // attempt, regardless of how many "beforeunload" listeners we call.
854 if (mState != State::TIMED_OUT &&
855 (mState != State::WAITING || (mPendingRequests && !mFoundBlocker))) {
856 return;
859 mState = State::PROMPTING;
861 // Clearing our reference to the timer will automatically cancel it if it's
862 // still running.
863 mTimer = nullptr;
865 if (!mFoundBlocker) {
866 SendReply(true);
867 return;
870 auto action = mAction;
871 if (StaticPrefs::dom_disable_beforeunload()) {
872 action = nsIDocumentViewer::eDontPromptAndUnload;
874 if (action != nsIDocumentViewer::ePrompt) {
875 SendReply(action == nsIDocumentViewer::eDontPromptAndUnload);
876 return;
879 // Handle any failure in prompting by aborting the navigation. See comment
880 // in nsContentViewer::PermitUnload for reasoning.
881 auto cleanup = MakeScopeExit([&]() { SendReply(false); });
883 if (nsCOMPtr<nsIPromptCollection> prompt =
884 do_GetService("@mozilla.org/embedcomp/prompt-collection;1")) {
885 RefPtr<Promise> promise;
886 prompt->AsyncBeforeUnloadCheck(mWGP->GetBrowsingContext(),
887 getter_AddRefs(promise));
889 if (!promise) {
890 return;
893 promise->AppendNativeHandler(this);
894 cleanup.release();
898 void SendReply(bool aAllow) {
899 MOZ_ASSERT(mState != State::REPLIED);
900 mResolver(aAllow);
901 mState = State::REPLIED;
904 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
905 ErrorResult& aRv) override {
906 MOZ_ASSERT(mState == State::PROMPTING);
908 SendReply(JS::ToBoolean(aValue));
911 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
912 ErrorResult& aRv) override {
913 MOZ_ASSERT(mState == State::PROMPTING);
915 SendReply(false);
918 NS_DECL_ISUPPORTS
920 private:
921 ~CheckPermitUnloadRequest() {
922 // We may get here without having sent a reply if the promise we're waiting
923 // on is destroyed without being resolved or rejected.
924 if (mState != State::REPLIED) {
925 SendReply(false);
929 enum class State : uint8_t {
930 UNINITIALIZED,
931 WAITING,
932 TIMED_OUT,
933 PROMPTING,
934 REPLIED,
937 std::function<void(bool)> mResolver;
939 RefPtr<WindowGlobalParent> mWGP;
940 nsCOMPtr<nsITimer> mTimer;
942 uint32_t mPendingRequests = 0;
944 nsIDocumentViewer::PermitUnloadAction mAction;
946 State mState = State::UNINITIALIZED;
948 bool mFoundBlocker = false;
951 NS_IMPL_ISUPPORTS(CheckPermitUnloadRequest, nsITimerCallback)
953 } // namespace
955 mozilla::ipc::IPCResult WindowGlobalParent::RecvCheckPermitUnload(
956 bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
957 CheckPermitUnloadResolver&& aResolver) {
958 if (!IsCurrentGlobal()) {
959 aResolver(false);
960 return IPC_OK();
963 auto request = MakeRefPtr<CheckPermitUnloadRequest>(
964 this, aHasInProcessBlocker, aAction, std::move(aResolver));
965 request->Run(/* aIgnoreProcess */ GetContentParent());
967 return IPC_OK();
970 already_AddRefed<Promise> WindowGlobalParent::PermitUnload(
971 PermitUnloadAction aAction, uint32_t aTimeout, mozilla::ErrorResult& aRv) {
972 nsIGlobalObject* global = GetParentObject();
973 RefPtr<Promise> promise = Promise::Create(global, aRv);
974 if (NS_WARN_IF(aRv.Failed())) {
975 return nullptr;
978 auto request = MakeRefPtr<CheckPermitUnloadRequest>(
979 this, /* aHasInProcessBlocker */ false,
980 nsIDocumentViewer::PermitUnloadAction(aAction),
981 [promise](bool aAllow) { promise->MaybeResolve(aAllow); });
982 request->Run(/* aIgnoreProcess */ nullptr, aTimeout);
984 return promise.forget();
987 void WindowGlobalParent::PermitUnload(std::function<void(bool)>&& aResolver) {
988 RefPtr<CheckPermitUnloadRequest> request = new CheckPermitUnloadRequest(
989 this, /* aHasInProcessBlocker */ false,
990 nsIDocumentViewer::PermitUnloadAction::ePrompt, std::move(aResolver));
991 request->Run();
994 already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
995 const DOMRect* aRect, double aScale, const nsACString& aBackgroundColor,
996 bool aResetScrollPosition, mozilla::ErrorResult& aRv) {
997 nsIGlobalObject* global = GetParentObject();
998 RefPtr<Promise> promise = Promise::Create(global, aRv);
999 if (NS_WARN_IF(aRv.Failed())) {
1000 return nullptr;
1003 nscolor color;
1004 if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0),
1005 aBackgroundColor, &color,
1006 nullptr, nullptr))) {
1007 aRv = NS_ERROR_FAILURE;
1008 return nullptr;
1011 gfx::CrossProcessPaintFlags flags =
1012 gfx::CrossProcessPaintFlags::UseHighQualityScaling;
1013 if (!aRect) {
1014 // If no explicit Rect was passed, we want the currently visible viewport.
1015 flags |= gfx::CrossProcessPaintFlags::DrawView;
1016 } else if (aResetScrollPosition) {
1017 flags |= gfx::CrossProcessPaintFlags::ResetScrollPosition;
1020 if (!gfx::CrossProcessPaint::Start(this, aRect, (float)aScale, color, flags,
1021 promise)) {
1022 aRv = NS_ERROR_FAILURE;
1023 return nullptr;
1025 return promise.forget();
1028 void WindowGlobalParent::DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
1029 const Maybe<IntRect>& aRect,
1030 float aScale,
1031 nscolor aBackgroundColor,
1032 uint32_t aFlags) {
1033 auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor, aFlags);
1035 RefPtr<gfx::CrossProcessPaint> paint(aPaint);
1036 RefPtr<WindowGlobalParent> wgp(this);
1037 promise->Then(
1038 GetMainThreadSerialEventTarget(), __func__,
1039 [paint, wgp](PaintFragment&& aFragment) {
1040 paint->ReceiveFragment(wgp, std::move(aFragment));
1042 [paint, wgp](ResponseRejectReason&& aReason) {
1043 paint->LostFragment(wgp);
1048 * Accumulated page use counter data for a given top-level content document.
1050 struct PageUseCounters {
1051 // The number of page use counter data messages we are still waiting for.
1052 uint32_t mWaiting = 0;
1054 // Whether we have received any page use counter data.
1055 bool mReceivedAny = false;
1057 // The accumulated page use counters.
1058 UseCounters mUseCounters;
1061 mozilla::ipc::IPCResult WindowGlobalParent::RecvExpectPageUseCounters(
1062 const MaybeDiscarded<WindowContext>& aTop) {
1063 if (aTop.IsNull()) {
1064 return IPC_FAIL(this, "aTop must not be null");
1067 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1068 ("Expect page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
1069 InnerWindowId(), aTop.ContextId()));
1071 // We've been called to indicate that the document in our window intends
1072 // to send use counter data to accumulate towards the top-level document's
1073 // page use counters. This causes us to wait for this window to go away
1074 // (in WindowGlobalParent::ActorDestroy) before reporting the page use
1075 // counters via Telemetry.
1076 RefPtr<WindowGlobalParent> page =
1077 static_cast<WindowGlobalParent*>(aTop.GetMaybeDiscarded());
1078 if (!page || page->mSentPageUseCounters) {
1079 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1080 (" > too late, won't report page use counters for this straggler"));
1081 return IPC_OK();
1084 if (mPageUseCountersWindow) {
1085 if (mPageUseCountersWindow != page) {
1086 return IPC_FAIL(this,
1087 "ExpectPageUseCounters called on the same "
1088 "WindowContext with a different aTop value");
1091 // We can get called with the same aTop value more than once, e.g. for
1092 // initial about:blank documents and then subsequent "real" documents loaded
1093 // into the same window. We must note each source window only once.
1094 return IPC_OK();
1097 // Note that the top-level document must wait for one more window's use
1098 // counters before reporting via Telemetry.
1099 mPageUseCountersWindow = page;
1100 if (!page->mPageUseCounters) {
1101 page->mPageUseCounters = MakeUnique<PageUseCounters>();
1103 ++page->mPageUseCounters->mWaiting;
1105 MOZ_LOG(
1106 gUseCountersLog, LogLevel::Debug,
1107 (" > top-level now waiting on %d\n", page->mPageUseCounters->mWaiting));
1109 return IPC_OK();
1112 mozilla::ipc::IPCResult WindowGlobalParent::RecvAccumulatePageUseCounters(
1113 const UseCounters& aUseCounters) {
1114 // We've been called to accumulate use counter data into the page use counters
1115 // for the document in mPageUseCountersWindow.
1117 MOZ_LOG(
1118 gUseCountersLog, LogLevel::Debug,
1119 ("Accumulate page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
1120 InnerWindowId(),
1121 mPageUseCountersWindow ? mPageUseCountersWindow->InnerWindowId() : 0));
1123 if (!mPageUseCountersWindow || mPageUseCountersWindow->mSentPageUseCounters) {
1124 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1125 (" > too late, won't report page use counters for this straggler"));
1126 return IPC_OK();
1129 MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters);
1130 MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters->mWaiting > 0);
1132 mPageUseCountersWindow->mPageUseCounters->mUseCounters |= aUseCounters;
1133 mPageUseCountersWindow->mPageUseCounters->mReceivedAny = true;
1134 return IPC_OK();
1137 // This is called on the top-level WindowGlobal, i.e. the one that is
1138 // accumulating the page use counters, not the (potentially descendant) window
1139 // that has finished providing use counter data.
1140 void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
1141 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1142 ("Stop expecting page use counters: -> WindowContext %" PRIu64,
1143 InnerWindowId()));
1145 if (!mPageUseCounters) {
1146 MOZ_ASSERT_UNREACHABLE("Not expecting page use counter data");
1147 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1148 (" > not expecting page use counter data"));
1149 return;
1152 MOZ_ASSERT(mPageUseCounters->mWaiting > 0);
1153 --mPageUseCounters->mWaiting;
1155 if (mPageUseCounters->mWaiting > 0) {
1156 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1157 (" > now waiting on %d", mPageUseCounters->mWaiting));
1158 return;
1161 if (mPageUseCounters->mReceivedAny) {
1162 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1163 (" > reporting [%s]",
1164 nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
1166 Maybe<nsCString> urlForLogging;
1167 const bool dumpCounters = StaticPrefs::dom_use_counters_dump_page();
1168 if (dumpCounters) {
1169 urlForLogging.emplace(
1170 nsContentUtils::TruncatedURLForDisplay(mDocumentURI));
1173 glean::use_counter::top_level_content_documents_destroyed.Add();
1175 bool any = false;
1176 for (int32_t c = 0; c < eUseCounter_Count; ++c) {
1177 auto uc = static_cast<UseCounter>(c);
1178 if (!mPageUseCounters->mUseCounters[uc]) {
1179 continue;
1181 any = true;
1182 const char* metricName = IncrementUseCounter(uc, /* aIsPage = */ true);
1183 if (dumpCounters) {
1184 printf_stderr("USE_COUNTER_PAGE: %s - %s\n", metricName,
1185 urlForLogging->get());
1189 if (!any) {
1190 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1191 (" > page use counter data was received, but was empty"));
1193 } else {
1194 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1195 (" > no page use counter data was received"));
1198 mSentPageUseCounters = true;
1199 mPageUseCounters = nullptr;
1202 Element* WindowGlobalParent::GetRootOwnerElement() {
1203 WindowGlobalParent* top = TopWindowContext();
1204 if (!top) {
1205 return nullptr;
1208 if (IsInProcess()) {
1209 return top->BrowsingContext()->GetEmbedderElement();
1212 if (BrowserParent* parent = top->GetBrowserParent()) {
1213 return parent->GetOwnerElement();
1216 return nullptr;
1219 void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) {
1220 if (!aEmbedder) {
1221 aEmbedder = GetRootOwnerElement();
1223 if (aEmbedder) {
1224 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1225 obs->NotifyWhenScriptSafe(ToSupports(aEmbedder),
1226 "browser-shutdown-tabstate-updated", nullptr);
1231 mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() {
1232 CanonicalBrowsingContext* bc = BrowsingContext();
1233 if (bc && bc->AncestorsAreCurrent()) {
1234 bc->Top()->RequestRestoreTabContent(this);
1236 return IPC_OK();
1239 nsCString BFCacheStatusToString(uint32_t aFlags) {
1240 if (aFlags == 0) {
1241 return "0"_ns;
1244 nsCString flags;
1245 #define ADD_BFCACHESTATUS_TO_STRING(_flag) \
1246 if (aFlags & BFCacheStatus::_flag) { \
1247 if (!flags.IsEmpty()) { \
1248 flags.Append('|'); \
1250 flags.AppendLiteral(#_flag); \
1251 aFlags &= ~BFCacheStatus::_flag; \
1254 ADD_BFCACHESTATUS_TO_STRING(NOT_ALLOWED);
1255 ADD_BFCACHESTATUS_TO_STRING(EVENT_HANDLING_SUPPRESSED);
1256 ADD_BFCACHESTATUS_TO_STRING(SUSPENDED);
1257 ADD_BFCACHESTATUS_TO_STRING(UNLOAD_LISTENER);
1258 ADD_BFCACHESTATUS_TO_STRING(REQUEST);
1259 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_GET_USER_MEDIA);
1260 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_PEER_CONNECTION);
1261 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_EME_CONTENT);
1262 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_MSE_CONTENT);
1263 ADD_BFCACHESTATUS_TO_STRING(HAS_ACTIVE_SPEECH_SYNTHESIS);
1264 ADD_BFCACHESTATUS_TO_STRING(HAS_USED_VR);
1265 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_REMOTE_SUBFRAMES);
1266 ADD_BFCACHESTATUS_TO_STRING(NOT_ONLY_TOPLEVEL_IN_BCG);
1267 ADD_BFCACHESTATUS_TO_STRING(BEFOREUNLOAD_LISTENER);
1268 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_LOCK);
1270 #undef ADD_BFCACHESTATUS_TO_STRING
1272 MOZ_ASSERT(aFlags == 0,
1273 "Missing stringification for enum value in BFCacheStatus.");
1274 return flags;
1277 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateBFCacheStatus(
1278 const uint32_t& aOnFlags, const uint32_t& aOffFlags) {
1279 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) {
1280 nsAutoCString uri("[no uri]");
1281 if (mDocumentURI) {
1282 uri = mDocumentURI->GetSpecOrDefault();
1284 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
1285 ("Setting BFCache flags for %s +(%s) -(%s)", uri.get(),
1286 BFCacheStatusToString(aOnFlags).get(),
1287 BFCacheStatusToString(aOffFlags).get()));
1289 mBFCacheStatus |= aOnFlags;
1290 mBFCacheStatus &= ~aOffFlags;
1291 return IPC_OK();
1294 mozilla::ipc::IPCResult
1295 WindowGlobalParent::RecvUpdateActivePeerConnectionStatus(bool aIsAdded) {
1296 if (aIsAdded) {
1297 RecvUpdateBFCacheStatus(BFCacheStatus::ACTIVE_PEER_CONNECTION, 0);
1298 } else {
1299 RecvUpdateBFCacheStatus(0, BFCacheStatus::ACTIVE_PEER_CONNECTION);
1302 if (WindowGlobalParent* top = TopWindowContext()) {
1303 CheckedUint32 newValue(top->mNumOfProcessesWithActivePeerConnections);
1304 if (aIsAdded) {
1305 ++newValue;
1306 } else {
1307 --newValue;
1309 if (!newValue.isValid()) {
1310 return IPC_FAIL(this,
1311 "mNumOfProcessesWithActivePeerConnections overflowed");
1314 top->mNumOfProcessesWithActivePeerConnections = newValue.value();
1315 Unused << top->SetHasActivePeerConnections(newValue.value() > 0);
1318 return IPC_OK();
1321 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetSingleChannelId(
1322 const Maybe<uint64_t>& aSingleChannelId) {
1323 mSingleChannelId = aSingleChannelId;
1324 return IPC_OK();
1327 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetDocumentDomain(
1328 NotNull<nsIURI*> aDomain) {
1329 if (mSandboxFlags & SANDBOXED_DOMAIN) {
1330 // We're sandboxed; disallow setting domain
1331 return IPC_FAIL(this, "Sandbox disallows domain setting.");
1334 // Might need to do a featurepolicy check here, like we currently do in the
1335 // child process?
1337 nsCOMPtr<nsIURI> uri;
1338 mDocumentPrincipal->GetDomain(getter_AddRefs(uri));
1339 if (!uri) {
1340 uri = mDocumentPrincipal->GetURI();
1341 if (!uri) {
1342 return IPC_OK();
1346 if (!Document::IsValidDomain(uri, aDomain)) {
1347 // Error: illegal domain
1348 return IPC_FAIL(
1349 this, "Setting domain that's not a suffix of existing domain value.");
1352 if (Group()->IsPotentiallyCrossOriginIsolated()) {
1353 return IPC_FAIL(this, "Setting domain in a cross-origin isolated BC.");
1356 mDocumentPrincipal->SetDomain(aDomain);
1357 return IPC_OK();
1360 mozilla::ipc::IPCResult WindowGlobalParent::RecvReloadWithHttpsOnlyException() {
1361 nsresult rv;
1362 nsCOMPtr<nsIURI> currentUri = BrowsingContext()->Top()->GetCurrentURI();
1364 if (!currentUri) {
1365 return IPC_FAIL(this, "HTTPS-only mode: Failed to get current URI");
1368 bool isViewSource = currentUri->SchemeIs("view-source");
1370 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentUri);
1371 nsCOMPtr<nsIURI> innerURI;
1372 if (isViewSource) {
1373 nestedURI->GetInnerURI(getter_AddRefs(innerURI));
1374 } else {
1375 innerURI = currentUri;
1378 if (!innerURI->SchemeIs("https") && !innerURI->SchemeIs("http")) {
1379 return IPC_FAIL(this, "HTTPS-only mode: Illegal state");
1382 // If the error page is within an iFrame, we create an exception for whatever
1383 // scheme the top-level site is currently on, because the user wants to
1384 // unbreak the iFrame and not the top-level page. When the error page shows up
1385 // on a top-level request, then we replace the scheme with http, because the
1386 // user wants to unbreak the whole page.
1387 nsCOMPtr<nsIURI> newURI;
1388 if (!BrowsingContext()->IsTop()) {
1389 newURI = innerURI;
1390 } else {
1391 Unused << NS_MutateURI(innerURI).SetScheme("http"_ns).Finalize(
1392 getter_AddRefs(newURI));
1395 OriginAttributes originAttributes =
1396 TopWindowContext()->DocumentPrincipal()->OriginAttributesRef();
1398 originAttributes.SetFirstPartyDomain(true, newURI);
1400 nsCOMPtr<nsIPermissionManager> permMgr =
1401 components::PermissionManager::Service();
1402 if (!permMgr) {
1403 return IPC_FAIL(
1404 this, "HTTPS-only mode: Failed to get Permission Manager service");
1407 nsCOMPtr<nsIPrincipal> principal =
1408 BasePrincipal::CreateContentPrincipal(newURI, originAttributes);
1410 rv = permMgr->AddFromPrincipal(
1411 principal, "https-only-load-insecure"_ns,
1412 nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION,
1413 nsIPermissionManager::EXPIRE_SESSION, 0);
1415 if (NS_FAILED(rv)) {
1416 return IPC_FAIL(
1417 this, "HTTPS-only mode: Failed to add permission to the principal");
1420 nsCOMPtr<nsIURI> insecureURI = newURI;
1421 if (isViewSource) {
1422 nsAutoCString spec;
1423 MOZ_ALWAYS_SUCCEEDS(newURI->GetSpec(spec));
1424 if (NS_FAILED(
1425 NS_NewURI(getter_AddRefs(insecureURI), "view-source:"_ns + spec))) {
1426 return IPC_FAIL(
1427 this, "HTTPS-only mode: Failed to re-construct view-source URI");
1431 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(insecureURI);
1432 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
1433 loadState->SetLoadType(LOAD_NORMAL_REPLACE);
1435 RefPtr<CanonicalBrowsingContext> topBC = BrowsingContext()->Top();
1436 topBC->LoadURI(loadState, /* setNavigating */ true);
1438 return IPC_OK();
1441 IPCResult WindowGlobalParent::RecvDiscoverIdentityCredentialFromExternalSource(
1442 const IdentityCredentialRequestOptions& aOptions,
1443 const DiscoverIdentityCredentialFromExternalSourceResolver& aResolver) {
1444 IdentityCredential::DiscoverFromExternalSourceInMainProcess(
1445 DocumentPrincipal(), this->BrowsingContext(), aOptions)
1446 ->Then(
1447 GetCurrentSerialEventTarget(), __func__,
1448 [aResolver](const IPCIdentityCredential& aResult) {
1449 return aResolver(Some(aResult));
1451 [aResolver](nsresult aErr) { aResolver(Nothing()); });
1452 return IPC_OK();
1455 IPCResult WindowGlobalParent::RecvGetStorageAccessPermission(
1456 GetStorageAccessPermissionResolver&& aResolve) {
1457 WindowGlobalParent* top = TopWindowContext();
1458 if (!top) {
1459 return IPC_FAIL_NO_REASON(this);
1461 nsIPrincipal* topPrincipal = top->DocumentPrincipal();
1462 nsIPrincipal* principal = DocumentPrincipal();
1463 uint32_t result;
1464 nsresult rv = AntiTrackingUtils::TestStoragePermissionInParent(
1465 topPrincipal, principal, &result);
1466 if (NS_WARN_IF(NS_FAILED(rv))) {
1467 aResolve(nsIPermissionManager::UNKNOWN_ACTION);
1468 return IPC_OK();
1471 aResolve(result);
1472 return IPC_OK();
1475 void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
1476 if (GetBrowsingContext()->IsTopContent()) {
1477 Telemetry::Accumulate(Telemetry::ORB_DID_EVER_BLOCK_RESPONSE,
1478 mShouldReportHasBlockedOpaqueResponse);
1481 if (mPageUseCountersWindow) {
1482 mPageUseCountersWindow->FinishAccumulatingPageUseCounters();
1483 mPageUseCountersWindow = nullptr;
1486 if (GetBrowsingContext()->IsTopContent() &&
1487 !mDocumentPrincipal->SchemeIs("about")) {
1488 // Record the page load
1489 uint32_t pageLoaded = 1;
1490 Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1492 // Record the mixed content status of the docshell in Telemetry
1493 enum {
1494 NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1495 MIXED_DISPLAY_CONTENT =
1496 1, // The page attempted to load Mixed Display Content
1497 MIXED_ACTIVE_CONTENT =
1498 2, // The page attempted to load Mixed Active Content
1499 MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed
1500 // Display & Mixed Active Content
1503 bool hasMixedDisplay =
1504 mSecurityState &
1505 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
1506 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
1507 bool hasMixedActive =
1508 mSecurityState &
1509 (nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
1510 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
1512 uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1513 if (hasMixedDisplay && hasMixedActive) {
1514 mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1515 } else if (hasMixedActive) {
1516 mixedContentLevel = MIXED_ACTIVE_CONTENT;
1517 } else if (hasMixedDisplay) {
1518 mixedContentLevel = MIXED_DISPLAY_CONTENT;
1520 Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1522 if (GetDocTreeHadMedia()) {
1523 ScalarAdd(Telemetry::ScalarID::MEDIA_ELEMENT_IN_PAGE_COUNT, 1);
1527 ContentParent* cp = nullptr;
1528 if (!IsInProcess()) {
1529 cp = static_cast<ContentParent*>(Manager()->Manager());
1532 Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
1533 // Keep the WindowContext and our BrowsingContextGroup alive until other
1534 // processes have acknowledged it has been discarded.
1535 Group()->AddKeepAlive();
1536 auto callback = [self = RefPtr{this}](auto) {
1537 self->Group()->RemoveKeepAlive();
1539 otherContent->SendDiscardWindowContext(InnerWindowId(), callback, callback);
1542 // Note that our WindowContext has become discarded.
1543 WindowContext::Discard();
1545 // Report content blocking log when destroyed.
1546 // There shouldn't have any content blocking log when a document is loaded in
1547 // the parent process(See NotifyContentBlockingEvent), so we could skip
1548 // reporting log when it is in-process.
1549 if (!IsInProcess()) {
1550 RefPtr<BrowserParent> browserParent =
1551 static_cast<BrowserParent*>(Manager());
1552 if (browserParent) {
1553 nsCOMPtr<nsILoadContext> loadContext = browserParent->GetLoadContext();
1554 if (loadContext && !loadContext->UsePrivateBrowsing() &&
1555 BrowsingContext()->IsTopContent()) {
1556 GetContentBlockingLog()->ReportLog(DocumentPrincipal());
1558 if (mDocumentURI && (net::SchemeIsHTTP(mDocumentURI) ||
1559 net::SchemeIsHTTPS(mDocumentURI))) {
1560 GetContentBlockingLog()->ReportCanvasFingerprintingLog(
1561 DocumentPrincipal());
1562 GetContentBlockingLog()->ReportFontFingerprintingLog(
1563 DocumentPrincipal());
1564 GetContentBlockingLog()->ReportEmailTrackingLog(DocumentPrincipal());
1570 // Destroy our JSWindowActors, and reject any pending queries.
1571 JSActorDidDestroy();
1573 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1574 if (obs) {
1575 obs->NotifyObservers(ToSupports(this), "window-global-destroyed", nullptr);
1578 if (mOriginCounter) {
1579 mOriginCounter->Accumulate();
1583 WindowGlobalParent::~WindowGlobalParent() = default;
1585 JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
1586 JS::Handle<JSObject*> aGivenProto) {
1587 return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
1590 nsIGlobalObject* WindowGlobalParent::GetParentObject() {
1591 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
1594 nsIDOMProcessParent* WindowGlobalParent::GetDomProcess() {
1595 if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
1596 return browserParent->Manager();
1598 return InProcessParent::Singleton();
1601 void WindowGlobalParent::DidBecomeCurrentWindowGlobal(bool aCurrent) {
1602 WindowGlobalParent* top = BrowsingContext()->GetTopWindowContext();
1603 if (top && top->mOriginCounter) {
1604 top->mOriginCounter->UpdateSiteOriginsFrom(this,
1605 /* aIncrease = */ aCurrent);
1608 if (!aCurrent && Fullscreen()) {
1609 ExitTopChromeDocumentFullscreen();
1613 bool WindowGlobalParent::ShouldTrackSiteOriginTelemetry() {
1614 CanonicalBrowsingContext* bc = BrowsingContext();
1616 if (!bc->IsTopContent()) {
1617 return false;
1620 RefPtr<BrowserParent> browserParent = GetBrowserParent();
1621 if (!browserParent ||
1622 !IsWebRemoteType(browserParent->Manager()->GetRemoteType())) {
1623 return false;
1626 return DocumentPrincipal()->GetIsContentPrincipal();
1629 void WindowGlobalParent::AddSecurityState(uint32_t aStateFlags) {
1630 MOZ_ASSERT(TopWindowContext() == this);
1631 MOZ_ASSERT((aStateFlags &
1632 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
1633 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
1634 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT |
1635 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT |
1636 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED |
1637 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED |
1638 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED_FIRST)) ==
1639 aStateFlags,
1640 "Invalid flags specified!");
1642 if ((mSecurityState & aStateFlags) == aStateFlags) {
1643 return;
1646 mSecurityState |= aStateFlags;
1648 if (GetBrowsingContext()->GetCurrentWindowGlobal() == this) {
1649 GetBrowsingContext()->UpdateSecurityState();
1653 bool WindowGlobalParent::HasActivePeerConnections() {
1654 MOZ_ASSERT(TopWindowContext() == this,
1655 "mNumOfProcessesWithActivePeerConnections is set only "
1656 "in the top window context");
1657 return mNumOfProcessesWithActivePeerConnections > 0;
1660 void WindowGlobalParent::ExitTopChromeDocumentFullscreen() {
1661 RefPtr<CanonicalBrowsingContext> chromeTop =
1662 BrowsingContext()->TopCrossChromeBoundary();
1663 if (Document* chromeDoc = chromeTop->GetDocument()) {
1664 Document::ClearPendingFullscreenRequests(chromeDoc);
1665 if (chromeDoc->Fullscreen()) {
1666 // This only clears the DOM fullscreen, will not exit from browser UI
1667 // fullscreen mode.
1668 Document::AsyncExitFullscreen(chromeDoc);
1673 void WindowGlobalParent::SetShouldReportHasBlockedOpaqueResponse(
1674 nsContentPolicyType aContentPolicy) {
1675 // It's always okay to block TYPE_BEACON, TYPE_PING and TYPE_CSP_REPORT in
1676 // the parent process because content processes can do nothing to their
1677 // responses. Hence excluding them from the telemetry as blocking
1678 // them have no webcompat concerns.
1679 if (aContentPolicy != nsIContentPolicy::TYPE_BEACON &&
1680 aContentPolicy != nsIContentPolicy::TYPE_PING &&
1681 aContentPolicy != nsIContentPolicy::TYPE_CSP_REPORT) {
1682 if (IsTop()) {
1683 mShouldReportHasBlockedOpaqueResponse = true;
1688 IPCResult WindowGlobalParent::RecvSetCookies(
1689 const nsCString& aBaseDomain, const OriginAttributes& aOriginAttributes,
1690 nsIURI* aHost, bool aFromHttp, const nsTArray<CookieStruct>& aCookies) {
1691 // Get CookieServiceParent via
1692 // ContentParent->NeckoParent->CookieServiceParent.
1693 ContentParent* contentParent = GetContentParent();
1694 NS_ENSURE_TRUE(contentParent, IPC_OK());
1696 net::PNeckoParent* neckoParent =
1697 LoneManagedOrNullAsserts(contentParent->ManagedPNeckoParent());
1698 NS_ENSURE_TRUE(neckoParent, IPC_OK());
1699 net::PCookieServiceParent* csParent =
1700 LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
1701 NS_ENSURE_TRUE(csParent, IPC_OK());
1702 auto* cs = static_cast<net::CookieServiceParent*>(csParent);
1704 return cs->SetCookies(aBaseDomain, aOriginAttributes, aHost, aFromHttp,
1705 aCookies, GetBrowsingContext());
1708 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowGlobalParent, WindowContext,
1709 mPageUseCountersWindow)
1711 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalParent,
1712 WindowContext)
1713 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1715 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalParent)
1716 NS_INTERFACE_MAP_END_INHERITING(WindowContext)
1718 NS_IMPL_ADDREF_INHERITED(WindowGlobalParent, WindowContext)
1719 NS_IMPL_RELEASE_INHERITED(WindowGlobalParent, WindowContext)
1721 } // namespace mozilla::dom