Files
sousa-gecko/dom/ipc/ParentProcessChannelHandle.cpp
Nika Layzell 8f39535371 Bug 2022249 - Part 1: Track the parent process channel as passed to WindowGlobalParent, r=smaug,ipc-reviewers,necko-reviewers,valentin,mccr8
This is done by creating a nsID when the object is passed into the content
process. An object containing the nsIChannel instance, as well as information
about where we expect it to be loaded, is kept on the ContentParent actor.

When the actual document is created, this nsID is sent up (wrapped in the
ParentProcessChannelHandle type), and re-matched with the entry in
ContentParent. This allows the actual state to be linked from the
DocumentLoadListener to the final WindowGlobalParent instance.

For now, only the channel is passed up as metadata, with some context as to
where the load is expected to complete being carried along as validation. It
would be good to also validate other information (such as the principal & final
document URI) against this channel object, but that is being left for another
bug.

In the future, we should also likely validate cases where no channel is
provided to ensure it is one of the known cases where a channel is not
available (i.e. initial about:blank, srcdoc, or javascript: URI).

Differential Revision: https://phabricator.services.mozilla.com/D286926
2026-06-11 15:46:18 +00:00

165 lines
5.2 KiB
C++

/* 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 "ParentProcessChannelHandle.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/WindowGlobalParent.h"
namespace mozilla::dom {
ParentProcessChannelHandle::ParentProcessChannelHandle(
const ExpectedContext& aExpectedContext, nsIChannel* aChannel)
: mUuidOrRecord(VariantType<Record>{},
Record{
.mExpectedContext = aExpectedContext,
.mChannel = aChannel,
}) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_IsParentProcess());
}
ParentProcessChannelHandle::ParentProcessChannelHandle(const nsID& aUuid)
: mUuidOrRecord(VariantType<nsID>{}, aUuid) {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_IsContentProcess());
}
Result<nsCOMPtr<nsIChannel>, StaticString>
ParentProcessChannelHandle::GetChannel(
CanonicalBrowsingContext* aBrowsingContext,
WindowGlobalParent* aStaticCloneOf) const {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_IsParentProcess());
MOZ_ASSERT(mUuidOrRecord.is<Record>());
const Record& record = mUuidOrRecord.as<Record>();
// Static clones do not create additional requests, meaning that channels will
// be attributed to the original BrowsingContext which was statically cloned.
if (aStaticCloneOf) {
aBrowsingContext = aStaticCloneOf->BrowsingContext();
}
MOZ_TRY(record.mExpectedContext.match(
[&](const ParentProcessChannelHandle::ExpectLoadedWithin& aExpect)
-> Result<Ok, StaticString> {
if (aExpect.mBrowsingContextId != aBrowsingContext->Id()) {
return Err(StaticString("wrong browsing context"));
}
return Ok();
},
[&](const ParentProcessChannelHandle::ExpectChildOf& aExpect)
-> Result<Ok, StaticString> {
if (!aBrowsingContext->GetParentWindowContext()) {
return Err(StaticString("missing parent window context"));
}
if (aExpect.mParentWindowId !=
aBrowsingContext->GetParentWindowContext()->InnerWindowId()) {
return Err(StaticString("wrong parent window context"));
}
return Ok();
}));
return record.mChannel;
}
const nsID& ParentProcessChannelHandle::GetUuid() const {
MOZ_ASSERT(NS_IsMainThread());
MOZ_ASSERT(XRE_IsContentProcess());
MOZ_ASSERT(mUuidOrRecord.is<nsID>());
return mUuidOrRecord.as<nsID>();
}
ParentProcessChannelHandle::~ParentProcessChannelHandle() {
MOZ_ASSERT(NS_IsMainThread());
// If we're in a content process, let our ContentParent know that there are no
// more references to this uuid.
if (XRE_IsContentProcess()) {
MOZ_ASSERT(mUuidOrRecord.is<nsID>());
RefPtr<ContentChild> cc = ContentChild::GetSingleton();
if (cc && cc->CanSend()) {
cc->SendDropParentProcessChannelHandle(mUuidOrRecord.as<nsID>());
}
}
}
} // namespace mozilla::dom
namespace IPC {
void ParamTraits<mozilla::dom::ParentProcessChannelHandle*>::Write(
MessageWriter* aWriter, paramType* aParam) {
IPC::WriteParam(aWriter, !!aParam);
if (!aParam) {
return;
}
if (!aWriter->GetActor() ||
aWriter->GetActor()->ToplevelProtocol()->GetProtocolId() !=
PContentMsgStart) {
aWriter->FatalError(
"ParentProcessChannelHandle can only be sent over PContent");
return;
}
if (XRE_IsContentProcess()) {
MOZ_RELEASE_ASSERT(aWriter->GetActor()->GetSide() ==
mozilla::ipc::ChildSide);
IPC::WriteParam(aWriter, aParam->GetUuid());
} else {
MOZ_RELEASE_ASSERT(aWriter->GetActor()->GetSide() ==
mozilla::ipc::ParentSide);
mozilla::dom::ContentParent* cp = static_cast<mozilla::dom::ContentParent*>(
aWriter->GetActor()->ToplevelProtocol());
nsID uuid = cp->AddParentProcessChannelHandle(aParam);
IPC::WriteParam(aWriter, uuid);
}
}
bool ParamTraits<mozilla::dom::ParentProcessChannelHandle*>::Read(
MessageReader* aReader, RefPtr<paramType>* aResult) {
bool nonNull = false;
if (!IPC::ReadParam(aReader, &nonNull)) {
return false;
}
if (!nonNull) {
*aResult = nullptr;
return true;
}
if (!aReader->GetActor() ||
aReader->GetActor()->ToplevelProtocol()->GetProtocolId() !=
PContentMsgStart) {
aReader->FatalError(
"ParentProcessChannelHandle can only be sent over PContent");
return false;
}
nsID uuid{};
if (!IPC::ReadParam(aReader, &uuid)) {
return false;
}
if (XRE_IsContentProcess()) {
MOZ_RELEASE_ASSERT(aReader->GetActor()->GetSide() ==
mozilla::ipc::ChildSide);
*aResult = new paramType(uuid);
return true;
}
MOZ_RELEASE_ASSERT(aReader->GetActor()->GetSide() ==
mozilla::ipc::ParentSide);
mozilla::dom::ContentParent* cp = static_cast<mozilla::dom::ContentParent*>(
aReader->GetActor()->ToplevelProtocol());
*aResult = cp->ReadParentProcessChannelHandle(uuid);
return *aResult != nullptr;
}
} // namespace IPC