Skip to content

Instantly share code, notes, and snippets.

@Constellation
Created October 30, 2014 12:23
Show Gist options
  • Select an option

  • Save Constellation/10865ff0d169a9d7a172 to your computer and use it in GitHub Desktop.

Select an option

Save Constellation/10865ff0d169a9d7a172 to your computer and use it in GitHub Desktop.
diff --git a/Source/WebCore/css/SelectorChecker.cpp b/Source/WebCore/css/SelectorChecker.cpp
index c35dd6d..0c88eed 100644
--- a/Source/WebCore/css/SelectorChecker.cpp
+++ b/Source/WebCore/css/SelectorChecker.cpp
@@ -63,6 +63,12 @@ enum class VisitedMatchType : unsigned char {
Disabled, Enabled
};
+enum class FragmentsLevel {
+ Root,
+ InMatches,
+ InOtherFunctionalPseudoClass,
+};
+
struct SelectorChecker::CheckingContextWithStatus : public SelectorChecker::CheckingContext {
CheckingContextWithStatus(const SelectorChecker::CheckingContext& checkingContext, const CSSSelector* selector, Element* element)
: SelectorChecker::CheckingContext(checkingContext)
@@ -70,18 +76,20 @@ struct SelectorChecker::CheckingContextWithStatus : public SelectorChecker::Chec
, element(element)
, visitedMatchType(resolvingMode == SelectorChecker::Mode::QueryingRules ? VisitedMatchType::Disabled : VisitedMatchType::Enabled)
, firstSelectorOfTheFragment(selector)
- , inFunctionalPseudoClass(false)
+ , fragmentsLevel(FragmentsLevel::Root)
, hasScrollbarPseudo(false)
, hasSelectionPseudo(false)
+ , inRightmost(true)
{ }
const CSSSelector* selector;
Element* element;
VisitedMatchType visitedMatchType;
const CSSSelector* firstSelectorOfTheFragment;
- bool inFunctionalPseudoClass;
+ FragmentsLevel fragmentsLevel;
bool hasScrollbarPseudo;
bool hasSelectionPseudo;
+ bool inRightmost;
};
static inline bool isFirstChildElement(const Element* element)
@@ -171,36 +179,38 @@ static inline int countElementsOfTypeAfter(const Element* element, const Qualifi
bool SelectorChecker::match(const CSSSelector* selector, Element* element, const CheckingContext& providedContext) const
{
CheckingContextWithStatus context(providedContext, selector, element);
- PseudoId pseudoId = NOPSEUDO;
- if (matchRecursively(context, pseudoId) != SelectorMatches)
+ PseudoIdSet pseudoIdSet = NOPSEUDO;
+ MatchResult result = matchRecursively(context, pseudoIdSet);
+ if (result.match != SelectorMatches)
return false;
- if (context.pseudoId != NOPSEUDO && context.pseudoId != pseudoId)
+ if (context.pseudoId != NOPSEUDO && !(pseudoIdSet & (1 << context.pseudoId)))
return false;
- if (context.pseudoId == NOPSEUDO && pseudoId != NOPSEUDO) {
- if (context.resolvingMode == Mode::ResolvingStyle && pseudoId < FIRST_INTERNAL_PSEUDOID)
- context.elementStyle->setHasPseudoStyle(pseudoId);
+ if (context.pseudoId == NOPSEUDO && pseudoIdSet != NOPSEUDO) {
+ if (context.resolvingMode == Mode::ResolvingStyle && pseudoIdSet & PUBLIC_PSEUDOID_MASK)
+ context.elementStyle->setHasPseudoStyles(pseudoIdSet);
// When ignoring virtual pseudo elements, the context's pseudo should also be NOPSEUDO but that does
// not cause a failure.
- return context.resolvingMode == Mode::CollectingRulesIgnoringVirtualPseudoElements;
+ return context.resolvingMode == Mode::CollectingRulesIgnoringVirtualPseudoElements || result.matchToNonPseudoElement == MatchToNonPseudoElement::Matches;
}
return true;
}
-inline static bool hasScrollbarPseudoElement(PseudoId& dynamicPseudo)
+inline static bool hasScrollbarPseudoElement(const PseudoIdSet& dynamicPseudoIdSet)
{
- if (dynamicPseudo == SCROLLBAR
- || dynamicPseudo == SCROLLBAR_THUMB
- || dynamicPseudo == SCROLLBAR_BUTTON
- || dynamicPseudo == SCROLLBAR_TRACK
- || dynamicPseudo == SCROLLBAR_TRACK_PIECE
- || dynamicPseudo == SCROLLBAR_CORNER) {
+ if (dynamicPseudoIdSet &
+ ((1 << SCROLLBAR) |
+ (1 << SCROLLBAR_THUMB) |
+ (1 << SCROLLBAR_BUTTON) |
+ (1 << SCROLLBAR_TRACK) |
+ (1 << SCROLLBAR_TRACK_PIECE) |
+ (1 << SCROLLBAR_CORNER))) {
return true;
}
// RESIZER does not always have a scrollbar but it is a scrollbar-like pseudo element
// because it can have more than one pseudo element.
- return dynamicPseudo == RESIZER;
+ return dynamicPseudoIdSet & (1 << RESIZER);
}
static SelectorChecker::CheckingContextWithStatus checkingContextForParent(const SelectorChecker::CheckingContextWithStatus& context)
@@ -219,34 +229,38 @@ static SelectorChecker::CheckingContextWithStatus checkingContextForParent(const
// * SelectorFailsLocally - the selector fails for the element e
// * SelectorFailsAllSiblings - the selector fails for e and any sibling of e
// * SelectorFailsCompletely - the selector fails for e and any sibling or ancestor of e
-SelectorChecker::Match SelectorChecker::matchRecursively(const CheckingContextWithStatus& context, PseudoId& dynamicPseudo) const
+SelectorChecker::MatchResult SelectorChecker::matchRecursively(const CheckingContextWithStatus& context, PseudoIdSet& dynamicPseudoIdSet) const
{
+ MatchToNonPseudoElement matchToNonPseudoElement = MatchToNonPseudoElement::Matches;
+
// The first selector has to match.
- if (!checkOne(context))
- return SelectorFailsLocally;
+ if (!checkOne(context, dynamicPseudoIdSet, matchToNonPseudoElement))
+ return MatchResult::fails(SelectorFailsLocally);
if (context.selector->match() == CSSSelector::PseudoElement) {
// In Selectors Level 4, a pseudo element inside a functional pseudo class is undefined (issue 7).
- // Make it as matching failure until the spec clarifies this case.
- if (context.inFunctionalPseudoClass)
- return SelectorFailsCompletely;
+ // Make it as matching failure until the spec clarifies this case, except for :matches.
+ // In WebKit, we specially handles pseudo-elements in :matches.
+ if (context.fragmentsLevel != FragmentsLevel::Root && context.fragmentsLevel != FragmentsLevel::InMatches)
+ return MatchResult::fails(SelectorFailsCompletely);
if (context.selector->isCustomPseudoElement()) {
if (ShadowRoot* root = context.element->containingShadowRoot()) {
if (context.element->shadowPseudoId() != context.selector->value())
- return SelectorFailsLocally;
+ return MatchResult::fails(SelectorFailsLocally);
if (context.selector->pseudoElementType() == CSSSelector::PseudoElementWebKitCustom && root->type() != ShadowRoot::UserAgentShadowRoot)
- return SelectorFailsLocally;
+ return MatchResult::fails(SelectorFailsLocally);
} else
- return SelectorFailsLocally;
+ return MatchResult::fails(SelectorFailsLocally);
} else {
if ((!context.elementStyle && context.resolvingMode == Mode::ResolvingStyle) || context.resolvingMode == Mode::QueryingRules)
- return SelectorFailsLocally;
+ return MatchResult::fails(SelectorFailsLocally);
PseudoId pseudoId = CSSSelector::pseudoId(context.selector->pseudoElementType());
if (pseudoId != NOPSEUDO)
- dynamicPseudo = pseudoId;
+ dynamicPseudoIdSet |= (1 << pseudoId);
+ matchToNonPseudoElement = MatchToNonPseudoElement::Fails;
}
}
@@ -256,47 +270,50 @@ static SelectorChecker::CheckingContextWithStatus checkingContextForParent(const
// Prepare next selector
const CSSSelector* historySelector = context.selector->tagHistory();
if (!historySelector)
- return SelectorMatches;
+ return MatchResult::matches(matchToNonPseudoElement);
CheckingContextWithStatus nextContext(context);
nextContext.selector = historySelector;
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
if (relation != CSSSelector::SubSelector) {
// Bail-out if this selector is irrelevant for the pseudoId
- if (context.pseudoId != NOPSEUDO && context.pseudoId != dynamicPseudo)
- return SelectorFailsCompletely;
+ if (context.pseudoId != NOPSEUDO && !((1 << context.pseudoId) & dynamicPseudoIdSet))
+ return MatchResult::fails(SelectorFailsCompletely);
// Disable :visited matching when we try to match anything else than an ancestors.
if (relation != CSSSelector::Descendant && relation != CSSSelector::Child)
nextContext.visitedMatchType = VisitedMatchType::Disabled;
nextContext.pseudoId = NOPSEUDO;
+ nextContext.inRightmost = false;
}
switch (relation) {
case CSSSelector::Descendant:
nextContext = checkingContextForParent(nextContext);
nextContext.firstSelectorOfTheFragment = nextContext.selector;
- nextContext.elementStyle = 0;
+ nextContext.elementStyle = nullptr;
for (; nextContext.element; nextContext = checkingContextForParent(nextContext)) {
- Match match = this->matchRecursively(nextContext, ignoreDynamicPseudo);
- if (match == SelectorMatches || match == SelectorFailsCompletely)
- return match;
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
+ MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo);
+ if (result.match == SelectorMatches || result.match == SelectorFailsCompletely)
+ return MatchResult::update(result, matchToNonPseudoElement);
}
- return SelectorFailsCompletely;
+ return MatchResult::fails(SelectorFailsCompletely);
case CSSSelector::Child:
{
nextContext = checkingContextForParent(nextContext);
if (!nextContext.element)
- return SelectorFailsCompletely;
+ return MatchResult::fails(SelectorFailsCompletely);
nextContext.firstSelectorOfTheFragment = nextContext.selector;
nextContext.elementStyle = nullptr;
- Match match = matchRecursively(nextContext, ignoreDynamicPseudo);
- if (match == SelectorMatches || match == SelectorFailsCompletely)
- return match;
- return SelectorFailsAllSiblings;
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
+ MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo);
+ if (result.match == SelectorMatches || result.match == SelectorFailsCompletely) {
+ return MatchResult::update(result, matchToNonPseudoElement);
+ }
+ return MatchResult::fails(SelectorFailsAllSiblings);
}
case CSSSelector::DirectAdjacent:
@@ -306,56 +323,61 @@ static SelectorChecker::CheckingContextWithStatus checkingContextForParent(const
Element* previousElement = context.element->previousElementSibling();
if (!previousElement)
- return SelectorFailsAllSiblings;
+ return MatchResult::fails(SelectorFailsAllSiblings);
if (context.resolvingMode == Mode::ResolvingStyle)
previousElement->setAffectsNextSiblingElementStyle();
nextContext.element = previousElement;
nextContext.firstSelectorOfTheFragment = nextContext.selector;
- nextContext.elementStyle = 0;
- return matchRecursively(nextContext, ignoreDynamicPseudo);
+ nextContext.elementStyle = nullptr;
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
+ return MatchResult::update(matchRecursively(nextContext, ignoreDynamicPseudo), matchToNonPseudoElement);
}
case CSSSelector::IndirectAdjacent:
if (context.resolvingMode == Mode::ResolvingStyle)
context.element->setStyleIsAffectedByPreviousSibling();
nextContext.element = context.element->previousElementSibling();
nextContext.firstSelectorOfTheFragment = nextContext.selector;
- nextContext.elementStyle = 0;
+ nextContext.elementStyle = nullptr;
for (; nextContext.element; nextContext.element = nextContext.element->previousElementSibling()) {
if (context.resolvingMode == Mode::ResolvingStyle)
context.element->setAffectsNextSiblingElementStyle();
- Match match = this->matchRecursively(nextContext, ignoreDynamicPseudo);
- if (match == SelectorMatches || match == SelectorFailsAllSiblings || match == SelectorFailsCompletely)
- return match;
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
+ MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo);
+ if (result.match == SelectorMatches || result.match == SelectorFailsAllSiblings || result.match == SelectorFailsCompletely)
+ return MatchResult::update(result, matchToNonPseudoElement);
};
- return SelectorFailsAllSiblings;
+ return MatchResult::fails(SelectorFailsAllSiblings);
case CSSSelector::SubSelector:
+ {
// a selector is invalid if something follows a pseudo-element
// We make an exception for scrollbar pseudo elements and allow a set of pseudo classes (but nothing else)
// to follow the pseudo elements.
- nextContext.hasScrollbarPseudo = hasScrollbarPseudoElement(dynamicPseudo);
- nextContext.hasSelectionPseudo = dynamicPseudo == SELECTION;
- if ((context.elementStyle || context.resolvingMode == Mode::CollectingRules) && dynamicPseudo != NOPSEUDO
+ nextContext.hasScrollbarPseudo = hasScrollbarPseudoElement(dynamicPseudoIdSet);
+ nextContext.hasSelectionPseudo = dynamicPseudoIdSet & (1 << SELECTION);
+ if ((context.elementStyle || context.resolvingMode == Mode::CollectingRules) && dynamicPseudoIdSet != NOPSEUDO
&& !nextContext.hasSelectionPseudo
&& !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
- return SelectorFailsCompletely;
- return matchRecursively(nextContext, dynamicPseudo);
+ return MatchResult::fails(SelectorFailsCompletely);
+ return MatchResult::update(matchRecursively(nextContext, dynamicPseudoIdSet), matchToNonPseudoElement);
+ }
case CSSSelector::ShadowDescendant:
{
Element* shadowHostNode = context.element->shadowHost();
if (!shadowHostNode)
- return SelectorFailsCompletely;
+ return MatchResult::fails(SelectorFailsCompletely);
nextContext.element = shadowHostNode;
nextContext.firstSelectorOfTheFragment = nextContext.selector;
- nextContext.elementStyle = 0;
- return matchRecursively(nextContext, ignoreDynamicPseudo);
+ nextContext.elementStyle = nullptr;
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
+ return MatchResult::update(matchRecursively(nextContext, ignoreDynamicPseudo), matchToNonPseudoElement);
}
}
ASSERT_NOT_REACHED();
- return SelectorFailsCompletely;
+ return MatchResult::fails(SelectorFailsCompletely);
}
static bool attributeValueMatches(const Attribute& attribute, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
@@ -448,7 +470,7 @@ static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::CheckingCon
// selector does not use a pseudo-class selector other than ':active' and ':hover'.
// selector does not use a pseudo-element selector.
// selector is not part of an argument to a functional pseudo-class or pseudo-element.
- if (context.inFunctionalPseudoClass)
+ if (context.fragmentsLevel != FragmentsLevel::Root)
return true;
for (const CSSSelector* selector = context.firstSelectorOfTheFragment; selector; selector = selector->tagHistory()) {
@@ -490,7 +512,7 @@ static bool canMatchHoverOrActiveInQuirksMode(const SelectorChecker::CheckingCon
return false;
}
-bool SelectorChecker::checkOne(const CheckingContextWithStatus& context) const
+bool SelectorChecker::checkOne(const CheckingContextWithStatus& context, PseudoIdSet& dynamicPseudoIdSet, MatchToNonPseudoElement& matchToNonPseudoElement) const
{
Element* const & element = context.element;
const CSSSelector* const & selector = context.selector;
@@ -529,13 +551,13 @@ bool SelectorChecker::checkOne(const CheckingContextWithStatus& context) const
for (const CSSSelector* subselector = selectorList->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
CheckingContextWithStatus subcontext(context);
- subcontext.inFunctionalPseudoClass = true;
+ subcontext.fragmentsLevel = FragmentsLevel::InOtherFunctionalPseudoClass;
subcontext.selector = subselector;
subcontext.firstSelectorOfTheFragment = selectorList->first();
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
#if ENABLE(CSS_SELECTORS_LEVEL4)
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
- if (matchRecursively(subcontext, ignoreDynamicPseudo) == SelectorMatches) {
+ if (matchRecursively(subcontext, ignoreDynamicPseudo).match == SelectorMatches) {
ASSERT(ignoreDynamicPseudo == NOPSEUDO);
return false;
}
@@ -549,11 +571,12 @@ bool SelectorChecker::checkOne(const CheckingContextWithStatus& context) const
if (subcontext.selector->pseudoClassType() == CSSSelector::PseudoClassVisited)
return true;
}
- if (!checkOne(subcontext))
+ if (!checkOne(subcontext, ignoreDynamicPseudo).first)
return true;
#endif
}
#if ENABLE(CSS_SELECTORS_LEVEL4)
+ // Since :not cannot contain pseudo elements, there's no effect on matchedToNonPseudoElement.
return true;
#endif
} else if (context.hasScrollbarPseudo) {
@@ -665,16 +688,34 @@ bool SelectorChecker::checkOne(const CheckingContextWithStatus& context) const
break;
#if ENABLE(CSS_SELECTORS_LEVEL4)
case CSSSelector::PseudoClassMatches:
- for (const CSSSelector* subselector = selector->selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
- CheckingContextWithStatus subcontext(context);
- subcontext.inFunctionalPseudoClass = true;
- subcontext.selector = subselector;
- subcontext.firstSelectorOfTheFragment = subselector;
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
- if (matchRecursively(subcontext, ignoreDynamicPseudo) == SelectorMatches)
- return true;
+ {
+ bool hasMatchedAnything = false;
+ matchToNonPseudoElement = MatchToNonPseudoElement::Fails;
+ for (const CSSSelector* subselector = selector->selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
+ CheckingContextWithStatus subcontext(context);
+ subcontext.fragmentsLevel = FragmentsLevel::InMatches;
+ subcontext.selector = subselector;
+ subcontext.firstSelectorOfTheFragment = subselector;
+ PseudoIdSet localDynamicPseudoIdSet = NOPSEUDO;
+ MatchResult result = matchRecursively(subcontext, localDynamicPseudoIdSet);
+ if (result.match == SelectorMatches) {
+ if (!subcontext.inRightmost) {
+ // When encountering pseudo-element in the non-rightmost fragment, consider as unmatched.
+ if (localDynamicPseudoIdSet != NOPSEUDO)
+ continue;
+ // When the current fragment is non-rightmost fragment, it's not necessary to check all selectors to collect pseudo element ids.
+ return true;
+ }
+
+ if (result.matchToNonPseudoElement == MatchToNonPseudoElement::Matches)
+ matchToNonPseudoElement = MatchToNonPseudoElement::Matches;
+
+ dynamicPseudoIdSet |= localDynamicPseudoIdSet;
+ hasMatchedAnything = true;
+ }
+ }
+ return hasMatchedAnything;
}
- return false;
case CSSSelector::PseudoClassPlaceholderShown:
if (is<HTMLTextFormControlElement>(*element)) {
@@ -769,11 +810,11 @@ bool SelectorChecker::checkOne(const CheckingContextWithStatus& context) const
case CSSSelector::PseudoClassAny:
{
CheckingContextWithStatus subContext(context);
- subContext.inFunctionalPseudoClass = true;
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
+ subContext.fragmentsLevel = FragmentsLevel::InOtherFunctionalPseudoClass;
for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
subContext.firstSelectorOfTheFragment = subContext.selector;
- if (matchRecursively(subContext, ignoreDynamicPseudo) == SelectorMatches)
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
+ if (matchRecursively(subContext, ignoreDynamicPseudo).match == SelectorMatches)
return true;
}
}
@@ -790,7 +831,7 @@ bool SelectorChecker::checkOne(const CheckingContextWithStatus& context) const
case CSSSelector::PseudoClassVisited:
// ...except if :visited matching is disabled for ancestor/sibling matching.
// Inside functional pseudo class except for :not, :visited never matches.
- if (context.inFunctionalPseudoClass)
+ if (context.fragmentsLevel != FragmentsLevel::Root)
return false;
return element->isLink() && context.visitedMatchType == VisitedMatchType::Enabled;
case CSSSelector::PseudoClassDrag:
@@ -918,12 +959,12 @@ bool SelectorChecker::checkOne(const CheckingContextWithStatus& context) const
if (selector->match() == CSSSelector::PseudoElement && selector->pseudoElementType() == CSSSelector::PseudoElementCue) {
CheckingContextWithStatus subContext(context);
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
const CSSSelector* const & selector = context.selector;
for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
subContext.firstSelectorOfTheFragment = subContext.selector;
- subContext.inFunctionalPseudoClass = true;
- if (matchRecursively(subContext, ignoreDynamicPseudo) == SelectorMatches)
+ subContext.fragmentsLevel = FragmentsLevel::InOtherFunctionalPseudoClass;
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
+ if (matchRecursively(subContext, ignoreDynamicPseudo).match == SelectorMatches)
return true;
}
return false;
@@ -939,10 +980,10 @@ bool SelectorChecker::matchSelectorList(const CheckingContextWithStatus& baseCon
CheckingContextWithStatus subcontext(baseContext);
subcontext.element = &element;
subcontext.selector = subselector;
- subcontext.inFunctionalPseudoClass = true;
+ subcontext.fragmentsLevel = FragmentsLevel::InOtherFunctionalPseudoClass;
subcontext.firstSelectorOfTheFragment = subselector;
- PseudoId ignoreDynamicPseudo = NOPSEUDO;
- if (matchRecursively(subcontext, ignoreDynamicPseudo) == SelectorMatches) {
+ PseudoIdSet ignoreDynamicPseudo = NOPSEUDO;
+ if (matchRecursively(subcontext, ignoreDynamicPseudo).match == SelectorMatches) {
ASSERT(ignoreDynamicPseudo == NOPSEUDO);
return true;
}
diff --git a/Source/WebCore/css/SelectorChecker.h b/Source/WebCore/css/SelectorChecker.h
index 8077717..4ccc8ab 100644
--- a/Source/WebCore/css/SelectorChecker.h
+++ b/Source/WebCore/css/SelectorChecker.h
@@ -45,6 +45,41 @@ class SelectorChecker {
WTF_MAKE_NONCOPYABLE(SelectorChecker);
enum Match { SelectorMatches, SelectorFailsLocally, SelectorFailsAllSiblings, SelectorFailsCompletely };
+ // Whether the succeeded matching result is effective on non-pseudo-elements.
+ enum class MatchToNonPseudoElement {
+ Matches,
+ Fails,
+ };
+
+ struct MatchResult {
+ Match match;
+ MatchToNonPseudoElement matchToNonPseudoElement;
+
+ static MatchResult matches(MatchToNonPseudoElement matchToNonPseudoElement)
+ {
+ return {
+ Match::SelectorMatches,
+ matchToNonPseudoElement
+ };
+
+ }
+
+ static MatchResult update(MatchResult result, MatchToNonPseudoElement matchToNonPseudoElement)
+ {
+ if (matchToNonPseudoElement == MatchToNonPseudoElement::Fails)
+ result.matchToNonPseudoElement = MatchToNonPseudoElement::Fails;
+ return result;
+ }
+
+ static MatchResult fails(Match match)
+ {
+ return {
+ match,
+ MatchToNonPseudoElement::Fails
+ };
+ }
+ };
+
public:
enum class Mode : unsigned char {
ResolvingStyle = 0, CollectingRules, CollectingRulesIgnoringVirtualPseudoElements, QueryingRules
@@ -83,8 +118,8 @@ class SelectorChecker {
static unsigned determineLinkMatchType(const CSSSelector*);
private:
- Match matchRecursively(const CheckingContextWithStatus&, PseudoId&) const;
- bool checkOne(const CheckingContextWithStatus&) const;
+ MatchResult matchRecursively(const CheckingContextWithStatus&, PseudoIdSet&) const;
+ bool checkOne(const CheckingContextWithStatus&, PseudoIdSet&, MatchToNonPseudoElement&) const;
bool matchSelectorList(const CheckingContextWithStatus&, Element&, const CSSSelectorList&) const;
bool checkScrollbarPseudoClass(const CheckingContextWithStatus&, const CSSSelector*) const;
diff --git a/Source/WebCore/rendering/style/RenderStyle.h b/Source/WebCore/rendering/style/RenderStyle.h
index 9863950..ff3f18a 100644
--- a/Source/WebCore/rendering/style/RenderStyle.h
+++ b/Source/WebCore/rendering/style/RenderStyle.h
@@ -234,6 +234,13 @@ class RenderStyle: public RefCounted<RenderStyle> {
ASSERT(pseudo < FIRST_INTERNAL_PSEUDOID);
m_flags |= oneBitMask << (pseudoBitsOffset - 1 + pseudo);
}
+ void setHasPseudoStyles(PseudoIdSet pseudo)
+ {
+ ASSERT(pseudo != NOPSEUDO);
+ ASSERT((pseudo & PUBLIC_PSEUDOID_MASK) == pseudo);
+ static_assert(pseudoBitsOffset >= 1, "(pseudoBitsOffset - 1) should be valid.");
+ m_flags |= (static_cast<uint64_t>(pseudo) << (pseudoBitsOffset - 1));
+ }
ETableLayout tableLayout() const { return static_cast<ETableLayout>(getValue(tableLayoutBitMask, tableLayoutOffset)); }
void setTableLayout(ETableLayout tableLayout) { updateValue(tableLayout, tableLayoutBitMask, tableLayoutOffset); }
@@ -561,6 +568,7 @@ class RenderStyle: public RefCounted<RenderStyle> {
bool hasAnyPublicPseudoStyles() const;
bool hasPseudoStyle(PseudoId pseudo) const;
void setHasPseudoStyle(PseudoId pseudo);
+ void setHasPseudoStyles(PseudoIdSet pseudo);
bool hasUniquePseudoStyle() const;
// attribute getter methods
@@ -2155,6 +2163,11 @@ inline void RenderStyle::setHasPseudoStyle(PseudoId pseudo)
noninherited_flags.setHasPseudoStyle(pseudo);
}
+inline void RenderStyle::setHasPseudoStyles(PseudoIdSet pseudo)
+{
+ noninherited_flags.setHasPseudoStyles(pseudo);
+}
+
} // namespace WebCore
#endif // RenderStyle_h
diff --git a/Source/WebCore/rendering/style/RenderStyleConstants.h b/Source/WebCore/rendering/style/RenderStyleConstants.h
index 2a4aef5..6884901 100644
--- a/Source/WebCore/rendering/style/RenderStyleConstants.h
+++ b/Source/WebCore/rendering/style/RenderStyleConstants.h
@@ -80,6 +80,8 @@ enum PseudoId : unsigned char {
PUBLIC_PSEUDOID_MASK = ((1 << FIRST_INTERNAL_PSEUDOID) - 1) & ~((1 << FIRST_PUBLIC_PSEUDOID) - 1)
};
+typedef unsigned PseudoIdSet;
+
enum ColumnFill { ColumnFillBalance, ColumnFillAuto };
enum ColumnSpan { ColumnSpanNone = 0, ColumnSpanAll };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment