mirror of
https://github.com/roytam1/basilisk55.git
synced 2026-05-26 15:02:46 +00:00
ported from UXP: Support CSS shadow parts (51767db3)
This commit is contained in:
@@ -1004,6 +1004,7 @@ GK_ATOM(parameter, "parameter")
|
||||
GK_ATOM(parent, "parent")
|
||||
GK_ATOM(parentfocused, "parentfocused")
|
||||
GK_ATOM(parsetype, "parsetype")
|
||||
GK_ATOM(part, "part")
|
||||
GK_ATOM(password, "password")
|
||||
GK_ATOM(pattern, "pattern")
|
||||
GK_ATOM(patternSeparator, "pattern-separator")
|
||||
|
||||
@@ -824,16 +824,37 @@ nsBindingManager::WalkAllRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||
}
|
||||
}
|
||||
|
||||
// The approach in WalkAllShadowRootHostRules seems reasonable on the surface and reminds me of what is done with mScopedRoot elsewhere. But something important is missing, either here or in the code that would normally use it.
|
||||
|
||||
void
|
||||
nsBindingManager::WalkAllShadowRootHostRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
||||
ElementDependentRuleProcessorData* aData)
|
||||
{
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = true;
|
||||
WalkAllRules(aFunc, aData, true);
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = false;
|
||||
}
|
||||
{
|
||||
if (!mBoundContentSet) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool oldOnlyMatchHostPseudo = aData->mTreeMatchContext.mOnlyMatchHostPseudo;
|
||||
nsIContent* oldScopedRoot = aData->mTreeMatchContext.mScopedRoot;
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = true;
|
||||
|
||||
for (auto iter = mBoundContentSet->Iter(); !iter.Done(); iter.Next()) {
|
||||
nsIContent* boundContent = iter.Get()->GetKey();
|
||||
ShadowRoot* shadowRoot = boundContent->GetShadowRoot();
|
||||
if (!shadowRoot) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsXBLBinding* binding = shadowRoot->GetAssociatedBinding();
|
||||
if (!binding) {
|
||||
continue;
|
||||
}
|
||||
|
||||
aData->mTreeMatchContext.mScopedRoot = boundContent;
|
||||
binding->WalkRules(aFunc, aData);
|
||||
}
|
||||
|
||||
aData->mTreeMatchContext.mScopedRoot = oldScopedRoot;
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = oldOnlyMatchHostPseudo;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsBindingManager::MediumFeaturesChanged(nsPresContext* aPresContext,
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -334,6 +334,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);
|
||||
|
||||
@@ -6993,6 +6993,8 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
|
||||
if (hybridPseudoElementType == CSSPseudoElementType::slotted) {
|
||||
pseudoClassType = CSSPseudoClassType::slotted;
|
||||
aFlags |= SelectorParsingFlags::eDisallowCombinators;
|
||||
} else if (hybridPseudoElementType == CSSPseudoElementType::part) {
|
||||
pseudoClassType = CSSPseudoClassType::part;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7145,6 +7147,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);
|
||||
@@ -7156,14 +7180,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);
|
||||
|
||||
@@ -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, "")
|
||||
|
||||
|
||||
@@ -126,7 +126,8 @@ nsCSSPseudoClasses::HasStringArg(Type aType)
|
||||
aType == Type::mozEmptyExceptChildrenWithLocalname ||
|
||||
aType == Type::mozSystemMetric ||
|
||||
aType == Type::mozLocaleDir ||
|
||||
aType == Type::dir;
|
||||
aType == Type::dir ||
|
||||
aType == Type::part;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -211,7 +212,8 @@ nsCSSPseudoClasses::IsUserActionPseudoClass(Type aType)
|
||||
/* static */ bool
|
||||
nsCSSPseudoClasses::IsHybridPseudoElement(Type aType)
|
||||
{
|
||||
return aType == Type::slotted;
|
||||
return aType == Type::slotted ||
|
||||
aType == Type::part;
|
||||
}
|
||||
|
||||
/* static */ Maybe<bool>
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "mozilla/dom/HTMLSlotElement.h"
|
||||
#include "mozilla/dom/ShadowRoot.h"
|
||||
#include "nsIMozBrowserFrame.h"
|
||||
#include "nsGkAtoms.h"
|
||||
#include "nsRuleWalker.h"
|
||||
#include "nsStyleUtil.h"
|
||||
#include "StyleRule.h"
|
||||
@@ -183,6 +184,36 @@ InitSystemMetrics()
|
||||
return true;
|
||||
}
|
||||
|
||||
static nsPseudoClassList*
|
||||
FindPseudoClass(const nsCSSSelector* aSelector, CSSPseudoClassType aType)
|
||||
{
|
||||
for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
|
||||
pseudoClass;
|
||||
pseudoClass = pseudoClass->mNext) {
|
||||
if (pseudoClass->mType == aType) {
|
||||
return pseudoClass;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool
|
||||
ElementMatchesPart(Element* aElement, const nsPseudoClassList* aPseudoClass)
|
||||
{
|
||||
MOZ_ASSERT(aPseudoClass->mType == CSSPseudoClassType::part);
|
||||
MOZ_ASSERT(aPseudoClass->u.mString);
|
||||
|
||||
nsAutoString partValue;
|
||||
if (!aElement->GetAttr(kNameSpaceID_None, nsGkAtoms::part, partValue)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const nsDefaultStringComparator comparator;
|
||||
return nsStyleUtil::ValueIncludes(
|
||||
partValue, nsDependentString(aPseudoClass->u.mString), comparator);
|
||||
}
|
||||
|
||||
/* static */ void
|
||||
nsCSSRuleUtils::FreeSystemMetrics()
|
||||
{
|
||||
@@ -635,7 +666,10 @@ nsCSSRuleUtils::SelectorMatches(Element* aElement,
|
||||
"is false since we don't know how to set it correctly in "
|
||||
"Has(Attribute|State)DependentStyle");
|
||||
|
||||
if (aNodeMatchContext.mIsFeatureless &&
|
||||
nsPseudoClassList* partPseudo =
|
||||
FindPseudoClass(aSelector, CSSPseudoClassType::part);
|
||||
|
||||
if (aNodeMatchContext.mIsFeatureless && !partPseudo &&
|
||||
!CanMatchFeaturelessElement(aSelector)) {
|
||||
return false;
|
||||
}
|
||||
@@ -651,6 +685,27 @@ nsCSSRuleUtils::SelectorMatches(Element* aElement,
|
||||
targetElement = slot->AsElement();
|
||||
}
|
||||
|
||||
if (partPseudo) {
|
||||
if (!ElementMatchesPart(aElement, partPseudo)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsIContent* partHostContent = aElement->GetContainingShadowHost();
|
||||
if (!partHostContent || !partHostContent->IsElement()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Element* partHost = partHostContent->AsElement();
|
||||
if (aTreeMatchContext.mScopedRoot) {
|
||||
ShadowRoot* scopedShadow = aTreeMatchContext.mScopedRoot->GetShadowRoot();
|
||||
if (!scopedShadow || partHost->GetContainingShadow() != scopedShadow) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
targetElement = partHost;
|
||||
}
|
||||
|
||||
// namespace/tag match
|
||||
// optimization : bail out early if we can
|
||||
if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
|
||||
@@ -910,6 +965,9 @@ nsCSSRuleUtils::SelectorMatches(Element* aElement,
|
||||
}
|
||||
} break;
|
||||
|
||||
case CSSPseudoClassType::part:
|
||||
break;
|
||||
|
||||
case CSSPseudoClassType::host: {
|
||||
ShadowRoot* shadow = aElement->GetShadowRoot();
|
||||
// In order to match :host, the element must be a shadow root host,
|
||||
@@ -922,15 +980,11 @@ nsCSSRuleUtils::SelectorMatches(Element* aElement,
|
||||
return false;
|
||||
}
|
||||
|
||||
// We're matching :host from inside the shadow root.
|
||||
if (!aTreeMatchContext.mOnlyMatchHostPseudo) {
|
||||
// Check if the element has the same shadow root.
|
||||
if (aTreeMatchContext.mScopedRoot) {
|
||||
if (shadow != aTreeMatchContext.mScopedRoot->GetShadowRoot()) {
|
||||
return false;
|
||||
}
|
||||
// Check if the element has the same shadow root.
|
||||
if (aTreeMatchContext.mScopedRoot) {
|
||||
if (shadow != aTreeMatchContext.mScopedRoot->GetShadowRoot()) {
|
||||
return false;
|
||||
}
|
||||
// We were called elsewhere.
|
||||
}
|
||||
|
||||
// Reject if the next selector is an explicit universal selector.
|
||||
|
||||
Reference in New Issue
Block a user