Support CSS shadow parts

This commit is contained in:
Basilisk-Dev 2026-05-11 19:33:30 -04:00 committed by wuggy
commit a852c0a86a
10 changed files with 1882 additions and 29 deletions

View file

@ -404,6 +404,22 @@ RuleHash::AppendUniversalRule(const RuleSelectorPair& aRuleInfo)
RuleValue(aRuleInfo, mRuleCount++, mQuirksMode));
}
static bool
SelectorContainsPseudoClass(nsCSSSelector* aSelector, CSSPseudoClassType aType)
{
for (nsCSSSelector* selector = aSelector; selector; selector = selector->mNext) {
for (nsPseudoClassList* pseudoClass = selector->mPseudoClassList;
pseudoClass;
pseudoClass = pseudoClass->mNext) {
if (pseudoClass->mType == aType) {
return true;
}
}
}
return false;
}
void
RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo)
{
@ -411,7 +427,10 @@ RuleHash::AppendRule(const RuleSelectorPair& aRuleInfo)
if (selector->IsPseudoElement()) {
selector = selector->mNext;
}
if (nullptr != selector->mIDList) {
if (SelectorContainsPseudoClass(selector, CSSPseudoClassType::part)) {
AppendUniversalRule(aRuleInfo);
RULE_HASH_STAT_INCREMENT(mUniversalSelectors);
} else if (nullptr != selector->mIDList) {
AppendRuleToTable(&mIdTable, selector->mIDList->mAtom, aRuleInfo);
RULE_HASH_STAT_INCREMENT(mIdSelectors);
} else if (nullptr != selector->mClassList) {
@ -452,6 +471,10 @@ LookForTargetPseudo(nsCSSSelector* aSelector,
nsRestyleHint* possibleChange)
{
if (aMatchContext->mOnlyMatchHostPseudo) {
if (SelectorContainsPseudoClass(aSelector, CSSPseudoClassType::part)) {
return true;
}
while (aSelector && aSelector->mNext != nullptr) {
aSelector = aSelector->mNext;
}
@ -502,7 +525,8 @@ ContentEnumFunc(const RuleValue& value,
data->mTreeMatchContext.SetHaveRelevantLink();
}
// XXX: Ignore the ancestor filter if we're testing the assigned slot.
bool useAncestorFilter = !(data->mTreeMatchContext.mForAssignedSlot);
bool useAncestorFilter = !(data->mTreeMatchContext.mForAssignedSlot ||
data->mTreeMatchContext.mOnlyMatchHostPseudo);
if (useAncestorFilter && ancestorFilter &&
!ancestorFilter->MightHaveMatchingAncestor<RuleValue::eMaxAncestorHashes>(
value.mAncestorSelectorHashes)) {
@ -556,9 +580,18 @@ ContentEnumFunc(const RuleValue& value,
nodeContext,
data->mTreeMatchContext,
selectorFlags)) {
Element* treeMatchStart = data->mElement;
if (SelectorContainsPseudoClass(selector, CSSPseudoClassType::part)) {
nsIContent* partHost = data->mElement->GetContainingShadowHost();
if (!partHost || !partHost->IsElement()) {
return;
}
treeMatchStart = partHost->AsElement();
}
nsCSSSelector* next = selector->mNext;
if (!next || nsCSSRuleUtils::SelectorMatchesTree(
data->mElement,
treeMatchStart,
next,
data->mTreeMatchContext,
nodeContext.mIsRelevantLink ? SelectorMatchesTreeFlags(0)

View file

@ -335,6 +335,7 @@ nsCSSSelector::Clone(bool aDeepNext, bool aDeepNegations) const
result->mCasedTag = mCasedTag;
result->mOperator = mOperator;
result->mPseudoType = mPseudoType;
result->mHybridPseudoType = mHybridPseudoType;
NS_IF_CLONE(mIDList);
NS_IF_CLONE(mClassList);

View file

@ -6690,6 +6690,8 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
if (hybridPseudoElementType == CSSPseudoElementType::slotted) {
pseudoClassType = CSSPseudoClassType::slotted;
aFlags |= SelectorParsingFlags::eDisallowCombinators;
} else if (hybridPseudoElementType == CSSPseudoElementType::part) {
pseudoClassType = CSSPseudoClassType::part;
}
}
@ -6842,6 +6844,28 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
UngetToken();
return eSelectorParsingStatus_Error;
}
else if (hybridPseudoElementType != CSSPseudoElementType::NotPseudo) {
aSelector.SetHybridPseudoType(hybridPseudoElementType);
// Ensure hybrid pseudo-elements are rejected if they're not allowed.
if (disallowPseudoElements) {
UngetToken();
return eSelectorParsingStatus_Error;
}
if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
parsingStatus =
ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
}
else if (nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType)) {
parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
pseudoClassType,
flags);
}
else {
MOZ_ASSERT(false, "unexpected hybrid pseudo-element");
parsingStatus = eSelectorParsingStatus_Error;
}
}
else if (nsCSSPseudoClasses::HasStringArg(pseudoClassType)) {
parsingStatus =
ParsePseudoClassWithIdentArg(aSelector, pseudoClassType);
@ -6853,14 +6877,6 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
else {
MOZ_ASSERT(nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType),
"unexpected pseudo with function token");
if (hybridPseudoElementType != CSSPseudoElementType::NotPseudo) {
aSelector.SetHybridPseudoType(hybridPseudoElementType);
// Ensure hybrid pseudo-elements are rejected if they're not allowed.
if (disallowPseudoElements) {
UngetToken();
return eSelectorParsingStatus_Error;
}
}
parsingStatus = ParsePseudoClassWithSelectorListArg(aSelector,
pseudoClassType,
flags);

View file

@ -94,6 +94,9 @@ CSS_PSEUDO_CLASS(nthLastOfType, ":nth-last-of-type", 0, "")
// Match slot nodes.
CSS_PSEUDO_CLASS(slotted, ":slotted", 0, "layout.css.slotted-pseudo.enabled")
// Match elements exposed through a shadow host's part attribute.
CSS_PSEUDO_CLASS(part, ":part", 0, "dom.webcomponents.enabled")
// Match nodes that are HTML but not XHTML
CSS_PSEUDO_CLASS(mozIsHTML, ":-moz-is-html", 0, "")

View file

@ -125,7 +125,8 @@ nsCSSPseudoClasses::HasStringArg(Type aType)
aType == Type::mozSystemMetric ||
aType == Type::mozLocaleDir ||
aType == Type::mozDir ||
aType == Type::dir;
aType == Type::dir ||
aType == Type::part;
}
bool
@ -210,5 +211,6 @@ nsCSSPseudoClasses::IsUserActionPseudoClass(Type aType)
/* static */ bool
nsCSSPseudoClasses::IsHybridPseudoElement(Type aType)
{
return aType == Type::slotted;
return aType == Type::slotted ||
aType == Type::part;
}

View file

@ -28,10 +28,13 @@
CSS_PSEUDO_ELEMENT(after, ":after", CSS_PSEUDO_ELEMENT_IS_CSS2)
CSS_PSEUDO_ELEMENT(before, ":before", CSS_PSEUDO_ELEMENT_IS_CSS2)
// XXX: ::slotted() is treated as if it were a pseudo-class, and
// is never parsed as a pseudo-element.
// XXX: ::slotted() and ::part() are treated as if they were pseudo-classes,
// and are never parsed as pseudo-elements.
CSS_PSEUDO_ELEMENT(slotted, ":slotted",
CSS_PSEUDO_ELEMENT_SUPPORTS_TREE_ABIDING)
CSS_PSEUDO_ELEMENT(part, ":part",
CSS_PSEUDO_ELEMENT_SUPPORTS_TREE_ABIDING |
CSS_PSEUDO_ELEMENT_SUPPORTS_USER_ACTION_STATE)
CSS_PSEUDO_ELEMENT(backdrop, ":backdrop", 0)

View file

@ -78,7 +78,8 @@ nsCSSPseudoElements::IsCSS2PseudoElement(nsIAtom *aAtom)
/* static */ bool
nsCSSPseudoElements::IsHybridPseudoElement(CSSPseudoElementType aType)
{
return aType == CSSPseudoElementType::slotted;
return aType == CSSPseudoElementType::slotted ||
aType == CSSPseudoElementType::part;
}
/* static */ bool

File diff suppressed because it is too large Load diff