Issue #1593 - Part 1: Import William Chen's patches w/o selector implementation, fixed up.

This commit is contained in:
Jeremy Andrews 2021-11-04 03:47:57 -05:00 committed by roytam1
commit 7472bc02be
10 changed files with 273 additions and 32 deletions

View file

@ -148,7 +148,8 @@ nsPseudoClassList::nsPseudoClassList(CSSPseudoClassType aType,
: mType(aType),
mNext(nullptr)
{
NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType),
NS_ASSERTION(nsCSSPseudoClasses::HasSelectorListArg(aType) ||
nsCSSPseudoClasses::HasOptionalSelectorListArg(aType),
"unexpected pseudo-class");
NS_ASSERTION(aSelectorList, "selector list expected");
MOZ_COUNT_CTOR(nsPseudoClassList);
@ -313,6 +314,7 @@ nsCSSSelector::nsCSSSelector(void)
mPseudoClassList(nullptr),
mAttrList(nullptr),
mNegations(nullptr),
mExplicitUniversal(false),
mNext(nullptr),
mNameSpace(kNameSpaceID_Unknown),
mOperator(0),
@ -382,6 +384,17 @@ void nsCSSSelector::Reset(void)
mOperator = char16_t(0);
}
bool nsCSSSelector::HasFeatureSelectors()
{
return mExplicitUniversal || mLowercaseTag || mCasedTag ||
mIDList || mClassList || mAttrList;
}
void nsCSSSelector::SetHasExplicitUniversal()
{
mExplicitUniversal = true;
}
void nsCSSSelector::SetNameSpace(int32_t aNameSpace)
{
mNameSpace = aNameSpace;
@ -750,9 +763,9 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
// Universal selector: avoid writing the universal selector when we
// can avoid it, especially since we're required to avoid it for the
// inside of :not()
if (wroteNamespace ||
if (wroteNamespace || mExplicitUniversal ||
(!mIDList && !mClassList && !mPseudoClassList && !mAttrList &&
(aIsNegated || !mNegations))) {
aIsNegated)) {
aString.Append(char16_t('*'));
}
} else {
@ -760,9 +773,7 @@ nsCSSSelector::AppendToStringWithoutCombinatorsOrNegations
nsAutoString tag;
(isPseudoElement ? mLowercaseTag : mCasedTag)->ToString(tag);
if (isPseudoElement) {
if (!mNext) {
// Lone pseudo-element selector -- toss in a wildcard type selector
// XXXldb Why?
if (mExplicitUniversal) {
aString.Append(char16_t('*'));
}
// While our atoms use one colon, most pseudo-elements require two

View file

@ -151,6 +151,8 @@ public:
nsCSSSelector* Clone() const { return Clone(true, true); }
void Reset(void);
bool HasFeatureSelectors();
void SetHasExplicitUniversal();
void SetNameSpace(int32_t aNameSpace);
void SetTag(const nsString& aTag);
void AddID(const nsString& aID);
@ -231,9 +233,10 @@ public:
// the argument to functional pseudos
nsAttrSelector* mAttrList;
nsCSSSelector* mNegations;
nsCSSSelector* mNext;
bool mExplicitUniversal; // True if universal selector explicitly
nsCSSSelector* mNext; // appears in the selector
int32_t mNameSpace;
char16_t mOperator;
char mOperator;
private:
// The underlying type of CSSPseudoElementType is uint8_t and
// it packs well with mOperator. (char16_t + uint8_t is less than 32bits.)

View file

@ -5641,6 +5641,7 @@ CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
aSelector.SetTag(mToken.mIdent);
}
else if (mToken.IsSymbol('*')) { // universal selector
aSelector.SetHasExplicitUniversal();
aDataMask |= SEL_MASK_ELEM;
// don't set tag
}
@ -5652,6 +5653,7 @@ CSSParserImpl::ParseTypeOrUniversalSelector(int32_t& aDataMask,
}
else { // was universal element selector
SetDefaultNamespaceOnSelector(aSelector);
aSelector.SetHasExplicitUniversal();
aDataMask |= SEL_MASK_ELEM;
// don't set any tag in the selector
}
@ -6097,7 +6099,8 @@ CSSParserImpl::ParsePseudoSelector(int32_t& aDataMask,
CSSPseudoClassType::negation == pseudoClassType ||
nsCSSPseudoClasses::HasStringArg(pseudoClassType) ||
nsCSSPseudoClasses::HasNthPairArg(pseudoClassType) ||
nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType))) {
nsCSSPseudoClasses::HasSelectorListArg(pseudoClassType)) &&
!nsCSSPseudoClasses::HasOptionalSelectorListArg(pseudoClassType)) {
// There are no other function pseudos
REPORT_UNEXPECTED_TOKEN(PEPseudoSelNonFunc);
UngetToken();

View file

@ -48,6 +48,7 @@
#include "nsCSSRules.h"
#include "nsStyleSet.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/ShadowRoot.h"
#include "nsNthIndexCache.h"
#include "mozilla/ArrayUtils.h"
#include "mozilla/EventStates.h"
@ -1342,9 +1343,17 @@ struct NodeMatchContext {
// mForStyling is false, we have to assume we don't know.)
const bool mIsRelevantLink;
NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink)
// If the node should be considered featureless (as specified in
// selectors 4), then mIsFeature should be set to true to prevent
// matching unless the selector is a special pseudo class or pseudo
// element that matches featureless elements.
const bool mIsFeatureless;
NodeMatchContext(EventStates aStateMask, bool aIsRelevantLink,
bool aIsFeatureless = false)
: mStateMask(aStateMask)
, mIsRelevantLink(aIsRelevantLink)
, mIsFeatureless(aIsFeatureless)
{
}
};
@ -1631,6 +1640,11 @@ StateSelectorMatches(Element* aElement,
return true;
}
static bool AnySelectorInArgListMatches(Element* aElement,
nsPseudoClassList* aList,
NodeMatchContext& aNodeMatchContext,
TreeMatchContext& aTreeMatchContext);
static bool
StateSelectorMatches(Element* aElement,
nsCSSSelector* aSelector,
@ -1652,6 +1666,17 @@ StateSelectorMatches(Element* aElement,
return true;
}
// Returns whether aSelector can match featureless elements.
static bool CanMatchFeaturelessElement(nsCSSSelector* aSelector)
{
if (aSelector->HasFeatureSelectors()) {
return false;
}
return false;
}
// |aDependence| has two functions:
// * when non-null, it indicates that we're processing a negation,
// which is done only when SelectorMatches calls itself recursively
@ -1672,6 +1697,11 @@ static bool SelectorMatches(Element* aElement,
"is false since we don't know how to set it correctly in "
"Has(Attribute|State)DependentStyle");
if (aNodeMatchContext.mIsFeatureless &&
!CanMatchFeaturelessElement(aSelector)) {
return false;
}
// namespace/tag match
// optimization : bail out early if we can
if ((kNameSpaceID_Unknown != aSelector->mNameSpace &&
@ -1870,18 +1900,9 @@ static bool SelectorMatches(Element* aElement,
case CSSPseudoClassType::any:
{
nsCSSSelectorList *l;
for (l = pseudoClass->u.mSelectors; l; l = l->mNext) {
nsCSSSelector *s = l->mSelectors;
MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(),
"parser failed");
if (SelectorMatches(
aElement, s, aNodeMatchContext, aTreeMatchContext,
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
break;
}
}
if (!l) {
if (!AnySelectorInArgListMatches(aElement, pseudoClass,
aNodeMatchContext,
aTreeMatchContext)) {
return false;
}
}
@ -2263,6 +2284,26 @@ static bool SelectorMatches(Element* aElement,
return result;
}
static bool AnySelectorInArgListMatches(Element* aElement,
nsPseudoClassList* aList,
NodeMatchContext& aNodeMatchContext,
TreeMatchContext& aTreeMatchContext)
{
nsCSSSelectorList *l;
for (l = aList->u.mSelectors; l; l = l->mNext) {
nsCSSSelector *s = l->mSelectors;
MOZ_ASSERT(!s->mNext && !s->IsPseudoElement(),
"parser failed");
if (SelectorMatches(
aElement, s, aNodeMatchContext, aTreeMatchContext,
SelectorMatchesFlags::IS_PSEUDO_CLASS_ARGUMENT)) {
break;
}
}
return !!l;
}
#undef STATE_CHECK
#ifdef DEBUG
@ -2352,7 +2393,9 @@ SelectorMatchesTree(Element* aPrevElement,
MOZ_ASSERT(!aSelector || !aSelector->IsPseudoElement());
nsCSSSelector* selector = aSelector;
Element* prevElement = aPrevElement;
bool crossedShadowRootBoundary = false;
while (selector) { // check compound selectors
bool contentIsFeatureless = false;
NS_ASSERTION(!selector->mNext ||
selector->mNext->mOperator != char16_t(0),
"compound selector without combinator");
@ -2383,6 +2426,20 @@ SelectorMatchesTree(Element* aPrevElement,
// to test against is the parent
else {
nsIContent *content = prevElement->GetParent();
// In the shadow tree, the shadow host behaves as if it
// is a featureless parent of top-level elements of the shadow
// tree. Only cross shadow root boundary when the selector is the
// left most selector because ancestors of the host are not in
// the selector match list.
ShadowRoot* shadowRoot = content ?
ShadowRoot::FromNode(content) : nullptr;
if (shadowRoot && !selector->mNext && !crossedShadowRootBoundary) {
content = shadowRoot->GetHost();
crossedShadowRootBoundary = true;
contentIsFeatureless = true;
}
// GetParent could return a document fragment; we only want
// element parents.
if (content && content->IsElement()) {
@ -2434,7 +2491,8 @@ SelectorMatchesTree(Element* aPrevElement,
}
const bool isRelevantLink = (aFlags & eLookForRelevantLink) &&
nsCSSRuleProcessor::IsLink(element);
NodeMatchContext nodeContext(EventStates(), isRelevantLink);
NodeMatchContext nodeContext(EventStates(), isRelevantLink, contentIsFeatureless);
if (isRelevantLink) {
// If we find an ancestor of the matched node that is a link
// during the matching process, then it's the relevant link (see
@ -2504,6 +2562,20 @@ 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;
if (!seenHostPseudo) {
return;
}
}
if (!data->mTreeMatchContext.SetStyleScopeForSelectorMatching(data->mElement,
data->mScope)) {
// The selector is for a rule in a scoped style sheet, and the subject
@ -2563,7 +2635,8 @@ nsCSSRuleProcessor::RulesMatching(ElementRuleProcessorData *aData)
if (cascade) {
NodeMatchContext nodeContext(EventStates(),
nsCSSRuleProcessor::IsLink(aData->mElement));
nsCSSRuleProcessor::IsLink(aData->mElement),
aData->mElementIsFeatureless);
cascade->mRuleHash.EnumerateAllRules(aData->mElement, aData, nodeContext);
}
}
@ -2847,6 +2920,24 @@ 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;
break;
}
}
if (!seenHostPseudo) {
return;
}
}
// If, ignoring eRestyle_SomeDescendants, enumData->change already includes
// all the bits of possibleChange, don't bother calling SelectorMatches, since
@ -3318,7 +3409,7 @@ AddSelector(RuleCascadeData* aCascade,
}
}
// Recur through any :-moz-any selectors
// Recur through any :-moz-any or :host-context selectors
for (nsPseudoClassList* pseudoClass = negation->mPseudoClassList;
pseudoClass; pseudoClass = pseudoClass->mNext) {
if (pseudoClass->mType == CSSPseudoClassType::any) {
@ -3970,6 +4061,7 @@ TreeMatchContext::InitAncestors(Element *aElement)
for (uint32_t i = ancestors.Length(); i-- != 0; ) {
mAncestorFilter.PushAncestor(ancestors[i]);
PushStyleScope(ancestors[i]);
PushShadowHost(ancestors[i]);
}
}
}

View file

@ -164,6 +164,23 @@ struct MOZ_STACK_CLASS TreeMatchContext {
mStyleScopes.TruncateLength(mStyleScopes.Length() - 1);
}
}
void PushShadowHost(mozilla::dom::Element* aElement)
{
NS_PRECONDITION(aElement, "aElement must not be null");
if (aElement->GetShadowRoot()) {
mShadowHosts.AppendElement(aElement);
}
}
void PopShadowHost(mozilla::dom::Element* aElement)
{
NS_PRECONDITION(aElement, "aElement must not be null");
if (mShadowHosts.SafeLastElement(nullptr) == aElement) {
mShadowHosts.TruncateLength(mShadowHosts.Length() - 1);
}
}
bool PopStyleScopeForSelectorMatching(mozilla::dom::Element* aElement)
{
@ -233,6 +250,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
: mPushedAncestor(false)
, mPushedStyleScope(false)
, mPushedShadowHost(false)
, mTreeMatchContext(aTreeMatchContext)
, mElement(nullptr)
{
@ -245,8 +263,10 @@ struct MOZ_STACK_CLASS TreeMatchContext {
mElement = aElement;
mPushedAncestor = true;
mPushedStyleScope = true;
mPushedShadowHost = true;
mTreeMatchContext.mAncestorFilter.PushAncestor(aElement);
mTreeMatchContext.PushStyleScope(aElement);
mTreeMatchContext.PushShadowHost(aElement);
}
}
@ -278,11 +298,15 @@ struct MOZ_STACK_CLASS TreeMatchContext {
if (mPushedStyleScope) {
mTreeMatchContext.PopStyleScope(mElement);
}
if (mPushedShadowHost) {
mTreeMatchContext.PopShadowHost(mElement);
}
}
private:
bool mPushedAncestor;
bool mPushedStyleScope;
bool mPushedShadowHost;
TreeMatchContext& mTreeMatchContext;
mozilla::dom::Element* mElement;
MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
@ -342,6 +366,11 @@ struct MOZ_STACK_CLASS TreeMatchContext {
// The document we're working with.
nsIDocument* const mDocument;
// Only selectors that contain :host or :host-context pseudo class
// should be matched against elements. All other selectors should not
// match.
bool mOnlyMatchHostPseudo;
// Root of scoped stylesheet (set and unset by the supplier of the
// scoped stylesheet).
nsIContent* mScopedRoot;
@ -383,6 +412,9 @@ struct MOZ_STACK_CLASS TreeMatchContext {
// <style scoped> child).
AutoTArray<mozilla::dom::Element*, 1> mStyleScopes;
// List of ancestor elements that are a shadow root host.
AutoTArray<mozilla::dom::Element*, 1> mShadowHosts;
// The current style scope element for selector matching.
mozilla::dom::Element* mCurrentStyleScope;
@ -396,6 +428,7 @@ struct MOZ_STACK_CLASS TreeMatchContext {
, mHaveSpecifiedScope(false)
, mVisitedHandling(aVisitedHandling)
, mDocument(aDocument)
, mOnlyMatchHostPseudo(false)
, mScopedRoot(nullptr)
, mIsHTMLDocument(aDocument->IsHTMLDocument())
, mCompatMode(aDocument->GetCompatibilityMode())
@ -437,6 +470,7 @@ struct MOZ_STACK_CLASS ElementDependentRuleProcessorData :
: RuleProcessorData(aPresContext, aRuleWalker)
, mElement(aElement)
, mTreeMatchContext(aTreeMatchContext)
, mElementIsFeatureless(false)
{
NS_ASSERTION(aElement, "null element leaked into SelectorMatches");
NS_ASSERTION(aElement->OwnerDoc(), "Document-less node here?");
@ -446,6 +480,7 @@ struct MOZ_STACK_CLASS ElementDependentRuleProcessorData :
mozilla::dom::Element* const mElement; // weak ref, must not be null
TreeMatchContext& mTreeMatchContext;
bool mElementIsFeatureless;
};
struct MOZ_STACK_CLASS ElementRuleProcessorData :

View file

@ -1311,8 +1311,9 @@ nsStyleSet::WalkRuleProcessors(nsIStyleRuleProcessor::EnumFunc aFunc,
if (mBindingManager) {
// We can supply additional document-level sheets that should be walked.
if (aWalkAllXBLStylesheets) {
mBindingManager->WalkAllRules(aFunc, aData);
mBindingManager->WalkAllRules(aFunc, aData, false);
} else {
mBindingManager->WalkAllShadowRootHostRules(aFunc, aData);
mBindingManager->WalkRules(aFunc, aData, &cutOffInheritance);
}
}