Created
October 30, 2014 12:23
-
-
Save Constellation/10865ff0d169a9d7a172 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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