Bug 1843230 - Remove IsWin8OrLater checks from dom/geolocation/ r=emk
[gecko.git] / dom / ipc / WindowGlobalParent.cpp
blob63823f5725e4953e6b7ca0359d794ff0223d7df3
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/AsyncEventDispatcher.h"
12 #include "mozilla/ClearOnShutdown.h"
13 #include "mozilla/ContentBlockingAllowList.h"
14 #include "mozilla/dom/InProcessParent.h"
15 #include "mozilla/dom/BrowserBridgeParent.h"
16 #include "mozilla/dom/BrowsingContextGroup.h"
17 #include "mozilla/dom/CanonicalBrowsingContext.h"
18 #include "mozilla/dom/ClientInfo.h"
19 #include "mozilla/dom/ClientIPCTypes.h"
20 #include "mozilla/dom/ContentChild.h"
21 #include "mozilla/dom/ContentParent.h"
22 #include "mozilla/dom/BrowserHost.h"
23 #include "mozilla/dom/BrowserParent.h"
24 #include "mozilla/dom/IdentityCredential.h"
25 #include "mozilla/dom/MediaController.h"
26 #include "mozilla/dom/WindowGlobalChild.h"
27 #include "mozilla/dom/ChromeUtils.h"
28 #include "mozilla/dom/ipc/IdType.h"
29 #include "mozilla/dom/ipc/StructuredCloneData.h"
30 #include "mozilla/Components.h"
31 #include "mozilla/ScopeExit.h"
32 #include "mozilla/ServoCSSParser.h"
33 #include "mozilla/ServoStyleSet.h"
34 #include "mozilla/StaticPrefs_dom.h"
35 #include "mozilla/StaticPrefs_network.h"
36 #include "mozilla/Telemetry.h"
37 #include "mozilla/Variant.h"
38 #include "nsContentUtils.h"
39 #include "nsDocShell.h"
40 #include "nsDocShellLoadState.h"
41 #include "nsError.h"
42 #include "nsFrameLoader.h"
43 #include "nsFrameLoaderOwner.h"
44 #include "nsGlobalWindowInner.h"
45 #include "nsQueryObject.h"
46 #include "nsNetUtil.h"
47 #include "nsSandboxFlags.h"
48 #include "nsSerializationHelper.h"
49 #include "nsIBrowser.h"
50 #include "nsIEffectiveTLDService.h"
51 #include "nsIHttpsOnlyModePermission.h"
52 #include "nsIPromptCollection.h"
53 #include "nsITimer.h"
54 #include "nsITransportSecurityInfo.h"
55 #include "nsISharePicker.h"
56 #include "nsIURIMutator.h"
57 #include "nsIWebProgressListener.h"
59 #include "mozilla/dom/DOMException.h"
60 #include "mozilla/dom/DOMExceptionBinding.h"
62 #include "mozilla/dom/JSActorService.h"
63 #include "mozilla/dom/JSWindowActorBinding.h"
64 #include "mozilla/dom/JSWindowActorParent.h"
66 #include "SessionStoreFunctions.h"
67 #include "nsIXPConnect.h"
68 #include "nsImportModule.h"
70 #include "mozilla/dom/PBackgroundSessionStorageCache.h"
72 using namespace mozilla::ipc;
73 using namespace mozilla::dom::ipc;
75 extern mozilla::LazyLogModule gSHIPBFCacheLog;
76 extern mozilla::LazyLogModule gUseCountersLog;
78 namespace mozilla::dom {
80 WindowGlobalParent::WindowGlobalParent(
81 CanonicalBrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
82 uint64_t aOuterWindowId, FieldValues&& aInit)
83 : WindowContext(aBrowsingContext, aInnerWindowId, aOuterWindowId,
84 std::move(aInit)),
85 mIsInitialDocument(false),
86 mSandboxFlags(0),
87 mDocumentHasLoaded(false),
88 mDocumentHasUserInteracted(false),
89 mBlockAllMixedContent(false),
90 mUpgradeInsecureRequests(false) {
91 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(), "Parent process only");
94 already_AddRefed<WindowGlobalParent> WindowGlobalParent::CreateDisconnected(
95 const WindowGlobalInit& aInit) {
96 RefPtr<CanonicalBrowsingContext> browsingContext =
97 CanonicalBrowsingContext::Get(aInit.context().mBrowsingContextId);
98 if (NS_WARN_IF(!browsingContext)) {
99 return nullptr;
102 RefPtr<WindowGlobalParent> wgp =
103 GetByInnerWindowId(aInit.context().mInnerWindowId);
104 MOZ_RELEASE_ASSERT(!wgp, "Creating duplicate WindowGlobalParent");
106 FieldValues fields(aInit.context().mFields);
107 wgp =
108 new WindowGlobalParent(browsingContext, aInit.context().mInnerWindowId,
109 aInit.context().mOuterWindowId, std::move(fields));
110 wgp->mDocumentPrincipal = aInit.principal();
111 wgp->mDocumentURI = aInit.documentURI();
112 wgp->mIsInitialDocument = aInit.isInitialDocument();
113 wgp->mBlockAllMixedContent = aInit.blockAllMixedContent();
114 wgp->mUpgradeInsecureRequests = aInit.upgradeInsecureRequests();
115 wgp->mSandboxFlags = aInit.sandboxFlags();
116 wgp->mHttpsOnlyStatus = aInit.httpsOnlyStatus();
117 wgp->mSecurityInfo = aInit.securityInfo();
118 net::CookieJarSettings::Deserialize(aInit.cookieJarSettings(),
119 getter_AddRefs(wgp->mCookieJarSettings));
120 MOZ_RELEASE_ASSERT(wgp->mDocumentPrincipal, "Must have a valid principal");
122 nsresult rv = wgp->SetDocumentStoragePrincipal(aInit.storagePrincipal());
123 MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
124 "Must succeed in setting storage principal");
126 return wgp.forget();
129 void WindowGlobalParent::Init() {
130 MOZ_ASSERT(Manager(), "Should have a manager!");
132 // Invoke our base class' `Init` method. This will register us in
133 // `gWindowContexts`.
134 WindowContext::Init();
136 // Determine which content process the window global is coming from.
137 dom::ContentParentId processId(0);
138 ContentParent* cp = nullptr;
139 if (!IsInProcess()) {
140 cp = static_cast<ContentParent*>(Manager()->Manager());
141 processId = cp->ChildID();
143 // Ensure the content process has permissions for this principal.
144 cp->TransmitPermissionsForPrincipal(mDocumentPrincipal);
147 MOZ_DIAGNOSTIC_ASSERT(
148 !BrowsingContext()->GetParent() ||
149 BrowsingContext()->GetEmbedderInnerWindowId(),
150 "When creating a non-root WindowGlobalParent, the WindowGlobalParent "
151 "for our embedder should've already been created.");
153 // Ensure we have a document URI
154 if (!mDocumentURI) {
155 NS_NewURI(getter_AddRefs(mDocumentURI), "about:blank");
158 // NOTE: `cp` may be nullptr, but that's OK, we need to tell every other
159 // process in our group in that case.
160 IPCInitializer ipcinit = GetIPCInitializer();
161 Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
162 Unused << otherContent->SendCreateWindowContext(ipcinit);
165 if (!BrowsingContext()->IsDiscarded()) {
166 MOZ_ALWAYS_SUCCEEDS(
167 BrowsingContext()->SetCurrentInnerWindowId(InnerWindowId()));
169 Unused << SendSetContainerFeaturePolicy(
170 BrowsingContext()->GetContainerFeaturePolicy());
173 if (BrowsingContext()->IsTopContent()) {
174 // For top level sandboxed documents we need to create a new principal
175 // from URI + OriginAttributes, since the document principal will be a
176 // NullPrincipal. See Bug 1654546.
177 if (mSandboxFlags & SANDBOXED_ORIGIN) {
178 ContentBlockingAllowList::RecomputePrincipal(
179 mDocumentURI, mDocumentPrincipal->OriginAttributesRef(),
180 getter_AddRefs(mDocContentBlockingAllowListPrincipal));
181 } else {
182 ContentBlockingAllowList::ComputePrincipal(
183 mDocumentPrincipal,
184 getter_AddRefs(mDocContentBlockingAllowListPrincipal));
188 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
189 if (obs) {
190 obs->NotifyObservers(ToSupports(this), "window-global-created", nullptr);
193 if (!BrowsingContext()->IsDiscarded() && ShouldTrackSiteOriginTelemetry()) {
194 mOriginCounter.emplace();
195 mOriginCounter->UpdateSiteOriginsFrom(this, /* aIncrease = */ true);
199 void WindowGlobalParent::OriginCounter::UpdateSiteOriginsFrom(
200 WindowGlobalParent* aParent, bool aIncrease) {
201 MOZ_RELEASE_ASSERT(aParent);
203 if (aParent->DocumentPrincipal()->GetIsContentPrincipal()) {
204 nsAutoCString origin;
205 aParent->DocumentPrincipal()->GetSiteOrigin(origin);
207 if (aIncrease) {
208 int32_t& count = mOriginMap.LookupOrInsert(origin);
209 count += 1;
210 mMaxOrigins = std::max(mMaxOrigins, mOriginMap.Count());
211 } else if (auto entry = mOriginMap.Lookup(origin)) {
212 entry.Data() -= 1;
214 if (entry.Data() == 0) {
215 entry.Remove();
221 void WindowGlobalParent::OriginCounter::Accumulate() {
222 mozilla::Telemetry::Accumulate(
223 mozilla::Telemetry::HistogramID::
224 FX_NUMBER_OF_UNIQUE_SITE_ORIGINS_PER_DOCUMENT,
225 mMaxOrigins);
227 mMaxOrigins = 0;
228 mOriginMap.Clear();
231 /* static */
232 already_AddRefed<WindowGlobalParent> WindowGlobalParent::GetByInnerWindowId(
233 uint64_t aInnerWindowId) {
234 if (!XRE_IsParentProcess()) {
235 return nullptr;
238 return WindowContext::GetById(aInnerWindowId).downcast<WindowGlobalParent>();
241 already_AddRefed<WindowGlobalChild> WindowGlobalParent::GetChildActor() {
242 if (!CanSend()) {
243 return nullptr;
245 IProtocol* otherSide = InProcessParent::ChildActorFor(this);
246 return do_AddRef(static_cast<WindowGlobalChild*>(otherSide));
249 BrowserParent* WindowGlobalParent::GetBrowserParent() {
250 if (IsInProcess() || !CanSend()) {
251 return nullptr;
253 return static_cast<BrowserParent*>(Manager());
256 ContentParent* WindowGlobalParent::GetContentParent() {
257 if (IsInProcess() || !CanSend()) {
258 return nullptr;
260 return static_cast<ContentParent*>(Manager()->Manager());
263 already_AddRefed<nsFrameLoader> WindowGlobalParent::GetRootFrameLoader() {
264 dom::BrowsingContext* top = BrowsingContext()->Top();
266 RefPtr<nsFrameLoaderOwner> frameLoaderOwner =
267 do_QueryObject(top->GetEmbedderElement());
268 if (frameLoaderOwner) {
269 return frameLoaderOwner->GetFrameLoader();
271 return nullptr;
274 uint64_t WindowGlobalParent::ContentParentId() {
275 RefPtr<BrowserParent> browserParent = GetBrowserParent();
276 return browserParent ? browserParent->Manager()->ChildID() : 0;
279 int32_t WindowGlobalParent::OsPid() {
280 RefPtr<BrowserParent> browserParent = GetBrowserParent();
281 return browserParent ? browserParent->Manager()->Pid() : -1;
284 // A WindowGlobalPaernt is the root in its process if it has no parent, or its
285 // embedder is in a different process.
286 bool WindowGlobalParent::IsProcessRoot() {
287 if (!BrowsingContext()->GetParent()) {
288 return true;
291 RefPtr<WindowGlobalParent> embedder =
292 BrowsingContext()->GetEmbedderWindowGlobal();
293 if (NS_WARN_IF(!embedder)) {
294 return false;
297 return ContentParentId() != embedder->ContentParentId();
300 uint32_t WindowGlobalParent::ContentBlockingEvents() {
301 return GetContentBlockingLog()->GetContentBlockingEventsInLog();
304 void WindowGlobalParent::GetContentBlockingLog(nsAString& aLog) {
305 NS_ConvertUTF8toUTF16 log(GetContentBlockingLog()->Stringify());
306 aLog.Assign(std::move(log));
309 mozilla::ipc::IPCResult WindowGlobalParent::RecvLoadURI(
310 const MaybeDiscarded<dom::BrowsingContext>& aTargetBC,
311 nsDocShellLoadState* aLoadState, bool aSetNavigating) {
312 if (aTargetBC.IsNullOrDiscarded()) {
313 MOZ_LOG(
314 BrowsingContext::GetLog(), LogLevel::Debug,
315 ("ParentIPC: Trying to send a message with dead or detached context"));
316 return IPC_OK();
319 if (net::SchemeIsJavascript(aLoadState->URI())) {
320 return IPC_FAIL(this, "Illegal cross-process javascript: load attempt");
323 CanonicalBrowsingContext* targetBC = aTargetBC.get_canonical();
325 // FIXME: For cross-process loads, we should double check CanAccess() for the
326 // source browsing context in the parent process.
328 if (targetBC->Group() != BrowsingContext()->Group()) {
329 return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
332 // FIXME: We should really initiate the load in the parent before bouncing
333 // back down to the child.
335 targetBC->LoadURI(aLoadState, aSetNavigating);
336 return IPC_OK();
339 mozilla::ipc::IPCResult WindowGlobalParent::RecvInternalLoad(
340 nsDocShellLoadState* aLoadState) {
341 if (!aLoadState->Target().IsEmpty() ||
342 aLoadState->TargetBrowsingContext().IsNull()) {
343 return IPC_FAIL(this, "must already be retargeted");
345 if (aLoadState->TargetBrowsingContext().IsDiscarded()) {
346 MOZ_LOG(
347 BrowsingContext::GetLog(), LogLevel::Debug,
348 ("ParentIPC: Trying to send a message with dead or detached context"));
349 return IPC_OK();
352 if (net::SchemeIsJavascript(aLoadState->URI())) {
353 return IPC_FAIL(this, "Illegal cross-process javascript: load attempt");
356 CanonicalBrowsingContext* targetBC =
357 aLoadState->TargetBrowsingContext().get_canonical();
359 // FIXME: For cross-process loads, we should double check CanAccess() for the
360 // source browsing context in the parent process.
362 if (targetBC->Group() != BrowsingContext()->Group()) {
363 return IPC_FAIL(this, "Illegal cross-group BrowsingContext load");
366 // FIXME: We should really initiate the load in the parent before bouncing
367 // back down to the child.
369 targetBC->InternalLoad(aLoadState);
370 return IPC_OK();
373 IPCResult WindowGlobalParent::RecvUpdateDocumentURI(nsIURI* aURI) {
374 // XXX(nika): Assert that the URI change was one which makes sense (either
375 // about:blank -> a real URI, or a legal push/popstate URI change?)
376 mDocumentURI = aURI;
377 return IPC_OK();
380 nsresult WindowGlobalParent::SetDocumentStoragePrincipal(
381 nsIPrincipal* aNewDocumentStoragePrincipal) {
382 if (mDocumentPrincipal->Equals(aNewDocumentStoragePrincipal)) {
383 mDocumentStoragePrincipal = mDocumentPrincipal;
384 return NS_OK;
387 // Compare originNoSuffix to ensure it's equal.
388 nsCString noSuffix;
389 nsresult rv = mDocumentPrincipal->GetOriginNoSuffix(noSuffix);
390 if (NS_FAILED(rv)) {
391 return rv;
394 nsCString storageNoSuffix;
395 rv = aNewDocumentStoragePrincipal->GetOriginNoSuffix(storageNoSuffix);
396 if (NS_FAILED(rv)) {
397 return rv;
400 if (noSuffix != storageNoSuffix) {
401 return NS_ERROR_FAILURE;
404 if (!mDocumentPrincipal->OriginAttributesRef().EqualsIgnoringPartitionKey(
405 aNewDocumentStoragePrincipal->OriginAttributesRef())) {
406 return NS_ERROR_FAILURE;
409 mDocumentStoragePrincipal = aNewDocumentStoragePrincipal;
410 return NS_OK;
413 IPCResult WindowGlobalParent::RecvUpdateDocumentPrincipal(
414 nsIPrincipal* aNewDocumentPrincipal,
415 nsIPrincipal* aNewDocumentStoragePrincipal) {
416 if (!mDocumentPrincipal->Equals(aNewDocumentPrincipal)) {
417 return IPC_FAIL(this,
418 "Trying to reuse WindowGlobalParent but the principal of "
419 "the new document does not match the old one");
421 mDocumentPrincipal = aNewDocumentPrincipal;
423 if (NS_FAILED(SetDocumentStoragePrincipal(aNewDocumentStoragePrincipal))) {
424 return IPC_FAIL(this,
425 "Trying to reuse WindowGlobalParent but the principal of "
426 "the new document does not match the storage principal");
429 return IPC_OK();
431 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentTitle(
432 const nsString& aTitle) {
433 if (mDocumentTitle.isSome() && mDocumentTitle.value() == aTitle) {
434 return IPC_OK();
437 mDocumentTitle = Some(aTitle);
439 // Send a pagetitlechanged event only for changes to the title
440 // for top-level frames.
441 if (!BrowsingContext()->IsTop()) {
442 return IPC_OK();
445 // Notify media controller in order to update its default metadata.
446 if (BrowsingContext()->HasCreatedMediaController()) {
447 BrowsingContext()->GetMediaController()->NotifyPageTitleChanged();
450 Element* frameElement = BrowsingContext()->GetEmbedderElement();
451 if (!frameElement) {
452 return IPC_OK();
455 AsyncEventDispatcher::RunDOMEventWhenSafe(
456 *frameElement, u"pagetitlechanged"_ns, CanBubble::eYes,
457 ChromeOnlyDispatch::eYes);
459 return IPC_OK();
462 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateHttpsOnlyStatus(
463 uint32_t aHttpsOnlyStatus) {
464 mHttpsOnlyStatus = aHttpsOnlyStatus;
465 return IPC_OK();
468 IPCResult WindowGlobalParent::RecvUpdateDocumentHasLoaded(
469 bool aDocumentHasLoaded) {
470 mDocumentHasLoaded = aDocumentHasLoaded;
471 return IPC_OK();
474 IPCResult WindowGlobalParent::RecvUpdateDocumentHasUserInteracted(
475 bool aDocumentHasUserInteracted) {
476 mDocumentHasUserInteracted = aDocumentHasUserInteracted;
477 return IPC_OK();
480 IPCResult WindowGlobalParent::RecvUpdateSandboxFlags(uint32_t aSandboxFlags) {
481 mSandboxFlags = aSandboxFlags;
482 return IPC_OK();
485 IPCResult WindowGlobalParent::RecvUpdateDocumentCspSettings(
486 bool aBlockAllMixedContent, bool aUpgradeInsecureRequests) {
487 mBlockAllMixedContent = aBlockAllMixedContent;
488 mUpgradeInsecureRequests = aUpgradeInsecureRequests;
489 return IPC_OK();
492 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetClientInfo(
493 const IPCClientInfo& aIPCClientInfo) {
494 mClientInfo = Some(ClientInfo(aIPCClientInfo));
495 return IPC_OK();
498 IPCResult WindowGlobalParent::RecvDestroy() {
499 // Make a copy so that we can avoid potential iterator invalidation when
500 // calling the user-provided Destroy() methods.
501 JSActorWillDestroy();
503 if (CanSend()) {
504 RefPtr<BrowserParent> browserParent = GetBrowserParent();
505 if (!browserParent || !browserParent->IsDestroyed()) {
506 Unused << Send__delete__(this);
509 return IPC_OK();
512 IPCResult WindowGlobalParent::RecvRawMessage(
513 const JSActorMessageMeta& aMeta, const Maybe<ClonedMessageData>& aData,
514 const Maybe<ClonedMessageData>& aStack) {
515 Maybe<StructuredCloneData> data;
516 if (aData) {
517 data.emplace();
518 data->BorrowFromClonedMessageData(*aData);
520 Maybe<StructuredCloneData> stack;
521 if (aStack) {
522 stack.emplace();
523 stack->BorrowFromClonedMessageData(*aStack);
525 ReceiveRawMessage(aMeta, std::move(data), std::move(stack));
526 return IPC_OK();
529 const nsACString& WindowGlobalParent::GetRemoteType() {
530 if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
531 return browserParent->Manager()->GetRemoteType();
534 return NOT_REMOTE_TYPE;
537 void WindowGlobalParent::NotifyContentBlockingEvent(
538 uint32_t aEvent, nsIRequest* aRequest, bool aBlocked,
539 const nsACString& aTrackingOrigin,
540 const nsTArray<nsCString>& aTrackingFullHashes,
541 const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
542 aReason) {
543 MOZ_ASSERT(NS_IsMainThread());
544 DebugOnly<bool> isCookiesBlocked =
545 aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
546 aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER ||
547 (aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_FOREIGN &&
548 StaticPrefs::network_cookie_rejectForeignWithExceptions_enabled());
549 MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
550 MOZ_ASSERT_IF(!isCookiesBlocked, aReason.isNothing());
551 MOZ_ASSERT_IF(isCookiesBlocked && !aBlocked, aReason.isSome());
552 MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
553 // TODO: temporarily remove this until we find the root case of Bug 1609144
554 // MOZ_DIAGNOSTIC_ASSERT_IF(XRE_IsE10sParentProcess(), !IsInProcess());
556 // Return early if this WindowGlobalParent is in process.
557 if (IsInProcess()) {
558 return;
561 Maybe<uint32_t> event = GetContentBlockingLog()->RecordLogParent(
562 aTrackingOrigin, aEvent, aBlocked, aReason, aTrackingFullHashes);
564 // Notify the OnContentBlockingEvent if necessary.
565 if (event) {
566 if (auto* webProgress = GetBrowsingContext()->GetWebProgress()) {
567 webProgress->OnContentBlockingEvent(webProgress, aRequest, event.value());
572 already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetActor(
573 JSContext* aCx, const nsACString& aName, ErrorResult& aRv) {
574 return JSActorManager::GetActor(aCx, aName, aRv)
575 .downcast<JSWindowActorParent>();
578 already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetExistingActor(
579 const nsACString& aName) {
580 return JSActorManager::GetExistingActor(aName)
581 .downcast<JSWindowActorParent>();
584 already_AddRefed<JSActor> WindowGlobalParent::InitJSActor(
585 JS::Handle<JSObject*> aMaybeActor, const nsACString& aName,
586 ErrorResult& aRv) {
587 RefPtr<JSWindowActorParent> actor;
588 if (aMaybeActor.get()) {
589 aRv = UNWRAP_OBJECT(JSWindowActorParent, aMaybeActor.get(), actor);
590 if (aRv.Failed()) {
591 return nullptr;
593 } else {
594 actor = new JSWindowActorParent();
597 MOZ_RELEASE_ASSERT(!actor->GetManager(),
598 "mManager was already initialized once!");
599 actor->Init(aName, this);
600 return actor.forget();
603 bool WindowGlobalParent::IsCurrentGlobal() {
604 return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() == this;
607 namespace {
609 class ShareHandler final : public PromiseNativeHandler {
610 public:
611 explicit ShareHandler(
612 mozilla::dom::WindowGlobalParent::ShareResolver&& aResolver)
613 : mResolver(std::move(aResolver)) {}
615 NS_DECL_ISUPPORTS
617 public:
618 virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
619 ErrorResult& aRv) override {
620 mResolver(NS_OK);
623 virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
624 ErrorResult& aRv) override {
625 if (NS_WARN_IF(!aValue.isObject())) {
626 mResolver(NS_ERROR_FAILURE);
627 return;
630 // nsresult is stored as Exception internally in Promise
631 JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
632 RefPtr<DOMException> unwrapped;
633 nsresult rv = UNWRAP_OBJECT(DOMException, &obj, unwrapped);
634 if (NS_WARN_IF(NS_FAILED(rv))) {
635 mResolver(NS_ERROR_FAILURE);
636 return;
639 mResolver(unwrapped->GetResult());
642 private:
643 ~ShareHandler() = default;
645 mozilla::dom::WindowGlobalParent::ShareResolver mResolver;
648 NS_IMPL_ISUPPORTS0(ShareHandler)
650 } // namespace
652 mozilla::ipc::IPCResult WindowGlobalParent::RecvGetContentBlockingEvents(
653 WindowGlobalParent::GetContentBlockingEventsResolver&& aResolver) {
654 uint32_t events = GetContentBlockingLog()->GetContentBlockingEventsInLog();
655 aResolver(events);
657 return IPC_OK();
660 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateCookieJarSettings(
661 const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
662 net::CookieJarSettings::Deserialize(aCookieJarSettingsArgs,
663 getter_AddRefs(mCookieJarSettings));
664 return IPC_OK();
667 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentSecurityInfo(
668 nsITransportSecurityInfo* aSecurityInfo) {
669 mSecurityInfo = aSecurityInfo;
670 return IPC_OK();
673 mozilla::ipc::IPCResult WindowGlobalParent::RecvShare(
674 IPCWebShareData&& aData, WindowGlobalParent::ShareResolver&& aResolver) {
675 // Widget Layer handoff...
676 nsCOMPtr<nsISharePicker> sharePicker =
677 do_GetService("@mozilla.org/sharepicker;1");
678 if (!sharePicker) {
679 aResolver(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
680 return IPC_OK();
683 // Initialize the ShareWidget
684 RefPtr<BrowserParent> parent = GetBrowserParent();
685 nsCOMPtr<mozIDOMWindowProxy> openerWindow;
686 if (parent) {
687 openerWindow = parent->GetParentWindowOuter();
688 if (!openerWindow) {
689 aResolver(NS_ERROR_FAILURE);
690 return IPC_OK();
693 sharePicker->Init(openerWindow);
695 // And finally share the data...
696 RefPtr<Promise> promise;
697 nsresult rv = sharePicker->Share(aData.title(), aData.text(), aData.url(),
698 getter_AddRefs(promise));
699 if (NS_FAILED(rv)) {
700 aResolver(rv);
701 return IPC_OK();
704 // Handler finally awaits response...
705 RefPtr<ShareHandler> handler = new ShareHandler(std::move(aResolver));
706 promise->AppendNativeHandler(handler);
708 return IPC_OK();
711 namespace {
713 class CheckPermitUnloadRequest final : public PromiseNativeHandler,
714 public nsITimerCallback {
715 public:
716 CheckPermitUnloadRequest(WindowGlobalParent* aWGP, bool aHasInProcessBlocker,
717 nsIContentViewer::PermitUnloadAction aAction,
718 std::function<void(bool)>&& aResolver)
719 : mResolver(std::move(aResolver)),
720 mWGP(aWGP),
721 mAction(aAction),
722 mFoundBlocker(aHasInProcessBlocker) {}
724 void Run(ContentParent* aIgnoreProcess = nullptr, uint32_t aTimeout = 0) {
725 MOZ_ASSERT(mState == State::UNINITIALIZED);
726 mState = State::WAITING;
728 RefPtr<CheckPermitUnloadRequest> self(this);
730 AutoTArray<ContentParent*, 8> seen;
731 if (aIgnoreProcess) {
732 seen.AppendElement(aIgnoreProcess);
735 BrowsingContext* bc = mWGP->GetBrowsingContext();
736 bc->PreOrderWalk([&](dom::BrowsingContext* aBC) {
737 if (WindowGlobalParent* wgp =
738 aBC->Canonical()->GetCurrentWindowGlobal()) {
739 ContentParent* cp = wgp->GetContentParent();
740 if (wgp->HasBeforeUnload() && !seen.ContainsSorted(cp)) {
741 seen.InsertElementSorted(cp);
742 mPendingRequests++;
743 auto resolve = [self](bool blockNavigation) {
744 if (blockNavigation) {
745 self->mFoundBlocker = true;
747 self->ResolveRequest();
749 if (cp) {
750 cp->SendDispatchBeforeUnloadToSubtree(
751 bc, resolve, [self](auto) { self->ResolveRequest(); });
752 } else {
753 ContentChild::DispatchBeforeUnloadToSubtree(bc, resolve);
759 if (mPendingRequests && aTimeout) {
760 Unused << NS_NewTimerWithCallback(getter_AddRefs(mTimer), this, aTimeout,
761 nsITimer::TYPE_ONE_SHOT);
764 CheckDoneWaiting();
767 void ResolveRequest() {
768 mPendingRequests--;
769 CheckDoneWaiting();
772 NS_IMETHODIMP Notify(nsITimer* aTimer) override {
773 MOZ_ASSERT(aTimer == mTimer);
774 if (mState == State::WAITING) {
775 mState = State::TIMED_OUT;
776 CheckDoneWaiting();
778 return NS_OK;
781 void CheckDoneWaiting() {
782 // If we've found a blocker, we prompt immediately without waiting for
783 // further responses. The user's response applies to the entire navigation
784 // attempt, regardless of how many "beforeunload" listeners we call.
785 if (mState != State::TIMED_OUT &&
786 (mState != State::WAITING || (mPendingRequests && !mFoundBlocker))) {
787 return;
790 mState = State::PROMPTING;
792 // Clearing our reference to the timer will automatically cancel it if it's
793 // still running.
794 mTimer = nullptr;
796 if (!mFoundBlocker) {
797 SendReply(true);
798 return;
801 auto action = mAction;
802 if (StaticPrefs::dom_disable_beforeunload()) {
803 action = nsIContentViewer::eDontPromptAndUnload;
805 if (action != nsIContentViewer::ePrompt) {
806 SendReply(action == nsIContentViewer::eDontPromptAndUnload);
807 return;
810 // Handle any failure in prompting by aborting the navigation. See comment
811 // in nsContentViewer::PermitUnload for reasoning.
812 auto cleanup = MakeScopeExit([&]() { SendReply(false); });
814 if (nsCOMPtr<nsIPromptCollection> prompt =
815 do_GetService("@mozilla.org/embedcomp/prompt-collection;1")) {
816 RefPtr<Promise> promise;
817 prompt->AsyncBeforeUnloadCheck(mWGP->GetBrowsingContext(),
818 getter_AddRefs(promise));
820 if (!promise) {
821 return;
824 promise->AppendNativeHandler(this);
825 cleanup.release();
829 void SendReply(bool aAllow) {
830 MOZ_ASSERT(mState != State::REPLIED);
831 mResolver(aAllow);
832 mState = State::REPLIED;
835 void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
836 ErrorResult& aRv) override {
837 MOZ_ASSERT(mState == State::PROMPTING);
839 SendReply(JS::ToBoolean(aValue));
842 void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
843 ErrorResult& aRv) override {
844 MOZ_ASSERT(mState == State::PROMPTING);
846 SendReply(false);
849 NS_DECL_ISUPPORTS
851 private:
852 ~CheckPermitUnloadRequest() {
853 // We may get here without having sent a reply if the promise we're waiting
854 // on is destroyed without being resolved or rejected.
855 if (mState != State::REPLIED) {
856 SendReply(false);
860 enum class State : uint8_t {
861 UNINITIALIZED,
862 WAITING,
863 TIMED_OUT,
864 PROMPTING,
865 REPLIED,
868 std::function<void(bool)> mResolver;
870 RefPtr<WindowGlobalParent> mWGP;
871 nsCOMPtr<nsITimer> mTimer;
873 uint32_t mPendingRequests = 0;
875 nsIContentViewer::PermitUnloadAction mAction;
877 State mState = State::UNINITIALIZED;
879 bool mFoundBlocker = false;
882 NS_IMPL_ISUPPORTS(CheckPermitUnloadRequest, nsITimerCallback)
884 } // namespace
886 mozilla::ipc::IPCResult WindowGlobalParent::RecvCheckPermitUnload(
887 bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
888 CheckPermitUnloadResolver&& aResolver) {
889 if (!IsCurrentGlobal()) {
890 aResolver(false);
891 return IPC_OK();
894 auto request = MakeRefPtr<CheckPermitUnloadRequest>(
895 this, aHasInProcessBlocker, aAction, std::move(aResolver));
896 request->Run(/* aIgnoreProcess */ GetContentParent());
898 return IPC_OK();
901 already_AddRefed<Promise> WindowGlobalParent::PermitUnload(
902 PermitUnloadAction aAction, uint32_t aTimeout, mozilla::ErrorResult& aRv) {
903 nsIGlobalObject* global = GetParentObject();
904 RefPtr<Promise> promise = Promise::Create(global, aRv);
905 if (NS_WARN_IF(aRv.Failed())) {
906 return nullptr;
909 auto request = MakeRefPtr<CheckPermitUnloadRequest>(
910 this, /* aHasInProcessBlocker */ false,
911 nsIContentViewer::PermitUnloadAction(aAction),
912 [promise](bool aAllow) { promise->MaybeResolve(aAllow); });
913 request->Run(/* aIgnoreProcess */ nullptr, aTimeout);
915 return promise.forget();
918 void WindowGlobalParent::PermitUnload(std::function<void(bool)>&& aResolver) {
919 RefPtr<CheckPermitUnloadRequest> request = new CheckPermitUnloadRequest(
920 this, /* aHasInProcessBlocker */ false,
921 nsIContentViewer::PermitUnloadAction::ePrompt, std::move(aResolver));
922 request->Run();
925 already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
926 const DOMRect* aRect, double aScale, const nsACString& aBackgroundColor,
927 bool aResetScrollPosition, mozilla::ErrorResult& aRv) {
928 nsIGlobalObject* global = GetParentObject();
929 RefPtr<Promise> promise = Promise::Create(global, aRv);
930 if (NS_WARN_IF(aRv.Failed())) {
931 return nullptr;
934 nscolor color;
935 if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(0, 0, 0),
936 aBackgroundColor, &color,
937 nullptr, nullptr))) {
938 aRv = NS_ERROR_FAILURE;
939 return nullptr;
942 gfx::CrossProcessPaintFlags flags =
943 gfx::CrossProcessPaintFlags::UseHighQualityScaling;
944 if (!aRect) {
945 // If no explicit Rect was passed, we want the currently visible viewport.
946 flags |= gfx::CrossProcessPaintFlags::DrawView;
947 } else if (aResetScrollPosition) {
948 flags |= gfx::CrossProcessPaintFlags::ResetScrollPosition;
951 if (!gfx::CrossProcessPaint::Start(this, aRect, (float)aScale, color, flags,
952 promise)) {
953 aRv = NS_ERROR_FAILURE;
954 return nullptr;
956 return promise.forget();
959 void WindowGlobalParent::DrawSnapshotInternal(gfx::CrossProcessPaint* aPaint,
960 const Maybe<IntRect>& aRect,
961 float aScale,
962 nscolor aBackgroundColor,
963 uint32_t aFlags) {
964 auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor, aFlags);
966 RefPtr<gfx::CrossProcessPaint> paint(aPaint);
967 RefPtr<WindowGlobalParent> wgp(this);
968 promise->Then(
969 GetMainThreadSerialEventTarget(), __func__,
970 [paint, wgp](PaintFragment&& aFragment) {
971 paint->ReceiveFragment(wgp, std::move(aFragment));
973 [paint, wgp](ResponseRejectReason&& aReason) {
974 paint->LostFragment(wgp);
979 * Accumulated page use counter data for a given top-level content document.
981 struct PageUseCounters {
982 // The number of page use counter data messages we are still waiting for.
983 uint32_t mWaiting = 0;
985 // Whether we have received any page use counter data.
986 bool mReceivedAny = false;
988 // The accumulated page use counters.
989 UseCounters mUseCounters;
992 mozilla::ipc::IPCResult WindowGlobalParent::RecvExpectPageUseCounters(
993 const MaybeDiscarded<WindowContext>& aTop) {
994 if (aTop.IsNull()) {
995 return IPC_FAIL(this, "aTop must not be null");
998 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
999 ("Expect page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
1000 InnerWindowId(), aTop.ContextId()));
1002 // We've been called to indicate that the document in our window intends
1003 // to send use counter data to accumulate towards the top-level document's
1004 // page use counters. This causes us to wait for this window to go away
1005 // (in WindowGlobalParent::ActorDestroy) before reporting the page use
1006 // counters via Telemetry.
1007 RefPtr<WindowGlobalParent> page =
1008 static_cast<WindowGlobalParent*>(aTop.GetMaybeDiscarded());
1009 if (!page || page->mSentPageUseCounters) {
1010 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1011 (" > too late, won't report page use counters for this straggler"));
1012 return IPC_OK();
1015 if (mPageUseCountersWindow) {
1016 if (mPageUseCountersWindow != page) {
1017 return IPC_FAIL(this,
1018 "ExpectPageUseCounters called on the same "
1019 "WindowContext with a different aTop value");
1022 // We can get called with the same aTop value more than once, e.g. for
1023 // initial about:blank documents and then subsequent "real" documents loaded
1024 // into the same window. We must note each source window only once.
1025 return IPC_OK();
1028 // Note that the top-level document must wait for one more window's use
1029 // counters before reporting via Telemetry.
1030 mPageUseCountersWindow = page;
1031 if (!page->mPageUseCounters) {
1032 page->mPageUseCounters = MakeUnique<PageUseCounters>();
1034 ++page->mPageUseCounters->mWaiting;
1036 MOZ_LOG(
1037 gUseCountersLog, LogLevel::Debug,
1038 (" > top-level now waiting on %d\n", page->mPageUseCounters->mWaiting));
1040 return IPC_OK();
1043 mozilla::ipc::IPCResult WindowGlobalParent::RecvAccumulatePageUseCounters(
1044 const UseCounters& aUseCounters) {
1045 // We've been called to accumulate use counter data into the page use counters
1046 // for the document in mPageUseCountersWindow.
1048 MOZ_LOG(
1049 gUseCountersLog, LogLevel::Debug,
1050 ("Accumulate page use counters: WindowContext %" PRIu64 " -> %" PRIu64,
1051 InnerWindowId(),
1052 mPageUseCountersWindow ? mPageUseCountersWindow->InnerWindowId() : 0));
1054 if (!mPageUseCountersWindow || mPageUseCountersWindow->mSentPageUseCounters) {
1055 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1056 (" > too late, won't report page use counters for this straggler"));
1057 return IPC_OK();
1060 MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters);
1061 MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters->mWaiting > 0);
1063 mPageUseCountersWindow->mPageUseCounters->mUseCounters |= aUseCounters;
1064 mPageUseCountersWindow->mPageUseCounters->mReceivedAny = true;
1065 return IPC_OK();
1068 // This is called on the top-level WindowGlobal, i.e. the one that is
1069 // accumulating the page use counters, not the (potentially descendant) window
1070 // that has finished providing use counter data.
1071 void WindowGlobalParent::FinishAccumulatingPageUseCounters() {
1072 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1073 ("Stop expecting page use counters: -> WindowContext %" PRIu64,
1074 InnerWindowId()));
1076 if (!mPageUseCounters) {
1077 MOZ_ASSERT_UNREACHABLE("Not expecting page use counter data");
1078 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1079 (" > not expecting page use counter data"));
1080 return;
1083 MOZ_ASSERT(mPageUseCounters->mWaiting > 0);
1084 --mPageUseCounters->mWaiting;
1086 if (mPageUseCounters->mWaiting > 0) {
1087 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1088 (" > now waiting on %d", mPageUseCounters->mWaiting));
1089 return;
1092 if (mPageUseCounters->mReceivedAny) {
1093 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1094 (" > reporting [%s]",
1095 nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
1097 Maybe<nsCString> urlForLogging;
1098 const bool dumpCounters = StaticPrefs::dom_use_counters_dump_page();
1099 if (dumpCounters) {
1100 urlForLogging.emplace(
1101 nsContentUtils::TruncatedURLForDisplay(mDocumentURI));
1104 Telemetry::Accumulate(Telemetry::TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED, 1);
1106 for (int32_t c = 0; c < eUseCounter_Count; ++c) {
1107 auto uc = static_cast<UseCounter>(c);
1108 if (!mPageUseCounters->mUseCounters[uc]) {
1109 continue;
1112 auto id = static_cast<Telemetry::HistogramID>(
1113 Telemetry::HistogramFirstUseCounter + uc * 2 + 1);
1114 if (dumpCounters) {
1115 printf_stderr("USE_COUNTER_PAGE: %s - %s\n",
1116 Telemetry::GetHistogramName(id), urlForLogging->get());
1118 Telemetry::Accumulate(id, 1);
1120 } else {
1121 MOZ_LOG(gUseCountersLog, LogLevel::Debug,
1122 (" > no page use counter data was received"));
1125 mSentPageUseCounters = true;
1126 mPageUseCounters = nullptr;
1129 Element* WindowGlobalParent::GetRootOwnerElement() {
1130 WindowGlobalParent* top = TopWindowContext();
1131 if (!top) {
1132 return nullptr;
1135 if (IsInProcess()) {
1136 return top->BrowsingContext()->GetEmbedderElement();
1139 if (BrowserParent* parent = top->GetBrowserParent()) {
1140 return parent->GetOwnerElement();
1143 return nullptr;
1146 void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) {
1147 if (!aEmbedder) {
1148 aEmbedder = GetRootOwnerElement();
1150 if (aEmbedder) {
1151 if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
1152 obs->NotifyWhenScriptSafe(ToSupports(aEmbedder),
1153 "browser-shutdown-tabstate-updated", nullptr);
1158 mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() {
1159 CanonicalBrowsingContext* bc = BrowsingContext();
1160 if (bc && bc->AncestorsAreCurrent()) {
1161 bc->Top()->RequestRestoreTabContent(this);
1163 return IPC_OK();
1166 nsCString BFCacheStatusToString(uint32_t aFlags) {
1167 if (aFlags == 0) {
1168 return "0"_ns;
1171 nsCString flags;
1172 #define ADD_BFCACHESTATUS_TO_STRING(_flag) \
1173 if (aFlags & BFCacheStatus::_flag) { \
1174 if (!flags.IsEmpty()) { \
1175 flags.Append('|'); \
1177 flags.AppendLiteral(#_flag); \
1178 aFlags &= ~BFCacheStatus::_flag; \
1181 ADD_BFCACHESTATUS_TO_STRING(NOT_ALLOWED);
1182 ADD_BFCACHESTATUS_TO_STRING(EVENT_HANDLING_SUPPRESSED);
1183 ADD_BFCACHESTATUS_TO_STRING(SUSPENDED);
1184 ADD_BFCACHESTATUS_TO_STRING(UNLOAD_LISTENER);
1185 ADD_BFCACHESTATUS_TO_STRING(REQUEST);
1186 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_GET_USER_MEDIA);
1187 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_PEER_CONNECTION);
1188 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_EME_CONTENT);
1189 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_MSE_CONTENT);
1190 ADD_BFCACHESTATUS_TO_STRING(HAS_ACTIVE_SPEECH_SYNTHESIS);
1191 ADD_BFCACHESTATUS_TO_STRING(HAS_USED_VR);
1192 ADD_BFCACHESTATUS_TO_STRING(CONTAINS_REMOTE_SUBFRAMES);
1193 ADD_BFCACHESTATUS_TO_STRING(NOT_ONLY_TOPLEVEL_IN_BCG);
1194 ADD_BFCACHESTATUS_TO_STRING(BEFOREUNLOAD_LISTENER);
1195 ADD_BFCACHESTATUS_TO_STRING(ACTIVE_LOCK);
1197 #undef ADD_BFCACHESTATUS_TO_STRING
1199 MOZ_ASSERT(aFlags == 0,
1200 "Missing stringification for enum value in BFCacheStatus.");
1201 return flags;
1204 mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateBFCacheStatus(
1205 const uint32_t& aOnFlags, const uint32_t& aOffFlags) {
1206 if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) {
1207 nsAutoCString uri("[no uri]");
1208 if (mDocumentURI) {
1209 uri = mDocumentURI->GetSpecOrDefault();
1211 MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
1212 ("Setting BFCache flags for %s +(%s) -(%s)", uri.get(),
1213 BFCacheStatusToString(aOnFlags).get(),
1214 BFCacheStatusToString(aOffFlags).get()));
1216 mBFCacheStatus |= aOnFlags;
1217 mBFCacheStatus &= ~aOffFlags;
1218 return IPC_OK();
1221 mozilla::ipc::IPCResult
1222 WindowGlobalParent::RecvUpdateActivePeerConnectionStatus(bool aIsAdded) {
1223 if (aIsAdded) {
1224 RecvUpdateBFCacheStatus(BFCacheStatus::ACTIVE_PEER_CONNECTION, 0);
1225 } else {
1226 RecvUpdateBFCacheStatus(0, BFCacheStatus::ACTIVE_PEER_CONNECTION);
1229 if (WindowGlobalParent* top = TopWindowContext()) {
1230 CheckedUint32 newValue(top->mNumOfProcessesWithActivePeerConnections);
1231 if (aIsAdded) {
1232 ++newValue;
1233 } else {
1234 --newValue;
1236 if (!newValue.isValid()) {
1237 return IPC_FAIL(this,
1238 "mNumOfProcessesWithActivePeerConnections overflowed");
1241 top->mNumOfProcessesWithActivePeerConnections = newValue.value();
1242 Unused << top->SetHasActivePeerConnections(newValue.value() > 0);
1245 return IPC_OK();
1248 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetSingleChannelId(
1249 const Maybe<uint64_t>& aSingleChannelId) {
1250 mSingleChannelId = aSingleChannelId;
1251 return IPC_OK();
1254 mozilla::ipc::IPCResult WindowGlobalParent::RecvSetDocumentDomain(
1255 nsIURI* aDomain) {
1256 if (mSandboxFlags & SANDBOXED_DOMAIN) {
1257 // We're sandboxed; disallow setting domain
1258 return IPC_FAIL(this, "Sandbox disallows domain setting.");
1261 // Might need to do a featurepolicy check here, like we currently do in the
1262 // child process?
1264 nsCOMPtr<nsIURI> uri;
1265 mDocumentPrincipal->GetDomain(getter_AddRefs(uri));
1266 if (!uri) {
1267 uri = mDocumentPrincipal->GetURI();
1268 if (!uri) {
1269 return IPC_OK();
1273 if (!aDomain || !Document::IsValidDomain(uri, aDomain)) {
1274 // Error: illegal domain
1275 return IPC_FAIL(
1276 this, "Setting domain that's not a suffix of existing domain value.");
1279 if (Group()->IsPotentiallyCrossOriginIsolated()) {
1280 return IPC_FAIL(this, "Setting domain in a cross-origin isolated BC.");
1283 mDocumentPrincipal->SetDomain(aDomain);
1284 return IPC_OK();
1287 mozilla::ipc::IPCResult WindowGlobalParent::RecvReloadWithHttpsOnlyException() {
1288 nsresult rv;
1289 nsCOMPtr<nsIURI> currentUri = BrowsingContext()->Top()->GetCurrentURI();
1291 bool isViewSource = currentUri->SchemeIs("view-source");
1293 nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentUri);
1294 nsCOMPtr<nsIURI> innerURI;
1295 if (isViewSource) {
1296 nestedURI->GetInnerURI(getter_AddRefs(innerURI));
1297 } else {
1298 innerURI = currentUri;
1301 if (!innerURI->SchemeIs("https") && !innerURI->SchemeIs("http")) {
1302 return IPC_FAIL(this, "HTTPS-only mode: Illegal state");
1305 // If the error page is within an iFrame, we create an exception for whatever
1306 // scheme the top-level site is currently on, because the user wants to
1307 // unbreak the iFrame and not the top-level page. When the error page shows up
1308 // on a top-level request, then we replace the scheme with http, because the
1309 // user wants to unbreak the whole page.
1310 nsCOMPtr<nsIURI> newURI;
1311 if (!BrowsingContext()->IsTop()) {
1312 newURI = innerURI;
1313 } else {
1314 Unused << NS_MutateURI(innerURI).SetScheme("http"_ns).Finalize(
1315 getter_AddRefs(newURI));
1318 OriginAttributes originAttributes =
1319 TopWindowContext()->DocumentPrincipal()->OriginAttributesRef();
1321 originAttributes.SetFirstPartyDomain(true, newURI);
1323 nsCOMPtr<nsIPermissionManager> permMgr =
1324 components::PermissionManager::Service();
1325 if (!permMgr) {
1326 return IPC_FAIL(
1327 this, "HTTPS-only mode: Failed to get Permission Manager service");
1330 nsCOMPtr<nsIPrincipal> principal =
1331 BasePrincipal::CreateContentPrincipal(newURI, originAttributes);
1333 rv = permMgr->AddFromPrincipal(
1334 principal, "https-only-load-insecure"_ns,
1335 nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION,
1336 nsIPermissionManager::EXPIRE_SESSION, 0);
1338 if (NS_FAILED(rv)) {
1339 return IPC_FAIL(
1340 this, "HTTPS-only mode: Failed to add permission to the principal");
1343 nsCOMPtr<nsIURI> insecureURI = newURI;
1344 if (isViewSource) {
1345 nsAutoCString spec;
1346 MOZ_ALWAYS_SUCCEEDS(newURI->GetSpec(spec));
1347 if (NS_FAILED(
1348 NS_NewURI(getter_AddRefs(insecureURI), "view-source:"_ns + spec))) {
1349 return IPC_FAIL(
1350 this, "HTTPS-only mode: Failed to re-construct view-source URI");
1354 RefPtr<nsDocShellLoadState> loadState = new nsDocShellLoadState(insecureURI);
1355 loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
1356 loadState->SetLoadType(LOAD_NORMAL_REPLACE);
1358 BrowsingContext()->Top()->LoadURI(loadState, /* setNavigating */ true);
1360 return IPC_OK();
1363 IPCResult WindowGlobalParent::RecvDiscoverIdentityCredentialFromExternalSource(
1364 const IdentityCredentialRequestOptions& aOptions,
1365 const DiscoverIdentityCredentialFromExternalSourceResolver& aResolver) {
1366 IdentityCredential::DiscoverFromExternalSourceInMainProcess(
1367 DocumentPrincipal(), this->BrowsingContext(), aOptions)
1368 ->Then(
1369 GetCurrentSerialEventTarget(), __func__,
1370 [aResolver](const IPCIdentityCredential& aResult) {
1371 return aResolver(Some(aResult));
1373 [aResolver](nsresult aErr) { aResolver(Nothing()); });
1374 return IPC_OK();
1377 void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
1378 if (GetBrowsingContext()->IsTopContent()) {
1379 Telemetry::Accumulate(Telemetry::ORB_DID_EVER_BLOCK_RESPONSE,
1380 mShouldReportHasBlockedOpaqueResponse);
1383 if (mPageUseCountersWindow) {
1384 mPageUseCountersWindow->FinishAccumulatingPageUseCounters();
1385 mPageUseCountersWindow = nullptr;
1388 if (GetBrowsingContext()->IsTopContent() &&
1389 !mDocumentPrincipal->SchemeIs("about")) {
1390 // Record the page load
1391 uint32_t pageLoaded = 1;
1392 Accumulate(Telemetry::MIXED_CONTENT_UNBLOCK_COUNTER, pageLoaded);
1394 // Record the mixed content status of the docshell in Telemetry
1395 enum {
1396 NO_MIXED_CONTENT = 0, // There is no Mixed Content on the page
1397 MIXED_DISPLAY_CONTENT =
1398 1, // The page attempted to load Mixed Display Content
1399 MIXED_ACTIVE_CONTENT =
1400 2, // The page attempted to load Mixed Active Content
1401 MIXED_DISPLAY_AND_ACTIVE_CONTENT = 3 // The page attempted to load Mixed
1402 // Display & Mixed Active Content
1405 bool hasMixedDisplay =
1406 mSecurityState &
1407 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
1408 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
1409 bool hasMixedActive =
1410 mSecurityState &
1411 (nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
1412 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
1414 uint32_t mixedContentLevel = NO_MIXED_CONTENT;
1415 if (hasMixedDisplay && hasMixedActive) {
1416 mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
1417 } else if (hasMixedActive) {
1418 mixedContentLevel = MIXED_ACTIVE_CONTENT;
1419 } else if (hasMixedDisplay) {
1420 mixedContentLevel = MIXED_DISPLAY_CONTENT;
1422 Accumulate(Telemetry::MIXED_CONTENT_PAGE_LOAD, mixedContentLevel);
1424 if (GetDocTreeHadMedia()) {
1425 ScalarAdd(Telemetry::ScalarID::MEDIA_ELEMENT_IN_PAGE_COUNT, 1);
1429 ContentParent* cp = nullptr;
1430 if (!IsInProcess()) {
1431 cp = static_cast<ContentParent*>(Manager()->Manager());
1434 Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
1435 // Keep the WindowContext and our BrowsingContextGroup alive until other
1436 // processes have acknowledged it has been discarded.
1437 Group()->AddKeepAlive();
1438 auto callback = [self = RefPtr{this}](auto) {
1439 self->Group()->RemoveKeepAlive();
1441 otherContent->SendDiscardWindowContext(InnerWindowId(), callback, callback);
1444 // Note that our WindowContext has become discarded.
1445 WindowContext::Discard();
1447 // Report content blocking log when destroyed.
1448 // There shouldn't have any content blocking log when a document is loaded in
1449 // the parent process(See NotifyContentBlockingEvent), so we could skip
1450 // reporting log when it is in-process.
1451 if (!IsInProcess()) {
1452 RefPtr<BrowserParent> browserParent =
1453 static_cast<BrowserParent*>(Manager());
1454 if (browserParent) {
1455 nsCOMPtr<nsILoadContext> loadContext = browserParent->GetLoadContext();
1456 if (loadContext && !loadContext->UsePrivateBrowsing() &&
1457 BrowsingContext()->IsTopContent()) {
1458 GetContentBlockingLog()->ReportLog(DocumentPrincipal());
1460 if (mDocumentURI && (net::SchemeIsHTTP(mDocumentURI) ||
1461 net::SchemeIsHTTPS(mDocumentURI))) {
1462 GetContentBlockingLog()->ReportEmailTrackingLog(DocumentPrincipal());
1468 // Destroy our JSWindowActors, and reject any pending queries.
1469 JSActorDidDestroy();
1471 nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
1472 if (obs) {
1473 obs->NotifyObservers(ToSupports(this), "window-global-destroyed", nullptr);
1476 if (mOriginCounter) {
1477 mOriginCounter->Accumulate();
1481 WindowGlobalParent::~WindowGlobalParent() = default;
1483 JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
1484 JS::Handle<JSObject*> aGivenProto) {
1485 return WindowGlobalParent_Binding::Wrap(aCx, this, aGivenProto);
1488 nsIGlobalObject* WindowGlobalParent::GetParentObject() {
1489 return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
1492 nsIDOMProcessParent* WindowGlobalParent::GetDomProcess() {
1493 if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
1494 return browserParent->Manager();
1496 return InProcessParent::Singleton();
1499 void WindowGlobalParent::DidBecomeCurrentWindowGlobal(bool aCurrent) {
1500 WindowGlobalParent* top = BrowsingContext()->GetTopWindowContext();
1501 if (top && top->mOriginCounter) {
1502 top->mOriginCounter->UpdateSiteOriginsFrom(this,
1503 /* aIncrease = */ aCurrent);
1506 if (!aCurrent && Fullscreen()) {
1507 ExitTopChromeDocumentFullscreen();
1511 bool WindowGlobalParent::ShouldTrackSiteOriginTelemetry() {
1512 CanonicalBrowsingContext* bc = BrowsingContext();
1514 if (!bc->IsTopContent()) {
1515 return false;
1518 RefPtr<BrowserParent> browserParent = GetBrowserParent();
1519 if (!browserParent ||
1520 !IsWebRemoteType(browserParent->Manager()->GetRemoteType())) {
1521 return false;
1524 return DocumentPrincipal()->GetIsContentPrincipal();
1527 void WindowGlobalParent::AddSecurityState(uint32_t aStateFlags) {
1528 MOZ_ASSERT(TopWindowContext() == this);
1529 MOZ_ASSERT((aStateFlags &
1530 (nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
1531 nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
1532 nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT |
1533 nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT |
1534 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED |
1535 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED |
1536 nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED_FIRST)) ==
1537 aStateFlags,
1538 "Invalid flags specified!");
1540 if ((mSecurityState & aStateFlags) == aStateFlags) {
1541 return;
1544 mSecurityState |= aStateFlags;
1546 if (GetBrowsingContext()->GetCurrentWindowGlobal() == this) {
1547 GetBrowsingContext()->UpdateSecurityState();
1551 bool WindowGlobalParent::HasActivePeerConnections() {
1552 MOZ_ASSERT(TopWindowContext() == this,
1553 "mNumOfProcessesWithActivePeerConnections is set only "
1554 "in the top window context");
1555 return mNumOfProcessesWithActivePeerConnections > 0;
1558 void WindowGlobalParent::ExitTopChromeDocumentFullscreen() {
1559 RefPtr<CanonicalBrowsingContext> chromeTop =
1560 BrowsingContext()->TopCrossChromeBoundary();
1561 if (Document* chromeDoc = chromeTop->GetDocument()) {
1562 Document::ClearPendingFullscreenRequests(chromeDoc);
1563 if (chromeDoc->Fullscreen()) {
1564 // This only clears the DOM fullscreen, will not exit from browser UI
1565 // fullscreen mode.
1566 Document::AsyncExitFullscreen(chromeDoc);
1571 void WindowGlobalParent::SetShouldReportHasBlockedOpaqueResponse(
1572 nsContentPolicyType aContentPolicy) {
1573 // It's always okay to block TYPE_BEACON, TYPE_PING and TYPE_CSP_REPORT in
1574 // the parent process because content processes can do nothing to their
1575 // responses. Hence excluding them from the telemetry as blocking
1576 // them have no webcompat concerns.
1577 if (aContentPolicy != nsIContentPolicy::TYPE_BEACON &&
1578 aContentPolicy != nsIContentPolicy::TYPE_PING &&
1579 aContentPolicy != nsIContentPolicy::TYPE_CSP_REPORT) {
1580 if (IsTop()) {
1581 mShouldReportHasBlockedOpaqueResponse = true;
1586 NS_IMPL_CYCLE_COLLECTION_INHERITED(WindowGlobalParent, WindowContext,
1587 mPageUseCountersWindow)
1589 NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalParent,
1590 WindowContext)
1591 NS_IMPL_CYCLE_COLLECTION_TRACE_END
1593 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalParent)
1594 NS_INTERFACE_MAP_END_INHERITING(WindowContext)
1596 NS_IMPL_ADDREF_INHERITED(WindowGlobalParent, WindowContext)
1597 NS_IMPL_RELEASE_INHERITED(WindowGlobalParent, WindowContext)
1599 } // namespace mozilla::dom