Source code
Revision control
Copy as Markdown
Other Tools
/* 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,
#include "mozilla/dom/RemoteType.h"
#include "OriginAttributes.h"
#include "ipc/IPCMessageUtilsSpecializations.h"
#include "mozilla/BasePrincipal.h"
#include "mozilla/NeverDestroyed.h"
#include "nsIURI.h"
#include "nsNetUtil.h"
#include "nsPrintfCString.h"
#include "nsURLHelper.h"
namespace mozilla::dom {
// URLParams value used to indicate a boolean 'true' value.
// The lack of the attribute is used to indicate 'false'.
static constexpr nsLiteralCString kRemoteTypeAttrTrue = "1"_ns;
/* static */
const RemoteType& RemoteType::NotRemote() {
static NeverDestroyed<RemoteType> sNotRemote(Kind::NotRemote);
return *sNotRemote;
}
/* static */
RemoteType RemoteType::SharedWeb(const OriginAttributes& aAttrs) {
RemoteType type(Kind::WebContent);
type.mUserContextId = aAttrs.mUserContextId;
type.mPrivateBrowsingId = aAttrs.mPrivateBrowsingId;
type.mGeckoViewSessionContextId = aAttrs.mGeckoViewSessionContextId;
MOZ_ASSERT(type.CheckValidity());
return type;
}
RemoteType::RemoteType() = default;
RemoteType::~RemoteType() = default;
RemoteType::RemoteType(RemoteType::Kind aKind) : mKind(aKind) {
MOZ_RELEASE_ASSERT(CheckValidity());
}
RemoteType::RemoteType(const RemoteType&) = default;
RemoteType& RemoteType::operator=(const RemoteType&) = default;
bool RemoteType::operator==(const RemoteType& aOther) const {
bool isEqual =
mKind == aOther.mKind && mOriginNoSuffix == aOther.mOriginNoSuffix &&
std::apply(
[&, this](auto&&... aAttrs) -> bool {
return ((this->*aAttrs.mMember == aOther.*aAttrs.mMember) && ...);
},
kAttrs);
// Sanity checks to catch if someone misses a flag in one of the methods.
MOZ_ASSERT_IF(isEqual, Hash() == aOther.Hash());
MOZ_ASSERT_IF(isEqual, Stringify() == aOther.Stringify());
return isEqual;
}
// We can directly add integers & enums to the hash, but need to call HashString
// before adding strings to the hash.
template <typename T>
static auto PreHashAttr(const T& aValue) {
if constexpr (std::is_integral_v<T> || std::is_enum_v<T>) {
return aValue;
} else {
return HashString(aValue);
}
}
HashNumber RemoteType::Hash() const {
return std::apply(
[&, this](auto&&... aAttrs) -> HashNumber {
return HashGeneric(PreHashAttr(mKind), HashString(mOriginNoSuffix),
PreHashAttr(this->*aAttrs.mMember)...);
},
kAttrs);
}
// NOTE: This specifically parses the "kind" part of the remote type string,
// rather than the value returned from `StringifyKind`. This is only relevant
// for `NotRemote`, which uses "parent" for `StringifyKind`, but VoidCString()
// for `Stringify`.
//
// FIXME: We should make this more aligned by changing the representation of
static RemoteType::Kind ParseKind(const nsACString& aKindStr) {
if (aKindStr.IsVoid()) {
return RemoteType::Kind::NotRemote;
}
if (aKindStr == "prealloc"_ns) {
return RemoteType::Kind::Prealloc;
}
// NOTE: The 'webIsolated' and 'web' prefixes are interpreted the same, and
// generated based on the presence of a URL in ToString() to maintain
// a level of backwards compatibility with tests assuming the old format.
if (aKindStr == "web"_ns || aKindStr == "webIsolated"_ns) {
return RemoteType::Kind::WebContent;
}
if (aKindStr == "webCOOP+COEP") {
return RemoteType::Kind::WebCoopCoep;
}
if (aKindStr == "webServiceWorker") {
return RemoteType::Kind::WebServiceWorker;
}
if (aKindStr == "file"_ns) {
return RemoteType::Kind::File;
}
if (aKindStr == "privilegedabout"_ns) {
return RemoteType::Kind::PrivilegedAbout;
}
if (aKindStr == "privilegedmozilla"_ns) {
return RemoteType::Kind::PrivilegedMozilla;
}
if (aKindStr == "extension"_ns) {
return RemoteType::Kind::Extension;
}
if (aKindStr == "inference"_ns) {
return RemoteType::Kind::Inference;
}
return RemoteType::Kind::Unknown;
}
static bool ParseAttr(const nsACString& aValue, bool& aMember) {
aMember = true;
return aValue == kRemoteTypeAttrTrue;
}
static bool ParseAttr(const nsACString& aValue, uint32_t& aMember) {
nsresult rv = NS_OK;
aMember = aValue.ToInteger(&rv);
return NS_SUCCEEDED(rv);
}
static bool ParseAttr(const nsACString& aValue, nsString& aMember) {
aMember = NS_ConvertUTF8toUTF16(aValue);
return true;
}
/* static */ RemoteType RemoteType::ParseNoValidityCheck(
const nsACString& aRemoteType) {
RemoteType type;
int32_t equalIdx = aRemoteType.FindChar('=');
if (equalIdx == kNotFound) {
type.mKind = ParseKind(aRemoteType);
return type;
}
type.mKind = ParseKind(Substring(aRemoteType, 0, equalIdx));
nsDependentCSubstring site(aRemoteType, equalIdx + 1);
int32_t caretIdx = site.RFindChar('^');
if (caretIdx != kNotFound) {
bool ok = URLParams::Parse(
Substring(site, caretIdx + 1), true,
[&](const nsACString& aName, const nsACString& aValue) {
return std::apply(
[&](auto&&... aAttrs) {
return ((aName == aAttrs.mName &&
ParseAttr(aValue, type.*aAttrs.mMember)) ||
...);
},
kAttrs);
});
NS_ENSURE_TRUE(ok, RemoteType{});
type.mOriginNoSuffix = Substring(site, 0, caretIdx);
} else {
type.mOriginNoSuffix = site;
}
return type;
}
/* static */ RemoteType RemoteType::Parse(const nsACString& aRemoteType) {
RemoteType type = ParseNoValidityCheck(aRemoteType);
// Ensure the type parsed to a valid RemoteType, as we never want to expose an
// non-Unknown but invalid RemoteType outside of this .cpp file.
NS_ENSURE_TRUE(type.CheckValidity(), RemoteType{});
// Ensure the string representation of the remote type matches aRemoteType.
// This is much stricter than e.g. principal or OriginAttributes parsing, but
// we shouldn't be storing RemoteType instances in the profile, so it should
// be OK to be strict here.
NS_ENSURE_TRUE(type.Stringify() == aRemoteType, RemoteType{});
return type;
}
nsCString RemoteType::StringifyKind() const {
MOZ_ASSERT(CheckValidity());
switch (mKind) {
case RemoteType::Kind::NotRemote:
// NOTE: Unlike other remote types, the full RemoteType string for
return "parent"_ns;
case RemoteType::Kind::Prealloc:
return "prealloc"_ns;
case RemoteType::Kind::WebContent:
// NOTE: See the comment in ParseKind for why we do this.
return HasOrigin() ? "webIsolated"_ns : "web"_ns;
case RemoteType::Kind::WebCoopCoep:
return "webCOOP+COEP"_ns;
case RemoteType::Kind::WebServiceWorker:
return "webServiceWorker"_ns;
case RemoteType::Kind::File:
return "file"_ns;
case RemoteType::Kind::PrivilegedAbout:
return "privilegedabout"_ns;
case RemoteType::Kind::PrivilegedMozilla:
return "privilegedmozilla"_ns;
case RemoteType::Kind::Extension:
return "extension"_ns;
case RemoteType::Kind::Inference:
return "inference"_ns;
case RemoteType::Kind::Unknown:
// This is not a valid remote type, but we return a string here to avoid
// crashes in logs if an unknown remote type is printed.
return "<unknown>"_ns;
default:
MOZ_CRASH("Unsupported Kind in StringifyKind");
}
}
static void SetAttr(URLParams& aParam, const nsACString& aName,
const bool& aMember) {
if (aMember) {
aParam.Set(aName, kRemoteTypeAttrTrue);
}
}
static void SetAttr(URLParams& aParam, const nsACString& aName,
const uint32_t& aMember) {
if (aMember != 0) {
aParam.Set(aName, nsPrintfCString("%" PRIu32, aMember));
}
}
static void SetAttr(URLParams& aParam, const nsACString& aName,
const nsString& aMember) {
if (!aMember.IsEmpty()) {
aParam.Set(aName, NS_ConvertUTF16toUTF8(aMember));
}
}
nsAutoCString RemoteType::StringifyMeta() const {
MOZ_ASSERT(CheckValidity());
nsAutoCString meta(mOriginNoSuffix);
if (HasAttrs()) {
URLParams params;
std::apply(
[&, this](auto&&... aAttrs) {
(SetAttr(params, aAttrs.mName, this->*aAttrs.mMember), ...);
},
kAttrs);
nsAutoCString paramsStr;
params.Serialize(paramsStr, /* encode */ true);
meta += "^"_ns + paramsStr;
}
return meta;
}
nsCString RemoteType::Stringify() const {
MOZ_ASSERT(CheckValidity());
if (IsNotRemote()) {
return VoidCString();
}
nsCString kindStr = StringifyKind();
if (HasMeta()) {
kindStr += "="_ns + StringifyMeta();
}
return kindStr;
}
static bool AttrIsSet(const bool& aValue) { return aValue; }
static bool AttrIsSet(const uint32_t& aValue) { return aValue != 0; }
static bool AttrIsSet(const nsString& aValue) { return !aValue.IsEmpty(); }
bool RemoteType::HasAttrs() const {
return std::apply(
[this](auto&&... aArgs) {
return (AttrIsSet(this->*aArgs.mMember) || ...);
},
kAttrs);
}
bool RemoteType::CheckValidity() const {
// If we don't have a WebContent kind, no extra fields can be used.
if (!IsWeb() && HasMeta()) {
NS_WARNING("Invalid RemoteType: Non-web type has metadata");
return false;
}
if (HasOrigin()) {
nsCOMPtr<nsIURI> uri;
if (NS_FAILED(NS_NewURI(getter_AddRefs(uri), mOriginNoSuffix))) {
NS_WARNING("Invalid RemoteType: Invalid OriginNoSuffix URI");
return false;
}
nsCOMPtr<nsIPrincipal> principal =
BasePrincipal::CreateContentPrincipal(uri, GetOriginAttributes());
if (!principal) {
NS_WARNING("Invalid RemoteType: Failed to create content principal");
return false;
}
// Currently we always isolate by site, never by origin, so a remote type
// should only contain a site origin.
nsAutoCString origin;
nsAutoCString siteOrigin;
MOZ_ALWAYS_SUCCEEDS(principal->GetOriginNoSuffix(origin));
MOZ_ALWAYS_SUCCEEDS(principal->GetSiteOriginNoSuffix(siteOrigin));
if (origin != mOriginNoSuffix || origin != siteOrigin) {
NS_WARNING("Invalid RemoteType: Non-canonical OriginNoSuffix");
return false;
}
} else if (IsWebCoopCoep() || IsWebServiceWorker()) {
// These remote types require a URI specified.
NS_WARNING(
"Invalid RemoteType: Web{CoopCoep/ServiceWorker} without site origin");
return false;
}
return true;
}
bool RemoteType::SupportsPrealloc() const {
MOZ_ASSERT(IsKnown());
MOZ_ASSERT(mKind != Kind::Prealloc);
return mKind != Kind::NotRemote && mKind != Kind::File &&
mKind != Kind::PrivilegedAbout && mKind != Kind::Extension &&
!mDisableJit;
}
OriginAttributes RemoteType::GetOriginAttributes() const {
OriginAttributes attrs;
attrs.mUserContextId = mUserContextId;
attrs.mPrivateBrowsingId = mPrivateBrowsingId;
attrs.mGeckoViewSessionContextId = mGeckoViewSessionContextId;
return attrs;
}
RemoteType RemoteType::WithDisableJit(bool aDisableJit) const {
MOZ_RELEASE_ASSERT(IsWeb());
RemoteType copy(*this);
copy.mDisableJit = aDisableJit;
MOZ_ASSERT(copy.CheckValidity());
return copy;
}
RemoteType RemoteType::WithSiteOrigin(const nsACString& aSiteOriginNoSuffix,
Kind aNewKind) const {
MOZ_RELEASE_ASSERT(IsSharedWeb());
RemoteType copy(*this);
copy.mKind = aNewKind;
copy.mOriginNoSuffix = aSiteOriginNoSuffix;
// NOTE: We release-assert validity, as an invalid aSiteOriginURI or Kind
// could create an invalid RemoteType.
MOZ_RELEASE_ASSERT(copy.CheckValidity());
MOZ_RELEASE_ASSERT(copy.IsIsolatedWeb());
return copy;
}
} // namespace mozilla::dom
// NOTE: This could probably be made more efficient by removing the
// serialization to a string, but this keeps the logic simpler for now.
namespace IPC {
void ParamTraits<mozilla::dom::RemoteType>::Write(MessageWriter* aWriter,
const paramType& aParam) {
MOZ_ASSERT(aParam.IsKnown(), "Cannot send unknown RemoteType");
WriteParam(aWriter, aParam.Stringify());
}
bool ParamTraits<mozilla::dom::RemoteType>::Read(MessageReader* aReader,
paramType* aResult) {
nsCString s;
if (!ReadParam(aReader, &s)) {
return false;
}
*aResult = mozilla::dom::RemoteType::Parse(s);
return aResult->IsKnown();
}
} // namespace IPC