/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "mozilla/dom/WindowGlobalParent.h"
#include <algorithm>
#include "MMPrinter.h"
#include "mozilla/AntiTrackingUtils.h"
#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/BounceTrackingProtection.h"
#include "mozilla/ClearOnShutdown.h"
#include "mozilla/Components.h"
#include "mozilla/ContentBlockingAllowList.h"
#include "mozilla/EnumeratedRange.h"
#include "mozilla/IdentityCredentialRequestManager.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ServoCSSParser.h"
#include "mozilla/ServoStyleSet.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_network.h"
#include "mozilla/dom/BrowserBridgeParent.h"
#include "mozilla/dom/BrowserHost.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/BrowsingContextGroup.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ChromeUtils.h"
#include "mozilla/dom/ClientIPCTypes.h"
#include "mozilla/dom/ClientInfo.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/DOMException.h"
#include "mozilla/dom/DOMExceptionBinding.h"
#include "mozilla/dom/DigitalCredential.h"
#include "mozilla/dom/DigitalCredentialParent.h"
#include "mozilla/dom/ElementBinding.h"
#include "mozilla/dom/IdentityCredential.h"
#include "mozilla/dom/InProcessParent.h"
#include "mozilla/dom/JSActorService.h"
#include "mozilla/dom/JSWindowActorBinding.h"
#include "mozilla/dom/JSWindowActorParent.h"
#include "mozilla/dom/MediaController.h"
#include "mozilla/dom/Navigation.h"
#include "mozilla/dom/NavigatorLogin.h"
#include "mozilla/dom/PBackgroundSessionStorageCache.h"
#include "mozilla/dom/ParentProcessChannelHandle.h"
#include "mozilla/dom/SerialManagerParent.h"
#include "mozilla/dom/UseCounterMetrics.h"
#include "mozilla/dom/WebAuthnTransactionParent.h"
#include "mozilla/dom/WebIdentityParent.h"
#include "mozilla/dom/WindowGlobalChild.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/dom/ipc/StructuredCloneData.h"
#include "mozilla/glean/CaptchadetectionMetrics.h"
#include "mozilla/glean/DomMediaMetrics.h"
#include "mozilla/glean/DomSecurityMetrics.h"
#include "mozilla/glean/DomUseCounterMetrics.h"
#include "mozilla/glean/GeckoviewMetrics.h"
#include "mozilla/glean/NetwerkProtocolHttpMetrics.h"
#include "mozilla/ipc/ProtocolUtils.h"
#include "mozilla/net/CookieServiceParent.h"
#include "mozilla/net/NeckoParent.h"
#include "mozilla/net/PCookieServiceParent.h"
#include "nsContentUtils.h"
#include "nsDocShell.h"
#include "nsDocShellLoadState.h"
#include "nsError.h"
#include "nsFrameLoader.h"
#include "nsFrameLoaderOwner.h"
#include "nsIBrowser.h"
#include "nsICertOverrideService.h"
#include "nsICookieManager.h"
#include "nsICookieService.h"
#include "nsIEffectiveTLDService.h"
#include "nsIHttpsOnlyModePermission.h"
#include "nsIOService.h"
#include "nsIPromptCollection.h"
#include "nsISessionStoreFunctions.h"
#include "nsISharePicker.h"
#include "nsISiteIntegrityService.h"
#include "nsITimer.h"
#include "nsIURIMutator.h"
#include "nsIWebProgressListener.h"
#include "nsIX509Cert.h"
#include "nsIXPConnect.h"
#include "nsIXULRuntime.h"
#include "nsImportModule.h"
#include "nsNetUtil.h"
#include "nsQueryObject.h"
#include "nsSandboxFlags.h"
#include "nsScriptSecurityManager.h"
#include "nsSerializationHelper.h"
using namespace mozilla::ipc;
using namespace mozilla::dom::ipc;
extern mozilla::LazyLogModule gSHIPBFCacheLog;
extern mozilla::LazyLogModule gUseCountersLog;
namespace mozilla::dom {
/**
* Accumulated page use counter data for a given top-level content document.
*/
struct PageUseCounters {
// The number of page use counter data messages we are still waiting for.
uint32_t mWaiting =
0;
// Whether we have received any page use counter data.
bool mReceivedAny =
false;
// The accumulated page use counters.
UseCounters mUseCounters;
};
WindowGlobalParent::WindowGlobalParent(
CanonicalBrowsingContext* aBrowsingContext, uint64_t aInnerWindowId,
uint64_t aOuterWindowId, FieldValues&& aInit)
: WindowContext(aBrowsingContext, aInnerWindowId, aOuterWindowId,
std::move(aInit)),
mIsUncommittedInitialDocument(
false),
mSandboxFlags(
0),
mDocumentHasLoaded(
false),
mDocumentHasUserInteracted(
false),
mBlockAllMixedContent(
false),
mUpgradeInsecureRequests(
false) {
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess(),
"Parent process only");
}
already_AddRefed<WindowGlobalParent> WindowGlobalParent::CreateDisconnected(
const WindowGlobalInit& aInit, ContentParent* aForProcess) {
RefPtr<CanonicalBrowsingContext> browsingContext =
CanonicalBrowsingContext::Get(aInit.context().mBrowsingContextId);
if (NS_WARN_IF(!browsingContext)) {
return nullptr;
}
MOZ_RELEASE_ASSERT(!aInit.staticCloneOf().IsDiscarded());
RefPtr<WindowGlobalParent> wgp =
GetByInnerWindowId(aInit.context().mInnerWindowId);
MOZ_RELEASE_ASSERT(!wgp,
"Creating duplicate WindowGlobalParent");
FieldValues fields(aInit.context().mFields);
wgp =
new WindowGlobalParent(browsingContext, aInit.context().mInnerWindowId,
aInit.context().mOuterWindowId, std::move(fields));
wgp->mDocumentPrincipal = aInit.principal();
wgp->mDocumentURI = aInit.documentURI();
wgp->mStaticCloneOf = aInit.staticCloneOf().get_canonical();
wgp->mIsInitialDocument = Some(aInit.isInitialDocument());
wgp->mIsUncommittedInitialDocument = aInit.isUncommittedInitialDocument();
wgp->mBlockAllMixedContent = aInit.blockAllMixedContent();
wgp->mUpgradeInsecureRequests = aInit.upgradeInsecureRequests();
wgp->mSandboxFlags = aInit.sandboxFlags();
wgp->mHttpsOnlyStatus = aInit.httpsOnlyStatus();
net::CookieJarSettings::Deserialize(aInit.cookieJarSettings(),
getter_AddRefs(wgp->mCookieJarSettings));
MOZ_RELEASE_ASSERT(wgp->mDocumentPrincipal,
"Must have a valid principal");
MOZ_RELEASE_ASSERT(
!aForProcess || !wgp->mStaticCloneOf ||
wgp->mStaticCloneOf->GetContentParent() == aForProcess,
"Cannot static clone from a document in a different process!");
nsresult rv = wgp->SetDocumentStoragePrincipal(aInit.storagePrincipal());
MOZ_RELEASE_ASSERT(NS_SUCCEEDED(rv),
"Must succeed in setting storage principal");
if (aInit.documentChannelHandle()) {
auto result = aInit.documentChannelHandle()->GetChannel(
browsingContext, wgp->mStaticCloneOf);
if (result.isOk()) {
wgp->mDocumentChannel = result.unwrap();
}
else {
MOZ_CRASH_UNSAFE_PRINTF(
"Invalid documentChannelHandle: %s",
result.unwrapErr().get());
}
}
if (aInit.failedChannelHandle()) {
auto result = aInit.failedChannelHandle()->GetChannel(browsingContext,
wgp->mStaticCloneOf);
if (result.isOk()) {
wgp->mFailedChannel = result.unwrap();
}
else {
MOZ_CRASH_UNSAFE_PRINTF(
"Invalid failedChannelHandle: %s",
result.unwrapErr().get());
}
}
return wgp.forget();
}
void WindowGlobalParent::Init() {
MOZ_ASSERT(Manager(),
"Should have a manager!");
// Invoke our base class' `Init` method. This will register us in
// `gWindowContexts`.
WindowContext::Init();
// Determine which content process the window global is coming from.
dom::ContentParentId processId(
0);
ContentParent* cp = nullptr;
if (!IsInProcess()) {
cp =
static_cast<ContentParent*>(Manager()->Manager());
processId = cp->ChildID();
// Ensure the content process has permissions for this principal.
cp->TransmitPermissionsForPrincipal(mDocumentPrincipal);
}
MOZ_DIAGNOSTIC_ASSERT(
!BrowsingContext()->GetParent() ||
BrowsingContext()->GetEmbedderInnerWindowId(),
"When creating a non-root WindowGlobalParent, the WindowGlobalParent "
"for our embedder should've already been created.");
// Ensure we have a document URI
if (!mDocumentURI) {
NS_NewURI(getter_AddRefs(mDocumentURI),
"about:blank");
}
// NOTE: `cp` may be nullptr, but that's OK, we need to tell every other
// process in our group in that case.
IPCInitializer ipcinit = GetIPCInitializer();
Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
(
void)otherContent->SendCreateWindowContext(ipcinit);
});
if (!BrowsingContext()->IsDiscarded()) {
MOZ_ALWAYS_SUCCEEDS(
BrowsingContext()->SetCurrentInnerWindowId(InnerWindowId()));
}
if (BrowsingContext()->IsTopContent()) {
// For top level sandboxed documents we need to create a new principal
// from URI + OriginAttributes, since the document principal will be a
// NullPrincipal. See Bug 1654546.
if (mSandboxFlags & SANDBOXED_ORIGIN) {
ContentBlockingAllowList::RecomputePrincipal(
mDocumentURI, mDocumentPrincipal->OriginAttributesRef(),
getter_AddRefs(mDocContentBlockingAllowListPrincipal));
}
else {
ContentBlockingAllowList::ComputePrincipal(
mDocumentPrincipal,
getter_AddRefs(mDocContentBlockingAllowListPrincipal));
}
}
// A fresh document on a reused BC id must not inherit the previous
// document's AudioSession override. BFCache restore reuses the WGP so
// Init() does not run on restore — only new document loads trigger this
// path.
if (
auto* top = BrowsingContext()->Top();
top && top->HasCreatedMediaController()) {
top->GetMediaController()->ClearAudioSessionFor(BrowsingContext()->Id());
}
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(ToSupports(
this),
"window-global-created", nullptr);
}
if (!BrowsingContext()->IsDiscarded() && ShouldTrackSiteOriginTelemetry()) {
mOriginCounter.emplace();
mOriginCounter->UpdateSiteOriginsFrom(
this,
/* aIncrease = */ true);
}
}
void WindowGlobalParent::OriginCounter::UpdateSiteOriginsFrom(
WindowGlobalParent* aParent,
bool aIncrease) {
MOZ_RELEASE_ASSERT(aParent);
if (aParent->DocumentPrincipal()->GetIsContentPrincipal()) {
nsAutoCString origin;
aParent->DocumentPrincipal()->GetSiteOrigin(origin);
if (aIncrease) {
int32_t& count = mOriginMap.LookupOrInsert(origin);
count +=
1;
mMaxOrigins = std::max(mMaxOrigins, mOriginMap.Count());
}
else if (
auto entry = mOriginMap.Lookup(origin)) {
entry.Data() -=
1;
if (entry.Data() ==
0) {
entry.Remove();
}
}
}
}
void WindowGlobalParent::OriginCounter::Accumulate() {
mozilla::glean::geckoview::per_document_site_origins.AccumulateSingleSample(
mMaxOrigins);
mMaxOrigins =
0;
mOriginMap.Clear();
}
/* static */
already_AddRefed<WindowGlobalParent> WindowGlobalParent::GetByInnerWindowId(
uint64_t aInnerWindowId) {
if (!XRE_IsParentProcess()) {
return nullptr;
}
return WindowContext::GetById(aInnerWindowId).downcast<WindowGlobalParent>();
}
already_AddRefed<WindowGlobalChild> WindowGlobalParent::GetChildActor() {
if (!CanSend()) {
return nullptr;
}
IProtocol* otherSide = InProcessParent::ChildActorFor(
this);
return do_AddRef(
static_cast<WindowGlobalChild*>(otherSide));
}
BrowserParent* WindowGlobalParent::GetBrowserParent()
const {
if (IsInProcess() || !CanSend()) {
return nullptr;
}
return static_cast<BrowserParent*>(Manager());
}
ContentParent* WindowGlobalParent::GetContentParent() {
if (IsInProcess() || !CanSend()) {
return nullptr;
}
return static_cast<ContentParent*>(Manager()->Manager());
}
already_AddRefed<nsFrameLoader> WindowGlobalParent::GetRootFrameLoader() {
dom::BrowsingContext* top = BrowsingContext()->Top();
RefPtr<nsFrameLoaderOwner> frameLoaderOwner =
do_QueryObject(top->GetEmbedderElement());
if (frameLoaderOwner) {
return frameLoaderOwner->GetFrameLoader();
}
return nullptr;
}
uint64_t WindowGlobalParent::ContentParentId() {
RefPtr<BrowserParent> browserParent = GetBrowserParent();
return browserParent ? browserParent->Manager()->ChildID() :
0;
}
int32_t WindowGlobalParent::OsPid() {
RefPtr<BrowserParent> browserParent = GetBrowserParent();
return browserParent ? browserParent->Manager()->Pid() : -
1;
}
// A WindowGlobalPaernt is the root in its process if it has no parent, or its
// embedder is in a different process.
bool WindowGlobalParent::IsProcessRoot() {
if (!BrowsingContext()->GetParent()) {
return true;
}
RefPtr<WindowGlobalParent> embedder =
BrowsingContext()->GetEmbedderWindowGlobal();
if (NS_WARN_IF(!embedder)) {
return false;
}
return ContentParentId() != embedder->ContentParentId();
}
uint32_t WindowGlobalParent::ContentBlockingEvents() {
return GetContentBlockingLog()->GetContentBlockingEventsInLog();
}
void WindowGlobalParent::GetContentBlockingLog(nsAString& aLog) {
NS_ConvertUTF8toUTF16 log(GetContentBlockingLog()->Stringify());
aLog.Assign(std::move(log));
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvLoadURI(
const MaybeDiscarded<dom::BrowsingContext>& aTargetBC,
nsDocShellLoadState* aLoadState,
bool aSetNavigating) {
if (aTargetBC.IsNullOrDiscarded()) {
MOZ_LOG(
BrowsingContext::GetLog(), LogLevel::Debug,
(
"ParentIPC: Trying to send a message with dead or detached context"));
return IPC_OK();
}
if (aLoadState->URI()->SchemeIs(
"javascript")) {
return IPC_FAIL(
this,
"Illegal cross-process javascript: load attempt");
}
RefPtr<CanonicalBrowsingContext> targetBC = aTargetBC.get_canonical();
if (targetBC->Group() != BrowsingContext()->Group()) {
return IPC_FAIL(
this,
"Illegal cross-group BrowsingContext load");
}
if (!nsContentUtils::CanNavigate(BrowsingContext(), targetBC.get(),
DocumentPrincipal(),
true)) {
return IPC_FAIL(
this,
"Illegal cross-process load attempt (!CanNavigate())");
}
// FIXME: We should really initiate the load in the parent before bouncing
// back down to the child.
targetBC->LoadURI(aLoadState, aSetNavigating);
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvInternalLoad(
nsDocShellLoadState* aLoadState) {
if (!aLoadState->Target().IsEmpty() ||
aLoadState->TargetBrowsingContext().IsNull()) {
return IPC_FAIL(
this,
"must already be retargeted");
}
if (aLoadState->TargetBrowsingContext().IsDiscarded()) {
MOZ_LOG(
BrowsingContext::GetLog(), LogLevel::Debug,
(
"ParentIPC: Trying to send a message with dead or detached context"));
return IPC_OK();
}
if (aLoadState->URI()->SchemeIs(
"javascript")) {
return IPC_FAIL(
this,
"Illegal cross-process javascript: load attempt");
}
RefPtr<CanonicalBrowsingContext> targetBC =
aLoadState->TargetBrowsingContext().get_canonical();
if (targetBC->Group() != BrowsingContext()->Group()) {
return IPC_FAIL(
this,
"Illegal cross-group BrowsingContext load");
}
if (!nsContentUtils::CanNavigate(BrowsingContext(), targetBC.get(),
DocumentPrincipal(),
true)) {
return IPC_FAIL(
this,
"Illegal cross-process load attempt (!CanNavigate())");
}
// FIXME: We should really initiate the load in the parent before bouncing
// back down to the child.
targetBC->InternalLoad(aLoadState);
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvUpdateDocumentURI(NotNull<nsIURI*> aURI) {
// XXX(nika): Assert that the URI change was one which makes sense (either
// about:blank -> a real URI, or a legal push/popstate URI change):
nsAutoCString scheme;
if (NS_FAILED(aURI->GetScheme(scheme))) {
return IPC_FAIL(
this,
"Setting DocumentURI without scheme.");
}
nsCOMPtr<nsIIOService> ios = do_GetIOService();
if (!ios) {
return IPC_FAIL(
this,
"Cannot get IOService");
}
nsCOMPtr<nsIProtocolHandler> handler;
ios->GetProtocolHandler(scheme.get(), getter_AddRefs(handler));
if (!handler) {
return IPC_FAIL(
this,
"Setting DocumentURI with unknown protocol.");
}
nsCOMPtr<nsIURI> principalURI = mDocumentPrincipal->GetURI();
if (mDocumentPrincipal->GetIsNullPrincipal()) {
if (nsCOMPtr<nsIPrincipal> precursor =
mDocumentPrincipal->GetPrecursorPrincipal()) {
principalURI = precursor->GetURI();
}
}
if (nsScriptSecurityManager::IsHttpOrHttpsAndCrossOrigin(principalURI,
aURI)) {
return IPC_FAIL(
this,
"Setting DocumentURI with a different Origin than "
"principal URI");
}
mDocumentURI = std::move(aURI);
return IPC_OK();
}
nsresult WindowGlobalParent::SetDocumentStoragePrincipal(
nsIPrincipal* aNewDocumentStoragePrincipal) {
if (mDocumentPrincipal->Equals(aNewDocumentStoragePrincipal)) {
mDocumentStoragePrincipal = mDocumentPrincipal;
return NS_OK;
}
// Compare originNoSuffix to ensure it's equal.
nsCString noSuffix;
nsresult rv = mDocumentPrincipal->GetOriginNoSuffix(noSuffix);
if (NS_FAILED(rv)) {
return rv;
}
nsCString storageNoSuffix;
rv = aNewDocumentStoragePrincipal->GetOriginNoSuffix(storageNoSuffix);
if (NS_FAILED(rv)) {
return rv;
}
if (noSuffix != storageNoSuffix) {
return NS_ERROR_FAILURE;
}
if (!mDocumentPrincipal->OriginAttributesRef().EqualsIgnoringPartitionKey(
aNewDocumentStoragePrincipal->OriginAttributesRef())) {
return NS_ERROR_FAILURE;
}
mDocumentStoragePrincipal = aNewDocumentStoragePrincipal;
return NS_OK;
}
IPCResult WindowGlobalParent::RecvUpdateDocumentPrincipal(
nsIPrincipal* aNewDocumentPrincipal,
nsIPrincipal* aNewDocumentStoragePrincipal) {
if (!mDocumentPrincipal->Equals(aNewDocumentPrincipal)) {
return IPC_FAIL(
this,
"Trying to reuse WindowGlobalParent but the principal of "
"the new document does not match the old one");
}
mDocumentPrincipal = aNewDocumentPrincipal;
if (NS_FAILED(SetDocumentStoragePrincipal(aNewDocumentStoragePrincipal))) {
return IPC_FAIL(
this,
"Trying to reuse WindowGlobalParent but the principal of "
"the new document does not match the storage principal");
}
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateDocumentTitle(
const nsString& aTitle) {
if (mDocumentTitle.isSome() && mDocumentTitle.value() == aTitle) {
return IPC_OK();
}
mDocumentTitle = Some(aTitle);
// Send a pagetitlechanged event only for changes to the title
// for top-level frames.
if (!BrowsingContext()->IsTop()) {
return IPC_OK();
}
// Notify media controller in order to update its default metadata.
if (BrowsingContext()->HasCreatedMediaController()) {
BrowsingContext()->GetMediaController()->NotifyPageTitleChanged();
}
Element* frameElement = BrowsingContext()->GetEmbedderElement();
if (!frameElement) {
return IPC_OK();
}
AsyncEventDispatcher::RunDOMEventWhenSafe(
*frameElement, u
"pagetitlechanged"_ns, CanBubble::eYes,
ChromeOnlyDispatch::eYes);
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateHttpsOnlyStatus(
uint32_t aHttpsOnlyStatus) {
mHttpsOnlyStatus = aHttpsOnlyStatus;
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvUpdateDocumentHasLoaded(
bool aDocumentHasLoaded) {
mDocumentHasLoaded = aDocumentHasLoaded;
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvUpdateDocumentHasUserInteracted(
bool aDocumentHasUserInteracted) {
mDocumentHasUserInteracted = aDocumentHasUserInteracted;
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvUpdateSandboxFlags(uint32_t aSandboxFlags) {
mSandboxFlags = aSandboxFlags;
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvUpdateDocumentCspSettings(
bool aBlockAllMixedContent,
bool aUpgradeInsecureRequests) {
mBlockAllMixedContent = aBlockAllMixedContent;
mUpgradeInsecureRequests = aUpgradeInsecureRequests;
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvSetClientInfo(
const IPCClientInfo& aIPCClientInfo) {
mClientInfo = Some(ClientInfo(aIPCClientInfo));
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvDestroy() {
// Make a copy so that we can avoid potential iterator invalidation when
// calling the user-provided Destroy() methods.
JSActorWillDestroy();
if (CanSend()) {
RefPtr<BrowserParent> browserParent = GetBrowserParent();
if (!browserParent || !browserParent->IsDestroyed()) {
(
void)Send__delete__(
this);
}
}
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvRawMessage(
const JSActorMessageMeta& aMeta,
JSIPCValue&& aData,
StructuredCloneData* aStack) {
ReceiveRawMessage(aMeta, std::move(aData), aStack);
return IPC_OK();
}
const nsACString& WindowGlobalParent::GetRemoteType()
const {
if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
return browserParent->Manager()->GetRemoteType();
}
return NOT_REMOTE_TYPE;
}
void WindowGlobalParent::GetRemoteType(nsACString& aRemoteType)
const {
aRemoteType = GetRemoteType();
}
void WindowGlobalParent::NotifyContentBlockingEvent(
uint32_t aEvent, nsIRequest* aRequest,
bool aBlocked,
const nsACString& aTrackingOrigin,
const nsTArray<nsCString>& aTrackingFullHashes,
const Maybe<ContentBlockingNotifier::StorageAccessPermissionGrantedReason>&
aReason,
const Maybe<CanvasFingerprintingEvent>& aCanvasFingerprintingEvent) {
MOZ_ASSERT(NS_IsMainThread());
DebugOnly<
bool> isCookiesBlocked =
aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_TRACKER ||
aEvent == nsIWebProgressListener::STATE_COOKIES_BLOCKED_SOCIALTRACKER;
MOZ_ASSERT_IF(aBlocked, aReason.isNothing());
MOZ_ASSERT_IF(!isCookiesBlocked, aReason.isNothing());
MOZ_ASSERT_IF(isCookiesBlocked && !aBlocked, aReason.isSome());
MOZ_DIAGNOSTIC_ASSERT(XRE_IsParentProcess());
// TODO: temporarily remove this until we find the root case of Bug 1609144
// MOZ_DIAGNOSTIC_ASSERT_IF(XRE_IsE10sParentProcess(), !IsInProcess());
// Return early if this WindowGlobalParent is in process.
if (IsInProcess()) {
return;
}
Maybe<uint32_t> event = GetContentBlockingLog()->RecordLogParent(
aTrackingOrigin, aEvent, aBlocked, aReason, aTrackingFullHashes,
aCanvasFingerprintingEvent);
// Notify the OnContentBlockingEvent if necessary.
if (event) {
if (
auto* webProgress = GetBrowsingContext()->GetWebProgress()) {
webProgress->OnContentBlockingEvent(webProgress, aRequest, event.value());
}
}
}
already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetActor(
JSContext* aCx,
const nsACString& aName, ErrorResult& aRv) {
return JSActorManager::GetActor(aCx, aName, aRv)
.downcast<JSWindowActorParent>();
}
already_AddRefed<JSWindowActorParent> WindowGlobalParent::GetExistingActor(
const nsACString& aName) {
return JSActorManager::GetExistingActor(aName)
.downcast<JSWindowActorParent>();
}
already_AddRefed<JSActor> WindowGlobalParent::InitJSActor(
JS::Handle<JSObject*> aMaybeActor,
const nsACString& aName,
ErrorResult& aRv) {
RefPtr<JSWindowActorParent> actor;
if (aMaybeActor.get()) {
aRv = UNWRAP_OBJECT(JSWindowActorParent, aMaybeActor.get(), actor);
if (aRv.Failed()) {
return nullptr;
}
}
else {
actor =
new JSWindowActorParent();
}
MOZ_RELEASE_ASSERT(!actor->GetManager(),
"mManager was already initialized once!");
actor->Init(aName,
this);
return actor.forget();
}
bool WindowGlobalParent::IsCurrentGlobal() {
if (BrowsingContext() && BrowsingContext()->IsInBFCache()) {
return false;
}
return CanSend() && BrowsingContext()->GetCurrentWindowGlobal() ==
this;
}
bool WindowGlobalParent::IsActiveInTab() {
if (!CanSend()) {
return false;
}
CanonicalBrowsingContext* bc = BrowsingContext();
if (!bc || bc->GetCurrentWindowGlobal() !=
this) {
return false;
}
// We check the top BC so we don't need to worry about getting a stale value.
// That may not be necessary.
MOZ_ASSERT(bc->Top()->IsInBFCache() == bc->IsInBFCache(),
"BFCache bit out of sync?");
return bc->AncestorsAreCurrent() && !bc->Top()->IsInBFCache();
}
namespace {
class ShareHandler final :
public PromiseNativeHandler {
public:
explicit ShareHandler(
mozilla::dom::WindowGlobalParent::ShareResolver&& aResolver)
: mResolver(std::move(aResolver)) {}
NS_DECL_ISUPPORTS
public:
virtual void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) override {
mResolver(NS_OK);
}
virtual void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) override {
if (NS_WARN_IF(!aValue.isObject())) {
mResolver(NS_ERROR_FAILURE);
return;
}
// nsresult is stored as Exception internally in Promise
JS::Rooted<JSObject*> obj(aCx, &aValue.toObject());
RefPtr<DOMException> unwrapped;
nsresult rv = UNWRAP_OBJECT(DOMException, &obj, unwrapped);
if (NS_WARN_IF(NS_FAILED(rv))) {
mResolver(NS_ERROR_FAILURE);
return;
}
mResolver(unwrapped->GetResult());
}
private:
~ShareHandler() =
default;
mozilla::dom::WindowGlobalParent::ShareResolver mResolver;
};
NS_IMPL_ISUPPORTS0(ShareHandler)
}
// namespace
mozilla::ipc::IPCResult WindowGlobalParent::RecvGetContentBlockingEvents(
WindowGlobalParent::GetContentBlockingEventsResolver&& aResolver) {
uint32_t events = GetContentBlockingLog()->GetContentBlockingEventsInLog();
aResolver(events);
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateCookieJarSettings(
const CookieJarSettingsArgs& aCookieJarSettingsArgs) {
net::CookieJarSettings::Deserialize(aCookieJarSettingsArgs,
getter_AddRefs(mCookieJarSettings));
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateChannels(
ParentProcessChannelHandle* aDocumentHandle,
ParentProcessChannelHandle* aFailedHandle) {
nsCOMPtr<nsIChannel> documentChannel;
if (aDocumentHandle) {
auto result = aDocumentHandle->GetChannel(BrowsingContext());
if (result.isOk()) {
documentChannel = result.unwrap();
}
else {
MOZ_CRASH_UNSAFE_PRINTF(
"Invalid aDocumentHandle: %s",
result.unwrapErr().get());
}
}
nsCOMPtr<nsIChannel> failedChannel;
if (aFailedHandle) {
auto result = aFailedHandle->GetChannel(BrowsingContext());
if (result.isOk()) {
failedChannel = result.unwrap();
}
else {
MOZ_CRASH_UNSAFE_PRINTF(
"Invalid aFailedHandle: %s",
result.unwrapErr().get());
}
}
// NOTE: In the case where XSLT has performed a transformation on our
// document, it is possible for this method to be called multiple times. In
// that case, the channel should be identical to the channel which was
// previously loaded. Once XSLT is removed, we should be able to remove that
// part of this check.
if ((mDocumentChannel || mFailedChannel) &&
(mDocumentChannel != documentChannel ||
mFailedChannel != failedChannel)) {
return IPC_FAIL(
this,
"Conflicting attempts to set ParentProcessChannelHandle on "
"WindowGlobalParent");
}
mDocumentChannel = documentChannel;
mFailedChannel = failedChannel;
return IPC_OK();
}
already_AddRefed<nsIChannel> WindowGlobalParent::GetDocumentChannel() {
if (mDocumentChannel) {
return do_AddRef(mDocumentChannel);
}
if (Document* doc = GetExtantDoc()) {
return do_AddRef(doc->GetChannel());
}
return nullptr;
}
already_AddRefed<nsIChannel> WindowGlobalParent::GetFailedChannel() {
if (mFailedChannel) {
return do_AddRef(mFailedChannel);
}
if (Document* doc = GetExtantDoc()) {
return do_AddRef(doc->GetFailedChannel());
}
return nullptr;
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvShare(
IPCWebShareData&& aData, WindowGlobalParent::ShareResolver&& aResolver) {
// Widget Layer handoff...
nsCOMPtr<nsISharePicker> sharePicker =
do_GetService(
"@mozilla.org/sharepicker;1");
if (!sharePicker) {
aResolver(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return IPC_OK();
}
// Initialize the ShareWidget
RefPtr<BrowserParent> parent = GetBrowserParent();
nsCOMPtr<mozIDOMWindowProxy> openerWindow;
if (parent) {
openerWindow = parent->GetParentWindowOuter();
if (!openerWindow) {
aResolver(NS_ERROR_FAILURE);
return IPC_OK();
}
}
sharePicker->Init(openerWindow);
// And finally share the data...
RefPtr<Promise> promise;
nsresult rv = sharePicker->Share(aData.title(), aData.text(), aData.url(),
getter_AddRefs(promise));
if (NS_FAILED(rv)) {
aResolver(rv);
return IPC_OK();
}
// Handler finally awaits response...
RefPtr<ShareHandler> handler =
new ShareHandler(std::move(aResolver));
promise->AppendNativeHandler(handler);
return IPC_OK();
}
namespace {
class CheckPermitUnloadRequest final :
public PromiseNativeHandler,
public nsITimerCallback {
public:
CheckPermitUnloadRequest(
WindowGlobalParent* aWGP,
bool aHasInProcessBlocker,
nsIDocumentViewer::PermitUnloadAction aAction,
std::function<
void(nsIDocumentViewer::PermitUnloadResult)>&& aResolver)
: mResolver(std::move(aResolver)),
mWGP(aWGP),
mAction(aAction),
mFoundBlocker(aHasInProcessBlocker) {}
// Special case for when we also want to dispatch a "navigate" event to the
// top level window's navigation object. The target session history entry that
// we dispatch is what we use when we #fire-a-traverse-navigate-event. This
// will _only_ run `DispatchBeforeUnloadToSubtree` for the content process of
// the top level window. See further comments below in `Run` and in
// `DispatchBeforeUnloadToSubtree`.
void RunTraversable(nsDocShellLoadState* aDocShellLoadState) {
MOZ_DIAGNOSTIC_ASSERT(mWGP->BrowsingContext()->IsTop());
Run(nullptr,
0, aDocShellLoadState);
}
// The complementing special case for `RunTraversable`, which is short hand
// for running `DispatchBeforeUnloadToSubtree` for all content processes
// except for the content process of the top level window. See further
// comments below in `Run` and in `DispatchBeforeUnloadToSubtree`.
void RunChildNavigables() {
MOZ_DIAGNOSTIC_ASSERT(mWGP->BrowsingContext()->IsTop());
Run(mWGP->BrowsingContext()->GetContentParent(),
0);
}
void Run(ContentParent* aIgnoreProcess = nullptr, uint32_t aTimeout =
0,
nsDocShellLoadState* aDocShellLoadState = nullptr) {
MOZ_ASSERT(mState == State::UNINITIALIZED);
mState = State::WAITING;
RefPtr<CheckPermitUnloadRequest> self(
this);
AutoTArray<ContentParent*,
8> seen;
if (aIgnoreProcess) {
seen.AppendElement(aIgnoreProcess);
}
BrowsingContext* bc = mWGP->GetBrowsingContext();
auto resolve = [self](nsIDocumentViewer::PermitUnloadResult aResult) {
self->mFoundBlocker =
aResult == nsIDocumentViewer::eCanceledByBeforeUnload;
self->mReason = aResult;
self->ResolveRequest();
};
auto reject = [self](
auto) { self->ResolveRequest(); };
// If `aInfo` is passed, only dispatch to the content process of the top
// level window.
if (aDocShellLoadState) {
MOZ_DIAGNOSTIC_ASSERT(Navigation::IsAPIEnabled());
ContentParent* cp = mWGP->GetContentParent();
mPendingRequests++;
// Here eDontPromptAndUnload means that we ignore beforeunload handlers,
// but we still need to handle the traversable navigate handler.
mozilla::NotNull<RefPtr<nsDocShellLoadState>> loadState =
WrapNotNull(aDocShellLoadState);
if (mAction ==
nsIDocumentViewer::PermitUnloadAction::eDontPromptAndUnload) {
cp->SendDispatchNavigateToTraversable(bc, loadState, resolve, reject);
}
else {
cp->SendDispatchBeforeUnloadToSubtree(bc, Some(loadState), resolve,
reject);
}
}
else {
bc->PreOrderWalk([&](dom::BrowsingContext* aBC) {
if (WindowGlobalParent* wgp =
aBC->Canonical()->GetCurrentWindowGlobal()) {
ContentParent* cp = wgp->GetContentParent();
// `seen` contains processes that we've already dispatched
// "beforeunload" to.
if (wgp->NeedsBeforeUnload() && !seen.ContainsSorted(cp)) {
seen.InsertElementSorted(cp);
mPendingRequests++;
if (cp) {
cp->SendDispatchBeforeUnloadToSubtree(bc, Nothing(), resolve,
reject);
}
else {
NS_DispatchToMainThread(NS_NewRunnableFunction(
"DispatchBeforeUnloadToSubtree",
[bc = RefPtr{bc}, resolve]() {
ContentChild::DispatchBeforeUnloadToSubtree(bc, Nothing(),
resolve);
}));
}
}
}
});
}
if (mPendingRequests && aTimeout) {
(
void)NS_NewTimerWithCallback(getter_AddRefs(mTimer),
this, aTimeout,
nsITimer::TYPE_ONE_SHOT);
}
CheckDoneWaiting();
}
void ResolveRequest() {
mPendingRequests--;
CheckDoneWaiting();
}
NS_IMETHODIMP Notify(nsITimer* aTimer) override {
MOZ_ASSERT(aTimer == mTimer);
if (mState == State::WAITING) {
mState = State::TIMED_OUT;
CheckDoneWaiting();
}
return NS_OK;
}
void CheckDoneWaiting() {
// If we've found a blocker, we prompt immediately without waiting for
// further responses. The user's response applies to the entire navigation
// attempt, regardless of how many "beforeunload" listeners we call.
if (mState != State::TIMED_OUT &&
(mState != State::WAITING || (mPendingRequests && !mFoundBlocker))) {
return;
}
mState = State::PROMPTING;
// Clearing our reference to the timer will automatically cancel it if it's
// still running.
mTimer = nullptr;
if (!mFoundBlocker) {
SendReply();
return;
}
auto action = mAction;
if (StaticPrefs::dom_disable_beforeunload()) {
action = nsIDocumentViewer::eDontPromptAndUnload;
}
if (action != nsIDocumentViewer::ePrompt) {
if (action == nsIDocumentViewer::eDontPromptAndUnload) {
mReason = nsIDocumentViewer::eContinue;
}
else {
mReason = nsIDocumentViewer::eCanceledByBeforeUnload;
}
SendReply();
return;
}
// Handle any failure in prompting by aborting the navigation. See comment
// in nsDocumentViewer::PermitUnload for reasoning.
auto cleanup = MakeScopeExit([&]() {
mReason = nsIDocumentViewer::eCanceledByBeforeUnload;
SendReply();
});
if (nsCOMPtr<nsIPromptCollection> prompt =
do_GetService(
"@mozilla.org/embedcomp/prompt-collection;1")) {
RefPtr<Promise> promise;
prompt->AsyncBeforeUnloadCheck(mWGP->GetBrowsingContext(),
getter_AddRefs(promise));
if (!promise) {
return;
}
promise->AppendNativeHandler(
this);
cleanup.release();
}
}
void SendReply() {
MOZ_ASSERT(mState != State::REPLIED);
mResolver(mReason);
mState = State::REPLIED;
}
void ResolvedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) override {
MOZ_ASSERT(mState == State::PROMPTING);
if (!JS::ToBoolean(aValue)) {
mReason = nsIDocumentViewer::eCanceledByBeforeUnload;
}
else {
mReason = nsIDocumentViewer::eContinue;
}
SendReply();
}
void RejectedCallback(JSContext* aCx, JS::Handle<JS::Value> aValue,
ErrorResult& aRv) override {
MOZ_ASSERT(mState == State::PROMPTING);
mReason = nsIDocumentViewer::eCanceledByBeforeUnload;
SendReply();
}
NS_DECL_ISUPPORTS
private:
~CheckPermitUnloadRequest() {
// We may get here without having sent a reply if the promise we're waiting
// on is destroyed without being resolved or rejected.
if (mState != State::REPLIED) {
mReason = nsIDocumentViewer::eCanceledByBeforeUnload;
SendReply();
}
}
enum class State : uint8_t {
UNINITIALIZED,
WAITING,
TIMED_OUT,
PROMPTING,
REPLIED,
};
std::function<
void(nsIDocumentViewer::PermitUnloadResult)> mResolver;
RefPtr<WindowGlobalParent> mWGP;
nsCOMPtr<nsITimer> mTimer;
uint32_t mPendingRequests =
0;
nsIDocumentViewer::PermitUnloadAction mAction;
State mState = State::UNINITIALIZED;
bool mFoundBlocker =
false;
nsIDocumentViewer::PermitUnloadResult mReason = nsIDocumentViewer::eContinue;
};
NS_IMPL_ISUPPORTS(CheckPermitUnloadRequest, nsITimerCallback)
}
// namespace
mozilla::ipc::IPCResult WindowGlobalParent::RecvCheckPermitUnload(
bool aHasInProcessBlocker, XPCOMPermitUnloadAction aAction,
CheckPermitUnloadResolver&& aResolver) {
if (!IsCurrentGlobal()) {
aResolver(
false);
return IPC_OK();
}
auto request = MakeRefPtr<CheckPermitUnloadRequest>(
this, aHasInProcessBlocker, aAction,
[resolver = std::move(aResolver)](
nsIDocumentViewer::PermitUnloadResult aResult) {
resolver(aResult == nsIDocumentViewer::eContinue);
});
request->Run(
/* aIgnoreProcess */ GetContentParent());
return IPC_OK();
}
already_AddRefed<Promise> WindowGlobalParent::PermitUnload(
PermitUnloadAction aAction, uint32_t aTimeout, mozilla::ErrorResult& aRv) {
nsIGlobalObject* global = GetParentObject();
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
auto request = MakeRefPtr<CheckPermitUnloadRequest>(
this,
/* aHasInProcessBlocker */ false,
nsIDocumentViewer::PermitUnloadAction(aAction),
[promise](nsIDocumentViewer::PermitUnloadResult aResult) {
promise->MaybeResolve(aResult == nsIDocumentViewer::eContinue);
});
request->Run(
/* aIgnoreProcess */ nullptr, aTimeout);
return promise.forget();
}
void WindowGlobalParent::PermitUnload(
std::function<
void(nsIDocumentViewer::PermitUnloadResult)>&& aResolver) {
RefPtr<CheckPermitUnloadRequest> request =
MakeRefPtr<CheckPermitUnloadRequest>(
this,
/* aHasInProcessBlocker */ false,
nsIDocumentViewer::PermitUnloadAction::ePrompt, std::move(aResolver));
request->Run();
}
// https://html.spec.whatwg.org/#checking-if-unloading-is-canceled
// Implements the traversable-specific portion (step 4): fires beforeunload on
// the traversable's active document (if needed) and fires the traverse
// `navigate` event, which may intercept the load via `aDocShellLoadState`.
void WindowGlobalParent::CheckIfUnloadingIsCanceledForTraversable(
nsDocShellLoadState* aDocShellLoadState,
nsIDocumentViewer::PermitUnloadAction aAction,
std::function<
void(nsIDocumentViewer::PermitUnloadResult)>&& aResolver) {
MOZ_DIAGNOSTIC_ASSERT(BrowsingContext()->IsTop());
RefPtr<CheckPermitUnloadRequest> request =
MakeRefPtr<CheckPermitUnloadRequest>(
this,
/* aHasInProcessBlocker */ false,
aAction, std::move(aResolver));
request->RunTraversable(aDocShellLoadState);
}
void WindowGlobalParent::PermitUnloadChildNavigables(
nsIDocumentViewer::PermitUnloadAction aAction,
std::function<
void(nsIDocumentViewer::PermitUnloadResult)>&& aResolver) {
RefPtr<CheckPermitUnloadRequest> request =
MakeRefPtr<CheckPermitUnloadRequest>(
this,
/* aHasInProcessBlocker */ false,
aAction, std::move(aResolver));
request->RunChildNavigables();
}
already_AddRefed<mozilla::dom::Promise> WindowGlobalParent::DrawSnapshot(
const DOMRect* aRect,
double aScale,
const nsACString& aBackgroundColor,
bool aResetScrollPosition, mozilla::ErrorResult& aRv) {
nsIGlobalObject* global = GetParentObject();
RefPtr<Promise> promise = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
nscolor color;
if (NS_WARN_IF(!ServoCSSParser::ComputeColor(nullptr, NS_RGB(
0,
0,
0),
aBackgroundColor, &color,
nullptr, nullptr))) {
aRv = NS_ERROR_FAILURE;
return nullptr;
}
gfx::CrossProcessPaintFlags flags =
gfx::CrossProcessPaintFlags::UseHighQualityScaling;
if (!aRect) {
// If no explicit Rect was passed, we want the currently visible viewport.
flags |= gfx::CrossProcessPaintFlags::DrawView;
}
else if (aResetScrollPosition) {
flags |= gfx::CrossProcessPaintFlags::ResetScrollPosition;
}
if (!gfx::CrossProcessPaint::Start(
this, aRect, (
float)aScale, color, flags,
promise)) {
aRv = NS_ERROR_FAILURE;
return nullptr;
}
return promise.forget();
}
void WindowGlobalParent::DrawSnapshotInternal(
gfx::CrossProcessPaint* aPaint,
const Maybe<IntRect>& aRect,
float aScale,
nscolor aBackgroundColor, gfx::CrossProcessPaintFlags aFlags) {
auto promise = SendDrawSnapshot(aRect, aScale, aBackgroundColor, aFlags);
RefPtr<gfx::CrossProcessPaint> paint(aPaint);
RefPtr<WindowGlobalParent> wgp(
this);
promise->Then(
GetMainThreadSerialEventTarget(), __func__,
[paint, wgp](PaintFragment&& aFragment) {
paint->ReceiveFragment(wgp, std::move(aFragment));
},
[paint, wgp](ResponseRejectReason&& aReason) {
paint->LostFragment(wgp);
});
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvExpectPageUseCounters(
const MaybeDiscarded<WindowContext>& aTop) {
if (aTop.IsNull()) {
return IPC_FAIL(
this,
"aTop must not be null");
}
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
"Expect page use counters: WindowContext %" PRIu64
" -> %" PRIu64,
InnerWindowId(), aTop.ContextId()));
// We've been called to indicate that the document in our window intends
// to send use counter data to accumulate towards the top-level document's
// page use counters. This causes us to wait for this window to go away
// (in WindowGlobalParent::ActorDestroy) before reporting the page use
// counters via Telemetry.
RefPtr<WindowGlobalParent> page =
static_cast<WindowGlobalParent*>(aTop.GetMaybeDiscarded());
if (!page || page->mSentPageUseCounters) {
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
" > too late, won't report page use counters for this straggler"));
return IPC_OK();
}
if (mPageUseCountersWindow) {
if (mPageUseCountersWindow != page) {
return IPC_FAIL(
this,
"ExpectPageUseCounters called on the same "
"WindowContext with a different aTop value");
}
// We can get called with the same aTop value more than once, e.g. for
// initial about:blank documents and then subsequent "real" documents loaded
// into the same window. We must note each source window only once.
return IPC_OK();
}
// Note that the top-level document must wait for one more window's use
// counters before reporting via Telemetry.
mPageUseCountersWindow = page;
if (!page->mPageUseCounters) {
page->mPageUseCounters = MakeUnique<PageUseCounters>();
}
++page->mPageUseCounters->mWaiting;
MOZ_LOG(
gUseCountersLog, LogLevel::Debug,
(
" > top-level now waiting on %d\n", page->mPageUseCounters->mWaiting));
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvAccumulatePageUseCounters(
const UseCounters& aUseCounters) {
// We've been called to accumulate use counter data into the page use counters
// for the document in mPageUseCountersWindow.
MOZ_LOG(
gUseCountersLog, LogLevel::Debug,
(
"Accumulate page use counters: WindowContext %" PRIu64
" -> %" PRIu64,
InnerWindowId(),
mPageUseCountersWindow ? mPageUseCountersWindow->InnerWindowId() :
0));
if (!mPageUseCountersWindow || mPageUseCountersWindow->mSentPageUseCounters) {
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
" > too late, won't report page use counters for this straggler"));
return IPC_OK();
}
MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters);
MOZ_ASSERT(mPageUseCountersWindow->mPageUseCounters->mWaiting >
0);
mPageUseCountersWindow->mPageUseCounters->mUseCounters |= aUseCounters;
mPageUseCountersWindow->mPageUseCounters->mReceivedAny =
true;
return IPC_OK();
}
// This is called on the top-level WindowGlobal, i.e. the one that is
// accumulating the page use counters, not the (potentially descendant) window
// that has finished providing use counter data.
WindowGlobalParent::PageUseCounterResult
WindowGlobalParent::FinishAccumulatingPageUseCounters() {
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
"Stop expecting page use counters: -> WindowContext %" PRIu64,
InnerWindowId()));
if (!mPageUseCounters) {
MOZ_ASSERT_UNREACHABLE(
"Not expecting page use counter data");
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
" > not expecting page use counter data"));
return WindowGlobalParent::PageUseCounterResult();
}
MOZ_ASSERT(mPageUseCounters->mWaiting >
0);
--mPageUseCounters->mWaiting;
if (mPageUseCounters->mWaiting >
0) {
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
" > now waiting on %d", mPageUseCounters->mWaiting));
return PageUseCounterResultBits::WAITING;
}
PageUseCounterResult result;
if (mPageUseCounters->mReceivedAny) {
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
" > reporting [%s]",
nsContentUtils::TruncatedURLForDisplay(mDocumentURI).get()));
result += PageUseCounterResultBits::DATA_RECEIVED;
Maybe<nsCString> urlForLogging;
const bool dumpCounters = StaticPrefs::dom_use_counters_dump_page();
if (dumpCounters) {
urlForLogging.emplace(
nsContentUtils::TruncatedURLForDisplay(mDocumentURI));
}
glean::use_counter::top_level_content_documents_destroyed.Add();
if (CanonicalBrowsingContext* bc = BrowsingContext()) {
if (bc->UsePrivateBrowsing()) {
glean::captcha_detection::pages_visited_pbm.Add();
}
else {
glean::captcha_detection::pages_visited.Add();
}
}
bool any =
false;
for (
const UseCounter uc : MakeEnumeratedRange(eUseCounter_Count)) {
if (!mPageUseCounters->mUseCounters[uc]) {
continue;
}
any =
true;
const char* metricName = IncrementUseCounter(uc,
/* aIsPage = */ true);
if (dumpCounters) {
printf_stderr(
"USE_COUNTER_PAGE: %s - %s\n", metricName,
urlForLogging->get());
}
}
if (!any) {
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
" > page use counter data was received, but was empty"));
result += PageUseCounterResultBits::EMPTY_DATA;
}
}
else {
MOZ_LOG(gUseCountersLog, LogLevel::Debug,
(
" > no page use counter data was received"));
}
mSentPageUseCounters =
true;
mPageUseCounters = nullptr;
return result;
}
Element* WindowGlobalParent::GetRootOwnerElement() {
WindowGlobalParent* top = TopWindowContext();
if (!top) {
return nullptr;
}
if (IsInProcess()) {
return top->BrowsingContext()->GetEmbedderElement();
}
if (BrowserParent* parent = top->GetBrowserParent()) {
return parent->GetOwnerElement();
}
return nullptr;
}
void WindowGlobalParent::NotifySessionStoreUpdatesComplete(Element* aEmbedder) {
if (!aEmbedder) {
aEmbedder = GetRootOwnerElement();
}
if (aEmbedder) {
if (nsCOMPtr<nsIObserverService> obs = services::GetObserverService()) {
obs->NotifyWhenScriptSafe(ToSupports(aEmbedder),
"browser-shutdown-tabstate-updated", nullptr);
}
}
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvRequestRestoreTabContent() {
CanonicalBrowsingContext* bc = BrowsingContext();
if (bc && bc->AncestorsAreCurrent()) {
bc->Top()->RequestRestoreTabContent(
this);
}
return IPC_OK();
}
nsCString BFCacheStatusToString(uint32_t aFlags) {
if (aFlags ==
0) {
return "0"_ns;
}
nsCString flags;
#define ADD_BFCACHESTATUS_TO_STRING(_flag) \
if (aFlags & BFCacheStatus::_flag) { \
if (!flags.IsEmpty()) { \
flags.Append(
'|'); \
} \
flags.AppendLiteral(
#_flag); \
aFlags &= ~BFCacheStatus::_flag; \
}
ADD_BFCACHESTATUS_TO_STRING(NOT_ALLOWED);
ADD_BFCACHESTATUS_TO_STRING(EVENT_HANDLING_SUPPRESSED);
ADD_BFCACHESTATUS_TO_STRING(SUSPENDED);
ADD_BFCACHESTATUS_TO_STRING(UNLOAD_LISTENER);
ADD_BFCACHESTATUS_TO_STRING(REQUEST);
ADD_BFCACHESTATUS_TO_STRING(ACTIVE_GET_USER_MEDIA);
ADD_BFCACHESTATUS_TO_STRING(ACTIVE_PEER_CONNECTION);
ADD_BFCACHESTATUS_TO_STRING(CONTAINS_EME_CONTENT);
ADD_BFCACHESTATUS_TO_STRING(CONTAINS_MSE_CONTENT);
ADD_BFCACHESTATUS_TO_STRING(HAS_ACTIVE_SPEECH_SYNTHESIS);
ADD_BFCACHESTATUS_TO_STRING(HAS_USED_VR);
ADD_BFCACHESTATUS_TO_STRING(CONTAINS_REMOTE_SUBFRAMES);
ADD_BFCACHESTATUS_TO_STRING(NOT_ONLY_TOPLEVEL_IN_BCG);
ADD_BFCACHESTATUS_TO_STRING(ABOUT_PAGE);
ADD_BFCACHESTATUS_TO_STRING(RESTORING);
ADD_BFCACHESTATUS_TO_STRING(BEFOREUNLOAD_LISTENER);
ADD_BFCACHESTATUS_TO_STRING(ACTIVE_LOCK);
ADD_BFCACHESTATUS_TO_STRING(ACTIVE_WEBTRANSPORT);
ADD_BFCACHESTATUS_TO_STRING(PAGE_LOADING);
#undef ADD_BFCACHESTATUS_TO_STRING
MOZ_ASSERT(aFlags ==
0,
"Missing stringification for enum value in BFCacheStatus.");
return flags;
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvUpdateBFCacheStatus(
const uint32_t& aOnFlags,
const uint32_t& aOffFlags) {
if (MOZ_UNLIKELY(MOZ_LOG_TEST(gSHIPBFCacheLog, LogLevel::Debug))) {
nsAutoCString uri(
"[no uri]");
if (mDocumentURI) {
uri = mDocumentURI->GetSpecOrDefault();
}
MOZ_LOG(gSHIPBFCacheLog, LogLevel::Debug,
(
"Setting BFCache flags for %s +(%s) -(%s)", uri.get(),
BFCacheStatusToString(aOnFlags).get(),
BFCacheStatusToString(aOffFlags).get()));
}
mBFCacheStatus |= aOnFlags;
mBFCacheStatus &= ~aOffFlags;
return IPC_OK();
}
mozilla::ipc::IPCResult
WindowGlobalParent::RecvUpdateActivePeerConnectionStatus(
bool aIsAdded) {
if (aIsAdded) {
RecvUpdateBFCacheStatus(BFCacheStatus::ACTIVE_PEER_CONNECTION,
0);
}
else {
RecvUpdateBFCacheStatus(
0, BFCacheStatus::ACTIVE_PEER_CONNECTION);
}
if (WindowGlobalParent* top = TopWindowContext()) {
CheckedUint32 newValue(top->mNumOfProcessesWithActivePeerConnections);
if (aIsAdded) {
++newValue;
}
else {
--newValue;
}
if (!newValue.isValid()) {
return IPC_FAIL(
this,
"mNumOfProcessesWithActivePeerConnections overflowed");
}
top->mNumOfProcessesWithActivePeerConnections = newValue.value();
(
void)top->SetHasActivePeerConnections(newValue.value() >
0);
}
return IPC_OK();
}
void WindowGlobalParent::UpdateFullscreenKeyboardLockStatus(
FullscreenKeyboardLock aStatus) {
auto* bc = GetBrowsingContext();
if (
auto* topChromeBc = bc ? bc->TopCrossChromeBoundary() : nullptr;
topChromeBc != bc) {
if (
auto* doc = topChromeBc->GetExtantDocument()) {
doc->SetFullscreenKeyboardLockStatus(aStatus);
}
}
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvSetSingleChannelId(
const Maybe<uint64_t>& aSingleChannelId) {
mSingleChannelId = aSingleChannelId;
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvSetDocumentDomain(
NotNull<nsIURI*> aDomain) {
if (mSandboxFlags & SANDBOXED_DOMAIN) {
// We're sandboxed; disallow setting domain
return IPC_FAIL(
this,
"Sandbox disallows domain setting.");
}
// Might need to do a featurepolicy check here, like we currently do in the
// child process?
nsCOMPtr<nsIURI> uri;
mDocumentPrincipal->GetDomain(getter_AddRefs(uri));
if (!uri) {
uri = mDocumentPrincipal->GetURI();
if (!uri) {
return IPC_OK();
}
}
if (!Document::IsValidDomain(uri, aDomain)) {
// Error: illegal domain
return IPC_FAIL(
this,
"Setting domain that's not a suffix of existing domain value.");
}
if (Group()->IsPotentiallyCrossOriginIsolated()) {
return IPC_FAIL(
this,
"Setting domain in a cross-origin isolated BC.");
}
mDocumentPrincipal->SetDomain(aDomain);
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvSetSiteIntegrityProtected(
NotNull<nsIURI*> aSourceURI, uint64_t aMaxAge) {
nsCOMPtr<nsISiteIntegrityService> service =
do_GetService(
"@mozilla.org/security/integrity;1");
if (!service) {
return IPC_OK();
}
OriginAttributes originAttributes =
DocumentPrincipal()->OriginAttributesRef();
StoragePrincipalHelper::UpdateOriginAttributesForNetworkState(
aSourceURI, originAttributes);
(
void)service->SetProtected(aSourceURI, originAttributes, aMaxAge);
return IPC_OK();
}
nsresult WindowGlobalParent::DoAddCertException(
bool aTemporary) {
nsCOMPtr<nsIChannel> failedChannel(GetFailedChannel());
NS_ENSURE_TRUE(failedChannel, NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIURI> failedChannelURI;
nsresult rv =
NS_GetFinalChannelURI(failedChannel, getter_AddRefs(failedChannelURI));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIURI> innerURI(NS_GetInnermostURI(failedChannelURI));
NS_ENSURE_TRUE(innerURI, NS_ERROR_DOM_INVALID_STATE_ERR);
nsAutoCString host;
rv = innerURI->GetAsciiHost(host);
NS_ENSURE_SUCCESS(rv, rv);
int32_t port;
rv = innerURI->GetPort(&port);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsITransportSecurityInfo> failedSecurityInfo;
rv = failedChannel->GetSecurityInfo(getter_AddRefs(failedSecurityInfo));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(failedSecurityInfo, NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsIX509Cert> cert;
rv = failedSecurityInfo->GetServerCert(getter_AddRefs(cert));
NS_ENSURE_SUCCESS(rv, rv);
NS_ENSURE_TRUE(cert, NS_ERROR_NOT_AVAILABLE);
nsCOMPtr<nsICertOverrideService> overrideService(
do_GetService(NS_CERTOVERRIDE_CONTRACTID));
NS_ENSURE_TRUE(overrideService, NS_ERROR_FAILURE);
return overrideService->RememberValidityOverride(
host, port, mDocumentPrincipal->OriginAttributesRef(), cert, aTemporary);
}
IPCResult WindowGlobalParent::RecvAddCertException(
bool aTemporary, AddCertExceptionResolver&& aResolver) {
aResolver(DoAddCertException(aTemporary));
return IPC_OK();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvReloadWithHttpsOnlyException() {
nsresult rv;
nsCOMPtr<nsIURI> currentURI = BrowsingContext()->Top()->GetCurrentURI();
if (!currentURI) {
return IPC_FAIL(
this,
"HTTPS-only mode: Failed to get current URI");
}
bool isViewSource = currentURI->SchemeIs(
"view-source");
nsCOMPtr<nsINestedURI> nestedURI = do_QueryInterface(currentURI);
nsCOMPtr<nsIURI> innerURI;
if (isViewSource) {
nestedURI->GetInnerURI(getter_AddRefs(innerURI));
}
else {
innerURI = currentURI;
}
if (!net::SchemeIsHttpOrHttps(innerURI)) {
return IPC_FAIL(
this,
"HTTPS-only mode: Illegal state");
}
// We replace the scheme with http, because the user wants to unbreak the
// whole page.
nsCOMPtr<nsIURI> newURI;
(
void)NS_MutateURI(innerURI).SetScheme(
"http"_ns).Finalize(
getter_AddRefs(newURI));
OriginAttributes originAttributes =
TopWindowContext()->DocumentPrincipal()->OriginAttributesRef();
originAttributes.SetFirstPartyDomain(
true, newURI);
nsCOMPtr<nsIPermissionManager> permMgr =
components::PermissionManager::Service();
if (!permMgr) {
return IPC_FAIL(
this,
"HTTPS-only mode: Failed to get Permission Manager service");
}
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateContentPrincipal(newURI, originAttributes);
rv = permMgr->AddFromPrincipal(
principal,
"https-only-load-insecure"_ns,
nsIHttpsOnlyModePermission::LOAD_INSECURE_ALLOW_SESSION,
nsIPermissionManager::EXPIRE_SESSION,
0);
if (NS_FAILED(rv)) {
return IPC_FAIL(
this,
"HTTPS-only mode: Failed to add permission to the principal");
}
nsCOMPtr<nsIURI> insecureURI = newURI;
if (isViewSource) {
nsAutoCString spec;
MOZ_ALWAYS_SUCCEEDS(newURI->GetSpec(spec));
if (NS_FAILED(
NS_NewURI(getter_AddRefs(insecureURI),
"view-source:"_ns + spec))) {
return IPC_FAIL(
this,
"HTTPS-only mode: Failed to re-construct view-source URI");
}
}
RefPtr<nsDocShellLoadState> loadState =
new nsDocShellLoadState(insecureURI);
loadState->SetTriggeringPrincipal(nsContentUtils::GetSystemPrincipal());
loadState->SetLoadType(LOAD_NORMAL_REPLACE);
loadState->SetHttpsUpgradeTelemetry(
nsILoadInfo::HTTPS_ONLY_UPGRADE_DOWNGRADE);
RefPtr<CanonicalBrowsingContext> topBC = BrowsingContext()->Top();
topBC->LoadURI(loadState,
/* setNavigating */ true);
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvGetStorageAccessPermission(
bool aIncludeIdentityCredential,
GetStorageAccessPermissionResolver&& aResolve) {
WindowGlobalParent* top = TopWindowContext();
if (!top) {
return IPC_FAIL_NO_REASON(
this);
}
nsIPrincipal* topPrincipal = top->DocumentPrincipal();
nsIPrincipal* principal = DocumentPrincipal();
uint32_t result;
nsresult rv = AntiTrackingUtils::TestStoragePermissionInParent(
topPrincipal, principal, &result);
if (NS_WARN_IF(NS_FAILED(rv))) {
aResolve(nsIPermissionManager::UNKNOWN_ACTION);
return IPC_OK();
}
if (result == nsIPermissionManager::ALLOW_ACTION) {
aResolve(nsIPermissionManager::ALLOW_ACTION);
return IPC_OK();
}
if (aIncludeIdentityCredential) {
bool canCollect;
rv = identity::CanSilentlyCollect(topPrincipal, principal, &canCollect);
if (NS_WARN_IF(NS_FAILED(rv))) {
aResolve(nsIPermissionManager::UNKNOWN_ACTION);
return IPC_OK();
}
if (canCollect) {
aResolve(nsIPermissionManager::ALLOW_ACTION);
return IPC_OK();
}
}
aResolve(result);
return IPC_OK();
}
void WindowGlobalParent::ActorDestroy(ActorDestroyReason aWhy) {
if (GetBrowsingContext()->IsTopContent()) {
glean::orb::did_ever_block_response
.EnumGet(mShouldReportHasBlockedOpaqueResponse
? glean::orb::DidEverBlockResponseLabel::eTrue
: glean::orb::DidEverBlockResponseLabel::eFalse)
.Add();
}
PageUseCounterResult pageUseCounterResult;
if (mPageUseCountersWindow) {
pageUseCounterResult =
mPageUseCountersWindow->FinishAccumulatingPageUseCounters();
mPageUseCountersWindow = nullptr;
}
if (GetBrowsingContext()->IsTopContent() &&
!mDocumentPrincipal->SchemeIs(
"about")) {
// Record the page load
uint32_t pageLoaded =
1;
glean::mixed_content::unblock_counter.AccumulateSingleSample(pageLoaded);
// Record the mixed content status of the docshell in Telemetry
enum {
NO_MIXED_CONTENT =
0,
// There is no Mixed Content on the page
MIXED_DISPLAY_CONTENT =
1,
// The page attempted to load Mixed Display Content
MIXED_ACTIVE_CONTENT =
2,
// The page attempted to load Mixed Active Content
MIXED_DISPLAY_AND_ACTIVE_CONTENT =
3 // The page attempted to load Mixed
// Display & Mixed Active Content
};
bool hasMixedDisplay =
mSecurityState &
(nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT);
bool hasMixedActive =
mSecurityState &
(nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT);
uint32_t mixedContentLevel = NO_MIXED_CONTENT;
if (hasMixedDisplay && hasMixedActive) {
mixedContentLevel = MIXED_DISPLAY_AND_ACTIVE_CONTENT;
}
else if (hasMixedActive) {
mixedContentLevel = MIXED_ACTIVE_CONTENT;
}
else if (hasMixedDisplay) {
mixedContentLevel = MIXED_DISPLAY_CONTENT;
}
glean::mixed_content::page_load.AccumulateSingleSample(mixedContentLevel);
if (GetDocTreeHadMedia()) {
glean::media::element_in_page_count.Add(
1);
}
}
ContentParent* cp = nullptr;
if (!IsInProcess()) {
cp =
static_cast<ContentParent*>(Manager()->Manager());
}
Group()->EachOtherParent(cp, [&](ContentParent* otherContent) {
// Keep the WindowContext and our BrowsingContextGroup alive until other
// processes have acknowledged it has been discarded.
Group()->AddKeepAlive();
auto callback = [self = RefPtr{
this}](
auto) {
self->Group()->RemoveKeepAlive();
};
otherContent->SendDiscardWindowContext(InnerWindowId(), callback, callback);
});
// Report content blocking log when destroyed. In addition to the regular
// content-blocking log flush (shared with the flush-on-query path via
// MaybeReportContentBlockingLog), the teardown path also emits the
// canvas/font/email fingerprinting Glean metrics that are only meaningful
// at end-of-page.
MaybeReportContentBlockingLog();
if (!IsInProcess()) {
RefPtr<BrowserParent> browserParent =
static_cast<BrowserParent*>(Manager());
if (browserParent) {
nsCOMPtr<nsILoadContext> loadContext = browserParent->GetLoadContext();
if (loadContext && !loadContext->UsePrivateBrowsing() &&
BrowsingContext()->IsTopContent() && mDocumentURI &&
net::SchemeIsHttpOrHttps(mDocumentURI)) {
GetContentBlockingLog()->ReportCanvasFingerprintingLog(
DocumentPrincipal());
GetContentBlockingLog()->ReportFontFingerprintingLog(
DocumentPrincipal());
GetContentBlockingLog()->ReportEmailTrackingLog(DocumentPrincipal());
}
}
}
// Note that our WindowContext has become discarded.
WindowContext::Discard();
// Destroy our JSWindowActors, and reject any pending queries.
JSActorDidDestroy();
nsCOMPtr<nsIObserverService> obs = services::GetObserverService();
if (obs) {
obs->NotifyObservers(ToSupports(
this),
"window-global-destroyed", nullptr);
}
if (mOriginCounter) {
mOriginCounter->Accumulate();
}
}
WindowGlobalParent::~WindowGlobalParent() =
default;
void WindowGlobalParent::MaybeReportContentBlockingLog() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
// No blocking is recorded for in-process (non-remote) documents — see
// NotifyContentBlockingEvent.
if (IsInProcess()) {
return;
}
RefPtr<BrowserParent> browserParent =
static_cast<BrowserParent*>(Manager());
if (!browserParent) {
return;
}
nsCOMPtr<nsILoadContext> loadContext = browserParent->GetLoadContext();
if (!loadContext || loadContext->UsePrivateBrowsing()) {
return;
}
if (!BrowsingContext()->IsTopContent()) {
return;
}
GetContentBlockingLog()->ReportLog();
}
/* static */
void WindowGlobalParent::FlushAllContentBlockingLogs() {
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(NS_IsMainThread());
nsTArray<RefPtr<BrowsingContextGroup>> groups;
BrowsingContextGroup::GetAllGroups(groups);
for (
const auto& group : groups) {
for (
const auto& bc : group->Toplevels()) {
if (!bc) {
continue;
}
RefPtr<CanonicalBrowsingContext> canonical = bc->Canonical();
if (!canonical) {
continue;
}
RefPtr<WindowGlobalParent> wgp = canonical->GetCurrentWindowGlobal();
if (!wgp) {
continue;
}
wgp->MaybeReportContentBlockingLog();
}
}
}
JSObject* WindowGlobalParent::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) {
return WindowGlobalParent_Binding::Wrap(aCx,
this, aGivenProto);
}
nsIGlobalObject* WindowGlobalParent::GetParentObject() {
return xpc::NativeGlobal(xpc::PrivilegedJunkScope());
}
nsIDOMProcessParent* WindowGlobalParent::GetDomProcess() {
if (RefPtr<BrowserParent> browserParent = GetBrowserParent()) {
return browserParent->Manager();
}
return InProcessParent::Singleton();
}
void WindowGlobalParent::DidBecomeCurrentWindowGlobal(
bool aCurrent) {
WindowGlobalParent* top = BrowsingContext()->GetTopWindowContext();
if (top && top->mOriginCounter) {
top->mOriginCounter->UpdateSiteOriginsFrom(
this,
/* aIncrease = */ aCurrent);
}
if (!aCurrent && Fullscreen()) {
ExitTopChromeDocumentFullscreen();
}
}
bool WindowGlobalParent::ShouldTrackSiteOriginTelemetry() {
CanonicalBrowsingContext* bc = BrowsingContext();
if (!bc->IsTopContent()) {
return false;
}
RefPtr<BrowserParent> browserParent = GetBrowserParent();
if (!browserParent ||
!IsWebRemoteType(browserParent->Manager()->GetRemoteType())) {
return false;
}
return DocumentPrincipal()->GetIsContentPrincipal();
}
void WindowGlobalParent::AddSecurityState(uint32_t aStateFlags) {
MOZ_ASSERT(TopWindowContext() ==
this);
MOZ_ASSERT((aStateFlags &
(nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT |
nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT |
nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT |
nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT |
nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED |
nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADE_FAILED |
nsIWebProgressListener::STATE_HTTPS_ONLY_MODE_UPGRADED_FIRST)) ==
aStateFlags,
"Invalid flags specified!");
if ((mSecurityState & aStateFlags) == aStateFlags) {
return;
}
mSecurityState |= aStateFlags;
if (GetBrowsingContext()->GetCurrentWindowGlobal() ==
this) {
GetBrowsingContext()->UpdateSecurityState();
}
}
bool WindowGlobalParent::HasActivePeerConnections() {
MOZ_ASSERT(TopWindowContext() ==
this,
"mNumOfProcessesWithActivePeerConnections is set only "
"in the top window context");
return mNumOfProcessesWithActivePeerConnections >
0;
}
void WindowGlobalParent::ExitTopChromeDocumentFullscreen() {
RefPtr<CanonicalBrowsingContext> chromeTop =
BrowsingContext()->TopCrossChromeBoundary();
if (Document* chromeDoc = chromeTop->GetDocument()) {
Document::ClearPendingFullscreenRequests(chromeDoc);
if (chromeDoc->Fullscreen()) {
// This only clears the DOM fullscreen, will not exit from browser UI
// fullscreen mode.
Document::AsyncExitFullscreen(chromeDoc);
}
}
}
void WindowGlobalParent::SetShouldReportHasBlockedOpaqueResponse(
nsContentPolicyType aContentPolicy) {
// It's always okay to block TYPE_BEACON, TYPE_PING and TYPE_CSP_REPORT in
// the parent process because content processes can do nothing to their
// responses. Hence excluding them from the telemetry as blocking
// them have no webcompat concerns.
if (aContentPolicy != nsIContentPolicy::TYPE_BEACON &&
aContentPolicy != nsIContentPolicy::TYPE_PING &&
aContentPolicy != nsIContentPolicy::TYPE_CSP_REPORT) {
if (IsTop()) {
mShouldReportHasBlockedOpaqueResponse =
true;
}
}
}
IPCResult WindowGlobalParent::RecvSetCookies(
const nsCString& aBaseDomain,
const OriginAttributes& aOriginAttributes,
nsIURI* aHost,
bool aIsThirdParty,
const nsTArray<CookieStruct>& aCookies) {
// Get CookieServiceParent via
// ContentParent->NeckoParent->CookieServiceParent.
ContentParent* contentParent = GetContentParent();
NS_ENSURE_TRUE(contentParent, IPC_OK());
net::PNeckoParent* neckoParent =
LoneManagedOrNullAsserts(contentParent->ManagedPNeckoParent());
NS_ENSURE_TRUE(neckoParent, IPC_OK());
net::PCookieServiceParent* csParent =
LoneManagedOrNullAsserts(neckoParent->ManagedPCookieServiceParent());
NS_ENSURE_TRUE(csParent, IPC_OK());
auto* cs =
static_cast<net::CookieServiceParent*>(csParent);
return cs->SetCookies(aBaseDomain, aOriginAttributes, aHost, aIsThirdParty,
aCookies, GetBrowsingContext());
}
IPCResult WindowGlobalParent::RecvRecordUserActivationForBTP() {
WindowGlobalParent* top = TopWindowContext();
if (!top) {
return IPC_OK();
}
nsIPrincipal* principal = top->DocumentPrincipal();
if (!principal) {
return IPC_OK();
}
DebugOnly<nsresult> rv = BounceTrackingProtection::RecordUserActivation(
principal, Some(PR_Now()), top->BrowsingContext());
NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
"Failed to record BTP user activation.");
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvRecordUserInteractionForPermissions() {
WindowGlobalParent* top = TopWindowContext();
if (!top) {
return IPC_OK();
}
nsIPrincipal* principal = top->DocumentPrincipal();
if (!principal) {
return IPC_OK();
}
nsCOMPtr<nsIPermissionManager> permMgr =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
if (permMgr) {
(
void)permMgr->UpdateLastInteractionForPrincipal(principal);
}
return IPC_OK();
}
IPCResult WindowGlobalParent::RecvNotifyAudioSessionTypeOverride(
const dom::AudioSessionType& aType) {
// The MediaController lives on the top-level BC, but the override is
// keyed by the sender's BC id (which may be an iframe). The setter must
// be honoured even before any controllable media has started, so call
// GetMediaController() which creates the controller on demand.
if (
auto* top = BrowsingContext()->Top()) {
if (RefPtr<MediaController> controller = top->GetMediaController()) {
controller->SetAudioSessionTypeOverride(BrowsingContext()->Id(), aType);
}
}
return IPC_OK();
}
already_AddRefed<PSerialManagerParent>
WindowGlobalParent::AllocPSerialManagerParent() {
return MakeAndAddRef<SerialManagerParent>();
}
mozilla::ipc::IPCResult WindowGlobalParent::RecvPSerialManagerConstructor(
PSerialManagerParent* aActor) {
auto* manager =
static_cast<SerialManagerParent*>(aActor);
manager->Init(BrowsingContext()->GetBrowserId());
return IPC_OK();
}
already_AddRefed<PWebAuthnTransactionParent>
WindowGlobalParent::AllocPWebAuthnTransactionParent() {
return MakeAndAddRef<WebAuthnTransactionParent>();
}
already_AddRefed<PWebIdentityParent>
WindowGlobalParent::AllocPWebIdentityParent() {
return MakeAndAddRef<WebIdentityParent>();
}
already_AddRefed<PDigitalCredentialParent>
WindowGlobalParent::AllocPDigitalCredentialParent() {
return MakeAndAddRef<DigitalCredentialParent>();
}
NS_IMPL_CYCLE_COLLECTION_CLASS(WindowGlobalParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN_INHERITED(WindowGlobalParent,
WindowContext)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mPageUseCountersWindow)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mStaticCloneOf)
tmp->UnlinkManager();
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INHERITED(WindowGlobalParent,
WindowContext)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPageUseCountersWindow)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mStaticCloneOf)
if (!tmp->IsInProcess()) {
CycleCollectionNoteChild(cb,
static_cast<BrowserParent*>(tmp->Manager()),
"Manager()");
}
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN_INHERITED(WindowGlobalParent,
WindowContext)
NS_IMPL_CYCLE_COLLECTION_TRACE_END
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WindowGlobalParent)
NS_INTERFACE_MAP_END_INHERITING(WindowContext)
NS_IMPL_ADDREF_INHERITED(WindowGlobalParent, WindowContext)
NS_IMPL_RELEASE_INHERITED(WindowGlobalParent, WindowContext)
}
// namespace mozilla::dom