mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-04 23:08:39 +09:00
Issue #1592 - Part 4: Walk ::slotted()-containing rules for slottables
- Check against all selector parts and not the leftmost selector only for ::slotted() - Walk rules for ::slotted() regardless if the shadow root is opened/closed - Ensure that ::slotted() rules are walked in the right order - Fix ::slotted inheritance from topmost shadow root
This commit is contained in:
parent
92b31dd255
commit
518c41fd7a
3 changed files with 104 additions and 47 deletions
|
|
@ -681,6 +681,45 @@ nsBindingManager::WalkRules(nsIStyleRuleProcessor::EnumFunc aFunc,
|
|||
aData->mElementIsFeatureless = false;
|
||||
aData->mTreeMatchContext.mOnlyMatchHostPseudo = false;
|
||||
|
||||
// Walk the rules in shadow root for ::slotted() pseudo-element rules
|
||||
// if we have an assigned slot.
|
||||
if (aData->mElement->GetAssignedSlot()) {
|
||||
aData->mTreeMatchContext.mRestrictToSlottedPseudo = true;
|
||||
|
||||
AutoTArray<nsXBLBinding*, 2> stack;
|
||||
bool foundTopmostScope = false;
|
||||
for (nsIContent* parent = aData->mElement->GetFlattenedTreeParent();
|
||||
parent;
|
||||
parent = parent->GetFlattenedTreeParent()) {
|
||||
ShadowRoot* currentShadow = parent->GetShadowRoot();
|
||||
if (!currentShadow) {
|
||||
continue;
|
||||
}
|
||||
|
||||
nsXBLBinding* binding = currentShadow->GetAssociatedBinding();
|
||||
if (!binding) {
|
||||
continue;
|
||||
}
|
||||
stack.AppendElement(binding);
|
||||
|
||||
if (!foundTopmostScope) {
|
||||
aData->mTreeMatchContext.mScopedRoot = parent;
|
||||
foundTopmostScope = true;
|
||||
}
|
||||
}
|
||||
|
||||
while (!stack.IsEmpty()) {
|
||||
uint32_t index = stack.Length() - 1;
|
||||
nsXBLBinding* binding = stack.ElementAt(index);
|
||||
stack.RemoveElementAt(index);
|
||||
|
||||
aData->mTreeMatchContext.mIsTopmostScope = (index == 0);
|
||||
binding->WalkRules(aFunc, aData);
|
||||
}
|
||||
|
||||
aData->mTreeMatchContext.mRestrictToSlottedPseudo = false;
|
||||
}
|
||||
|
||||
// Walk the binding scope chain, starting with the binding attached to our
|
||||
// content, up till we run out of scopes or we get cut off.
|
||||
nsIContent *content = aData->mElement;
|
||||
|
|
|
|||
|
|
@ -2686,6 +2686,12 @@ SelectorMatchesTree(Element* aPrevElement,
|
|||
aTreeMatchContext.mCurrentStyleScope = styleScope;
|
||||
}
|
||||
selector = selector->mNext;
|
||||
if (!selector &&
|
||||
!aTreeMatchContext.mIsTopmostScope &&
|
||||
aTreeMatchContext.mRestrictToSlottedPseudo &&
|
||||
aTreeMatchContext.mScopedRoot != element) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
// for adjacent sibling and child combinators, if we didn't find
|
||||
|
|
@ -2764,6 +2770,52 @@ static bool SelectorListMatches(Element* aElement,
|
|||
aPreventComplexSelectors);
|
||||
}
|
||||
|
||||
static
|
||||
inline bool LookForTargetPseudo(nsCSSSelector* aSelector,
|
||||
TreeMatchContext* aMatchContext,
|
||||
nsRestyleHint* possibleChange) {
|
||||
if (aMatchContext->mOnlyMatchHostPseudo) {
|
||||
while (aSelector && aSelector->mNext != nullptr) {
|
||||
aSelector = aSelector->mNext;
|
||||
}
|
||||
|
||||
for (nsPseudoClassList* pseudoClass = aSelector->mPseudoClassList;
|
||||
pseudoClass;
|
||||
pseudoClass = pseudoClass->mNext) {
|
||||
if (pseudoClass->mType == CSSPseudoClassType::host ||
|
||||
pseudoClass->mType == CSSPseudoClassType::hostContext) {
|
||||
if (possibleChange) {
|
||||
// :host-context will walk ancestors looking for a match of a
|
||||
// compound selector, thus any changes to ancestors may require
|
||||
// restyling the subtree.
|
||||
*possibleChange |= eRestyle_Subtree;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
else if (aMatchContext->mRestrictToSlottedPseudo) {
|
||||
for (nsCSSSelector* selector = aSelector;
|
||||
selector;
|
||||
selector = selector->mNext) {
|
||||
if (!selector->mPseudoClassList) {
|
||||
continue;
|
||||
}
|
||||
for (nsPseudoClassList* pseudoClass = selector->mPseudoClassList;
|
||||
pseudoClass;
|
||||
pseudoClass = pseudoClass->mNext) {
|
||||
if (pseudoClass->mType == CSSPseudoClassType::slotted) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// We're not restricted to a specific pseudo-class.
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline
|
||||
void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
|
||||
ElementDependentRuleProcessorData* data, NodeMatchContext& nodeContext,
|
||||
|
|
@ -2778,29 +2830,11 @@ void ContentEnumFunc(const RuleValue& value, nsCSSSelector* aSelector,
|
|||
// We won't match; nothing else to do here
|
||||
return;
|
||||
}
|
||||
// If mOnlyMatchHostPseudo is set, then we only want to match against
|
||||
// selectors that contain a :host-context pseudo class.
|
||||
if (data->mTreeMatchContext.mOnlyMatchHostPseudo) {
|
||||
nsCSSSelector* selector = aSelector;
|
||||
while (selector && selector->mNext != nullptr) {
|
||||
selector = selector->mNext;
|
||||
}
|
||||
|
||||
bool seenHostPseudo = false;
|
||||
for (nsPseudoClassList* pseudoClass = selector->mPseudoClassList;
|
||||
pseudoClass;
|
||||
pseudoClass = pseudoClass->mNext) {
|
||||
if (pseudoClass->mType == CSSPseudoClassType::host ||
|
||||
pseudoClass->mType == CSSPseudoClassType::hostContext) {
|
||||
seenHostPseudo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!seenHostPseudo) {
|
||||
return;
|
||||
}
|
||||
if (!LookForTargetPseudo(aSelector, &data->mTreeMatchContext, nullptr)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
|
||||
data->mScope)) {
|
||||
// The selector is for a rule in a scoped style sheet, and the subject
|
||||
|
|
@ -3145,35 +3179,11 @@ AttributeEnumFunc(nsCSSSelector* aSelector,
|
|||
nsRestyleHint possibleChange =
|
||||
RestyleHintForSelectorWithAttributeChange(aData->change,
|
||||
aSelector, aRightmostSelector);
|
||||
// If mOnlyMatchHostPseudo is set, then we only want to match against
|
||||
// selectors that contain a :host-context pseudo class.
|
||||
if (data->mTreeMatchContext.mOnlyMatchHostPseudo) {
|
||||
nsCSSSelector* selector = aSelector;
|
||||
while (selector && selector->mNext != nullptr) {
|
||||
selector = selector->mNext;
|
||||
}
|
||||
|
||||
bool seenHostPseudo = false;
|
||||
for (nsPseudoClassList* pseudoClass = selector->mPseudoClassList;
|
||||
pseudoClass;
|
||||
pseudoClass = pseudoClass->mNext) {
|
||||
if (pseudoClass->mType == CSSPseudoClassType::host ||
|
||||
pseudoClass->mType == CSSPseudoClassType::hostContext) {
|
||||
// :host-context will walk ancestors looking for a match of a compound
|
||||
// selector, thus any changes to ancestors may require restyling the
|
||||
// subtree.
|
||||
possibleChange |= eRestyle_Subtree;
|
||||
seenHostPseudo = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!seenHostPseudo) {
|
||||
return;
|
||||
}
|
||||
if (!LookForTargetPseudo(aSelector, &data->mTreeMatchContext, &possibleChange)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
// If, ignoring eRestyle_SomeDescendants, enumData->change already includes
|
||||
// all the bits of possibleChange, don't bother calling SelectorMatches, since
|
||||
// even if it returns false enumData->change won't change. If possibleChange
|
||||
|
|
|
|||
|
|
@ -371,6 +371,9 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
|||
// match.
|
||||
bool mOnlyMatchHostPseudo;
|
||||
|
||||
// Restrict matching to selectors that contain a :slotted() pseudo-class.
|
||||
bool mRestrictToSlottedPseudo;
|
||||
|
||||
// Root of scoped stylesheet (set and unset by the supplier of the
|
||||
// scoped stylesheet).
|
||||
nsIContent* mScopedRoot;
|
||||
|
|
@ -403,6 +406,9 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
|||
// for an HTML5 scoped style sheet.
|
||||
bool mForScopedStyle;
|
||||
|
||||
// Whether we're currently in the topmost scope for shadow DOM.
|
||||
bool mIsTopmostScope;
|
||||
|
||||
enum MatchVisited {
|
||||
eNeverMatchVisited,
|
||||
eMatchVisitedDefault
|
||||
|
|
@ -429,6 +435,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
|||
, mVisitedHandling(aVisitedHandling)
|
||||
, mDocument(aDocument)
|
||||
, mOnlyMatchHostPseudo(false)
|
||||
, mRestrictToSlottedPseudo(false)
|
||||
, mScopedRoot(nullptr)
|
||||
, mIsHTMLDocument(aDocument->IsHTMLDocument())
|
||||
, mCompatMode(aDocument->GetCompatibilityMode())
|
||||
|
|
@ -436,6 +443,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
|
|||
, mSkippingParentDisplayBasedStyleFixup(false)
|
||||
, mForScopedStyle(false)
|
||||
, mCurrentStyleScope(nullptr)
|
||||
, mIsTopmostScope(false)
|
||||
{
|
||||
if (aMatchVisited != eNeverMatchVisited) {
|
||||
nsILoadContext* loadContext = mDocument->GetLoadContext();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue