Make the Selector's specificity part of Selector matching
authorbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 8 Nov 2014 02:05:36 +0000 (02:05 +0000)
committerbenjamin@webkit.org <benjamin@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 8 Nov 2014 02:05:36 +0000 (02:05 +0000)
https://bugs.webkit.org/show_bug.cgi?id=138486

Reviewed by Andreas Kling.

In CSS Selectors Level 4, the specificity of selectors is computed dynamically
based on which element is being matched.

For example, a selector:
    :matches(#foo, .bar, baz)
has a specificity of
    (1, 0, 0) on <baz id=foo class=bar>
    (0, 1, 0) on <baz class=bar>
    (0, 0, 1) on <baz>

Previously, the specificity of each selector was computed statically when populating
RuleSet. With more recent CSS, this gives us the wrong specificity because we do not know
how the selectors applies to the target.

This patch moves one tiny step in the direction of dynamic specificity. The specificity
is removed from RuleSet and is moved inside Selector Matching.

There is one bit worth keeping static: matching based on rule hash. This path is important
to avoid spending time compiling trivial selectors.
In order to keep rule hash matching working, the RuleData store which specificity class
the rule has in addition to the information about matching/not-matching. When going through
the fast path in ElementCollector, we compute the right specificity based on the type
of rule-hash matching.

* css/CSSSelector.cpp:
(WebCore::CSSSelector::specificityForOneSelector):
* css/CSSSelector.h:
* css/ElementRuleCollector.cpp:
(WebCore::ElementRuleCollector::addMatchedRule):
(WebCore::ElementRuleCollector::sortAndTransferMatchedRules):
(WebCore::ElementRuleCollector::ruleMatches):
(WebCore::ElementRuleCollector::collectMatchingRulesForList):
(WebCore::compareRules):
* css/ElementRuleCollector.h:
* css/RuleSet.cpp:
(WebCore::computeMatchBasedOnRuleHash):
(WebCore::RuleData::RuleData):
(WebCore::isSelectorMatchingHTMLBasedOnRuleHash): Deleted.
* css/RuleSet.h:
(WebCore::RuleData::matchBasedOnRuleHash):
(WebCore::RuleData::hasRightmostSelectorMatchingHTMLBasedOnRuleHash): Deleted.
(WebCore::RuleData::specificity): Deleted.
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::match):
* css/SelectorChecker.h:
* css/StyleResolver.h:
(WebCore::checkRegionSelector):
* cssjit/SelectorCompiler.cpp:
(WebCore::SelectorCompiler::SelectorCodeGenerator::SelectorCodeGenerator):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateSelectorChecker):
* cssjit/SelectorCompiler.h:
(WebCore::SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction):
(WebCore::SelectorCompiler::querySelectorSimpleSelectorCheckerFunction):
(WebCore::SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext):
(WebCore::SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext):
(WebCore::SelectorCompiler::simpleSelectorCheckerFunction): Deleted.
(WebCore::SelectorCompiler::selectorCheckerFunctionWithCheckingContext): Deleted.
* dom/SelectorQuery.cpp:
(WebCore::SelectorDataList::selectorMatches):
(WebCore::SelectorDataList::selectorClosest):
(WebCore::SelectorDataList::executeCompiledSimpleSelectorChecker):
(WebCore::SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext):
(WebCore::SelectorDataList::executeCompiledSingleMultiSelectorData):
(WebCore::SelectorDataList::execute):
* dom/SelectorQuery.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@175772 268f45cc-cd09-0410-ab3c-d52691b4dbfc

14 files changed:
Source/WebCore/ChangeLog
Source/WebCore/css/CSSSelector.cpp
Source/WebCore/css/CSSSelector.h
Source/WebCore/css/ElementRuleCollector.cpp
Source/WebCore/css/ElementRuleCollector.h
Source/WebCore/css/RuleSet.cpp
Source/WebCore/css/RuleSet.h
Source/WebCore/css/SelectorChecker.cpp
Source/WebCore/css/SelectorChecker.h
Source/WebCore/css/StyleResolver.h
Source/WebCore/cssjit/SelectorCompiler.cpp
Source/WebCore/cssjit/SelectorCompiler.h
Source/WebCore/dom/SelectorQuery.cpp
Source/WebCore/dom/SelectorQuery.h

index b8f0a54..7d0f055 100644 (file)
@@ -1,3 +1,76 @@
+2014-11-07  Benjamin Poulain  <benjamin@webkit.org>
+
+        Make the Selector's specificity part of Selector matching
+        https://bugs.webkit.org/show_bug.cgi?id=138486
+
+        Reviewed by Andreas Kling.
+
+        In CSS Selectors Level 4, the specificity of selectors is computed dynamically
+        based on which element is being matched.
+
+        For example, a selector:
+            :matches(#foo, .bar, baz)
+        has a specificity of
+            (1, 0, 0) on <baz id=foo class=bar>
+            (0, 1, 0) on <baz class=bar>
+            (0, 0, 1) on <baz>
+
+        Previously, the specificity of each selector was computed statically when populating
+        RuleSet. With more recent CSS, this gives us the wrong specificity because we do not know
+        how the selectors applies to the target.
+
+        This patch moves one tiny step in the direction of dynamic specificity. The specificity
+        is removed from RuleSet and is moved inside Selector Matching.
+
+        There is one bit worth keeping static: matching based on rule hash. This path is important
+        to avoid spending time compiling trivial selectors.
+        In order to keep rule hash matching working, the RuleData store which specificity class
+        the rule has in addition to the information about matching/not-matching. When going through
+        the fast path in ElementCollector, we compute the right specificity based on the type
+        of rule-hash matching.
+
+        * css/CSSSelector.cpp:
+        (WebCore::CSSSelector::specificityForOneSelector):
+        * css/CSSSelector.h:
+        * css/ElementRuleCollector.cpp:
+        (WebCore::ElementRuleCollector::addMatchedRule):
+        (WebCore::ElementRuleCollector::sortAndTransferMatchedRules):
+        (WebCore::ElementRuleCollector::ruleMatches):
+        (WebCore::ElementRuleCollector::collectMatchingRulesForList):
+        (WebCore::compareRules):
+        * css/ElementRuleCollector.h:
+        * css/RuleSet.cpp:
+        (WebCore::computeMatchBasedOnRuleHash):
+        (WebCore::RuleData::RuleData):
+        (WebCore::isSelectorMatchingHTMLBasedOnRuleHash): Deleted.
+        * css/RuleSet.h:
+        (WebCore::RuleData::matchBasedOnRuleHash):
+        (WebCore::RuleData::hasRightmostSelectorMatchingHTMLBasedOnRuleHash): Deleted.
+        (WebCore::RuleData::specificity): Deleted.
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::match):
+        * css/SelectorChecker.h:
+        * css/StyleResolver.h:
+        (WebCore::checkRegionSelector):
+        * cssjit/SelectorCompiler.cpp:
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::SelectorCodeGenerator):
+        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateSelectorChecker):
+        * cssjit/SelectorCompiler.h:
+        (WebCore::SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction):
+        (WebCore::SelectorCompiler::querySelectorSimpleSelectorCheckerFunction):
+        (WebCore::SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext):
+        (WebCore::SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext):
+        (WebCore::SelectorCompiler::simpleSelectorCheckerFunction): Deleted.
+        (WebCore::SelectorCompiler::selectorCheckerFunctionWithCheckingContext): Deleted.
+        * dom/SelectorQuery.cpp:
+        (WebCore::SelectorDataList::selectorMatches):
+        (WebCore::SelectorDataList::selectorClosest):
+        (WebCore::SelectorDataList::executeCompiledSimpleSelectorChecker):
+        (WebCore::SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext):
+        (WebCore::SelectorDataList::executeCompiledSingleMultiSelectorData):
+        (WebCore::SelectorDataList::execute):
+        * dom/SelectorQuery.h:
+
 2014-11-07  Alexey Proskuryakov  <ap@apple.com>
 
         CSP is enforced for eval in report-only mode on first page load
index 911502e..a257c7b 100644 (file)
@@ -94,7 +94,7 @@ inline unsigned CSSSelector::specificityForOneSelector() const
 {
     switch (match()) {
     case Id:
-        return 0x10000;
+        return static_cast<unsigned>(SelectorSpecificityIncrement::ClassA);
 
     case PagePseudoClass:
         break;
@@ -125,11 +125,11 @@ inline unsigned CSSSelector::specificityForOneSelector() const
     case Contain:
     case Begin:
     case End:
-        return 0x100;
+        return static_cast<unsigned>(SelectorSpecificityIncrement::ClassB);
     case Tag:
-        return (tagQName().localName() != starAtom) ? 1 : 0;
+        return (tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0;
     case PseudoElement:
-        return 1;
+        return static_cast<unsigned>(SelectorSpecificityIncrement::ClassC);
     case Unknown:
         return 0;
     }
index b2124e1..6130c73 100644 (file)
 namespace WebCore {
     class CSSSelectorList;
 
+    enum class SelectorSpecificityIncrement {
+        ClassA = 0x10000,
+        ClassB = 0x100,
+        ClassC = 1
+    };
+
     // this class represents a selector for a StyleRule
     class CSSSelector {
         WTF_MAKE_FAST_ALLOCATED;
index de73777..0081a27 100644 (file)
@@ -86,11 +86,11 @@ const Vector<RefPtr<StyleRule>>& ElementRuleCollector::matchedRuleList() const
     return m_matchedRuleList;
 }
 
-inline void ElementRuleCollector::addMatchedRule(const RuleData* rule)
+inline void ElementRuleCollector::addMatchedRule(const MatchedRule& matchedRule)
 {
     if (!m_matchedRules)
-        m_matchedRules = std::make_unique<Vector<const RuleData*, 32>>();
-    m_matchedRules->append(rule);
+        m_matchedRules = std::make_unique<Vector<MatchedRule, 32>>();
+    m_matchedRules->append(matchedRule);
 }
 
 void ElementRuleCollector::clearMatchedRules()
@@ -202,18 +202,18 @@ void ElementRuleCollector::sortAndTransferMatchedRules()
 
     sortMatchedRules();
 
-    Vector<const RuleData*, 32>& matchedRules = *m_matchedRules;
+    Vector<MatchedRule, 32>& matchedRules = *m_matchedRules;
     if (m_mode == SelectorChecker::Mode::CollectingRules) {
-        for (unsigned i = 0; i < matchedRules.size(); ++i)
-            m_matchedRuleList.append(matchedRules[i]->rule());
+        for (const MatchedRule& matchedRule : matchedRules)
+            m_matchedRuleList.append(matchedRule.ruleData->rule());
         return;
     }
 
     // Now transfer the set of matched rules over to our list of declarations.
-    for (unsigned i = 0; i < matchedRules.size(); i++) {
-        if (m_style && matchedRules[i]->containsUncommonAttributeSelector())
+    for (const MatchedRule& matchedRule : matchedRules) {
+        if (m_style && matchedRule.ruleData->containsUncommonAttributeSelector())
             m_style->setUnique();
-        m_result.addMatchedProperties(matchedRules[i]->rule()->properties(), matchedRules[i]->rule(), matchedRules[i]->linkMatchType(), matchedRules[i]->propertyWhitelistType(MatchingUARulesScope::isMatchingUARules()));
+        m_result.addMatchedProperties(matchedRule.ruleData->rule()->properties(), matchedRule.ruleData->rule(), matchedRule.ruleData->linkMatchType(), matchedRule.ruleData->propertyWhitelistType(MatchingUARulesScope::isMatchingUARules()));
     }
 }
 
@@ -274,12 +274,31 @@ void ElementRuleCollector::matchUARules(RuleSet* rules)
     sortAndTransferMatchedRules();
 }
 
-inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData)
+inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData, unsigned& specificity)
 {
     // We know a sufficiently simple single part selector matches simply because we found it from the rule hash when filtering the RuleSet.
     // This is limited to HTML only so we don't need to check the namespace (because of tag name match).
-    if (ruleData.hasRightmostSelectorMatchingHTMLBasedOnRuleHash() && m_element.isHTMLElement()) {
+    MatchBasedOnRuleHash matchBasedOnRuleHash = ruleData.matchBasedOnRuleHash();
+    if (matchBasedOnRuleHash != MatchBasedOnRuleHash::None && m_element.isHTMLElement()) {
         ASSERT_WITH_MESSAGE(m_pseudoStyleRequest.pseudoId == NOPSEUDO, "If we match based on the rule hash while collecting for a particular pseudo element ID, we would add incorrect rules for that pseudo element ID. We should never end in ruleMatches() with a pseudo element if the ruleData cannot match any pseudo element.");
+
+        switch (matchBasedOnRuleHash) {
+        case MatchBasedOnRuleHash::None:
+            ASSERT_NOT_REACHED();
+            break;
+        case MatchBasedOnRuleHash::Universal:
+            specificity = 0;
+            break;
+        case MatchBasedOnRuleHash::ClassA:
+            specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassA);
+            break;
+        case MatchBasedOnRuleHash::ClassB:
+            specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassB);
+            break;
+        case MatchBasedOnRuleHash::ClassC:
+            specificity = static_cast<unsigned>(SelectorSpecificityIncrement::ClassC);
+            break;
+        }
         return true;
     }
 
@@ -296,12 +315,15 @@ inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData)
     }
 
     if (compiledSelectorChecker && ruleData.compilationStatus() == SelectorCompilationStatus::SimpleSelectorChecker) {
-        SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, ruleData.compilationStatus());
-        ASSERT_WITH_MESSAGE(!selectorChecker(&m_element) || m_pseudoStyleRequest.pseudoId == NOPSEUDO, "When matching pseudo elements, we should never compile a selector checker without context unless it cannot match anything.");
+        SelectorCompiler::RuleCollectorSimpleSelectorChecker selectorChecker = SelectorCompiler::ruleCollectorSimpleSelectorCheckerFunction(compiledSelectorChecker, ruleData.compilationStatus());
+#if !ASSERT_MSG_DISABLED
+        unsigned ignoreSpecificity;
+        ASSERT_WITH_MESSAGE(!selectorChecker(&m_element, &ignoreSpecificity) || m_pseudoStyleRequest.pseudoId == NOPSEUDO, "When matching pseudo elements, we should never compile a selector checker without context unless it cannot match anything.");
+#endif
 #if CSS_SELECTOR_JIT_PROFILING
         ruleData.compiledSelectorUsed();
 #endif
-        return selectorChecker(&m_element);
+        return selectorChecker(&m_element, &specificity);
     }
 #endif // ENABLE(CSS_SELECTOR_JIT)
 
@@ -315,18 +337,18 @@ inline bool ElementRuleCollector::ruleMatches(const RuleData& ruleData)
     if (compiledSelectorChecker) {
         ASSERT(ruleData.compilationStatus() == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
 
-        SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, ruleData.compilationStatus());
+        SelectorCompiler::RuleCollectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::ruleCollectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, ruleData.compilationStatus());
 
 #if CSS_SELECTOR_JIT_PROFILING
         ruleData.compiledSelectorUsed();
 #endif
-        return selectorChecker(&m_element, &context);
+        return selectorChecker(&m_element, &context, &specificity);
     }
 #endif // ENABLE(CSS_SELECTOR_JIT)
 
     // Slow path.
     SelectorChecker selectorChecker(m_element.document());
-    return selectorChecker.match(ruleData.selector(), &m_element, context);
+    return selectorChecker.match(ruleData.selector(), &m_element, context, specificity);
 }
 
 void ElementRuleCollector::collectMatchingRulesForList(const Vector<RuleData>* rules, const MatchRequest& matchRequest, StyleResolver::RuleRange& ruleRange)
@@ -354,23 +376,24 @@ void ElementRuleCollector::collectMatchingRulesForList(const Vector<RuleData>* r
         if (m_sameOriginOnly && !ruleData.hasDocumentSecurityOrigin())
             continue;
 
-        if (ruleMatches(ruleData)) {
+        unsigned specificity;
+        if (ruleMatches(ruleData, specificity)) {
             // Update our first/last rule indices in the matched rules array.
             ++ruleRange.lastRuleIndex;
             if (ruleRange.firstRuleIndex == -1)
                 ruleRange.firstRuleIndex = ruleRange.lastRuleIndex;
 
             // Add this rule to our list of matched rules.
-            addMatchedRule(&ruleData);
+            addMatchedRule({&ruleData, specificity});
         }
     }
 }
 
-static inline bool compareRules(const RuleData* r1, const RuleData* r2)
+static inline bool compareRules(MatchedRule r1, MatchedRule r2)
 {
-    unsigned specificity1 = r1->specificity();
-    unsigned specificity2 = r2->specificity();
-    return (specificity1 == specificity2) ? r1->position() < r2->position() : specificity1 < specificity2;
+    unsigned specificity1 = r1.specificity;
+    unsigned specificity2 = r2.specificity;
+    return (specificity1 == specificity2) ? r1.ruleData->position() < r2.ruleData->position() : specificity1 < specificity2;
 }
 
 void ElementRuleCollector::sortMatchedRules()
index b6511d5..18a50cb 100644 (file)
@@ -38,6 +38,11 @@ class RuleData;
 class RuleSet;
 class SelectorFilter;
 
+struct MatchedRule {
+    const RuleData* ruleData;
+    unsigned specificity;
+};
+
 class ElementRuleCollector {
 public:
     ElementRuleCollector(Element& element, RenderStyle* style, const DocumentRuleSets& ruleSets, const SelectorFilter& selectorFilter)
@@ -81,12 +86,12 @@ private:
     void collectMatchingRules(const MatchRequest&, StyleResolver::RuleRange&);
     void collectMatchingRulesForRegion(const MatchRequest&, StyleResolver::RuleRange&);
     void collectMatchingRulesForList(const Vector<RuleData>*, const MatchRequest&, StyleResolver::RuleRange&);
-    bool ruleMatches(const RuleData&);
+    bool ruleMatches(const RuleData&, unsigned &specificity);
 
     void sortMatchedRules();
     void sortAndTransferMatchedRules();
 
-    void addMatchedRule(const RuleData*);
+    void addMatchedRule(const MatchedRule&);
 
     Element& m_element;
     RenderStyle* m_style;
@@ -100,7 +105,7 @@ private:
     SelectorChecker::Mode m_mode;
     bool m_canUseFastReject;
 
-    std::unique_ptr<Vector<const RuleData*, 32>> m_matchedRules;
+    std::unique_ptr<Vector<MatchedRule, 32>> m_matchedRules;
 
     // Output.
     Vector<RefPtr<StyleRule>> m_matchedRuleList;
index 698e0ca..1900aa1 100644 (file)
@@ -53,18 +53,28 @@ using namespace HTMLNames;
 
 // -----------------------------------------------------------------
 
-static inline bool isSelectorMatchingHTMLBasedOnRuleHash(const CSSSelector& selector)
+static inline MatchBasedOnRuleHash computeMatchBasedOnRuleHash(const CSSSelector& selector)
 {
     if (selector.tagHistory())
-        return false;
+        return MatchBasedOnRuleHash::None;
 
     if (selector.match() == CSSSelector::Tag) {
-        const AtomicString& selectorNamespace = selector.tagQName().namespaceURI();
-        return selectorNamespace == starAtom || selectorNamespace == xhtmlNamespaceURI;
+        const QualifiedName& tagQualifiedName = selector.tagQName();
+        const AtomicString& selectorNamespace = tagQualifiedName.namespaceURI();
+        if (selectorNamespace == starAtom || selectorNamespace == xhtmlNamespaceURI) {
+            if (tagQualifiedName == anyQName())
+                return MatchBasedOnRuleHash::Universal;
+            return MatchBasedOnRuleHash::ClassC;
+        }
+        return MatchBasedOnRuleHash::None;
     }
     if (SelectorChecker::isCommonPseudoClassSelector(&selector))
-        return true;
-    return selector.match() == CSSSelector::Id || selector.match() == CSSSelector::Class;
+        return MatchBasedOnRuleHash::ClassB;
+    if (selector.match() == CSSSelector::Id)
+        return MatchBasedOnRuleHash::ClassA;
+    if (selector.match() == CSSSelector::Class)
+        return MatchBasedOnRuleHash::ClassB;
+    return MatchBasedOnRuleHash::None;
 }
 
 static bool selectorCanMatchPseudoElement(const CSSSelector& rootSelector)
@@ -149,8 +159,7 @@ RuleData::RuleData(StyleRule* rule, unsigned selectorIndex, unsigned position, A
     , m_selectorIndex(selectorIndex)
     , m_hasDocumentSecurityOrigin(addRuleFlags & RuleHasDocumentSecurityOrigin)
     , m_position(position)
-    , m_specificity(selector()->specificity())
-    , m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash(isSelectorMatchingHTMLBasedOnRuleHash(*selector()))
+    , m_matchBasedOnRuleHash(static_cast<unsigned>(computeMatchBasedOnRuleHash(*selector())))
     , m_canMatchPseudoElement(selectorCanMatchPseudoElement(*selector()))
     , m_containsUncommonAttributeSelector(WebCore::containsUncommonAttributeSelector(selector()))
     , m_linkMatchType(SelectorChecker::determineLinkMatchType(selector()))
index a3dc40e..e7d8a0b 100644 (file)
@@ -54,6 +54,14 @@ class StyleResolver;
 class StyleRuleRegion;
 class StyleSheetContents;
 
+enum class MatchBasedOnRuleHash : unsigned {
+    None,
+    Universal,
+    ClassA,
+    ClassB,
+    ClassC
+};
+
 class RuleData {
 public:
     static const unsigned maximumSelectorComponentCount = 8192;
@@ -66,9 +74,8 @@ public:
     unsigned selectorIndex() const { return m_selectorIndex; }
 
     bool canMatchPseudoElement() const { return m_canMatchPseudoElement; }
-    bool hasRightmostSelectorMatchingHTMLBasedOnRuleHash() const { return m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash; }
+    MatchBasedOnRuleHash matchBasedOnRuleHash() const { return static_cast<MatchBasedOnRuleHash>(m_matchBasedOnRuleHash); }
     bool containsUncommonAttributeSelector() const { return m_containsUncommonAttributeSelector; }
-    unsigned specificity() const { return m_specificity; }
     unsigned linkMatchType() const { return m_linkMatchType; }
     bool hasDocumentSecurityOrigin() const { return m_hasDocumentSecurityOrigin; }
     PropertyWhitelistType propertyWhitelistType(bool isMatchingUARules = false) const { return isMatchingUARules ? PropertyWhitelistNone : static_cast<PropertyWhitelistType>(m_propertyWhitelistType); }
@@ -101,8 +108,7 @@ private:
     // This number was picked fairly arbitrarily. We can probably lower it if we need to.
     // Some simple testing showed <100,000 RuleData's on large sites.
     unsigned m_position : 18;
-    unsigned m_specificity : 24;
-    unsigned m_hasRightmostSelectorMatchingHTMLBasedOnRuleHash : 1;
+    unsigned m_matchBasedOnRuleHash : 3;
     unsigned m_canMatchPseudoElement : 1;
     unsigned m_containsUncommonAttributeSelector : 1;
     unsigned m_linkMatchType : 2; //  SelectorChecker::LinkMatchMask
index 41cf750..8dd2f33 100644 (file)
@@ -168,7 +168,7 @@ SelectorChecker::SelectorChecker(Document& document)
 {
 }
 
-bool SelectorChecker::match(const CSSSelector* selector, Element* element, const CheckingContext& providedContext) const
+bool SelectorChecker::match(const CSSSelector* selector, Element* element, const CheckingContext& providedContext, unsigned& specificity) const
 {
     CheckingContextWithStatus context(providedContext, selector, element);
     PseudoId pseudoId = NOPSEUDO;
@@ -176,6 +176,8 @@ bool SelectorChecker::match(const CSSSelector* selector, Element* element, const
         return false;
     if (context.pseudoId != NOPSEUDO && context.pseudoId != pseudoId)
         return false;
+
+    specificity = selector->specificity();
     if (context.pseudoId == NOPSEUDO && pseudoId != NOPSEUDO) {
         if (context.resolvingMode == Mode::ResolvingStyle && pseudoId < FIRST_INTERNAL_PSEUDOID)
             context.elementStyle->setHasPseudoStyle(pseudoId);
index 8077717..290bf17 100644 (file)
@@ -72,7 +72,7 @@ public:
 
     struct CheckingContextWithStatus;
 
-    bool match(const CSSSelector*, Element*, const CheckingContext&) const;
+    bool match(const CSSSelector*, Element*, const CheckingContext&, unsigned& specificity) const;
 
     static bool tagMatches(const Element*, const QualifiedName&);
     static bool isCommonPseudoClassSelector(const CSSSelector*);
index e3bf363..b2ca96b 100644 (file)
@@ -572,7 +572,8 @@ inline bool checkRegionSelector(const CSSSelector* regionSelector, Element* regi
     SelectorChecker selectorChecker(regionElement->document());
     for (const CSSSelector* s = regionSelector; s; s = CSSSelectorList::next(s)) {
         SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
-        if (selectorChecker.match(s, regionElement, selectorCheckingContext))
+        unsigned ignoredSpecificity;
+        if (selectorChecker.match(s, regionElement, selectorCheckingContext, ignoredSpecificity))
             return true;
     }
     return false;
index 1e33a97..017a167 100644 (file)
@@ -357,9 +357,7 @@ private:
     StackAllocator::StackReference m_lastVisitedElement;
     StackAllocator::StackReference m_startElement;
 
-#if CSS_SELECTOR_JIT_DEBUGGING
     const CSSSelector* m_originalSelector;
-#endif
 };
 
 const Assembler::RegisterID SelectorCodeGenerator::returnRegister = JSC::GPRInfo::returnValueGPR;
@@ -798,9 +796,7 @@ inline SelectorCodeGenerator::SelectorCodeGenerator(const CSSSelector* rootSelec
     , m_functionType(FunctionType::SimpleSelectorChecker)
     , m_visitedMode(VisitedMode::None)
     , m_descendantBacktrackingStartInUse(false)
-#if CSS_SELECTOR_JIT_DEBUGGING
     , m_originalSelector(rootSelector)
-#endif
 {
 #if CSS_SELECTOR_JIT_DEBUGGING
     dataLogF("Compiling \"%s\"\n", m_originalSelector->selectorText().utf8().data());
@@ -1609,6 +1605,14 @@ void SelectorCodeGenerator::generateSelectorChecker()
         generateRequestedPseudoElementEqualsToSelectorPseudoElement(failureOnFunctionEntry, m_selectorFragments.first(), checkingContextRegister);
     }
 
+    if (m_selectorContext == SelectorContext::RuleCollector) {
+        unsigned specificity = m_originalSelector->specificity();
+        if (m_functionType == FunctionType::SelectorCheckerWithCheckingContext)
+            m_assembler.store32(Assembler::TrustedImm32(specificity), JSC::GPRInfo::argumentGPR2);
+        else
+            m_assembler.store32(Assembler::TrustedImm32(specificity), JSC::GPRInfo::argumentGPR1);
+    }
+
     computeBacktrackingMemoryRequirements(m_selectorFragments);
     unsigned availableRegisterCount = m_registerAllocator.reserveCallerSavedRegisters(m_selectorFragments.registerRequirements);
 
index b107c43..2cb64a8 100644 (file)
@@ -77,22 +77,37 @@ enum class SelectorContext {
     QuerySelector
 };
 
-typedef unsigned (*SimpleSelectorChecker)(Element*);
-typedef unsigned (*SelectorCheckerWithCheckingContext)(Element*, const SelectorChecker::CheckingContext*);
+typedef unsigned (*RuleCollectorSimpleSelectorChecker)(Element*, unsigned*);
+typedef unsigned (*QuerySelectorSimpleSelectorChecker)(Element*);
+
+typedef unsigned (*RuleCollectorSelectorCheckerWithCheckingContext)(Element*, const SelectorChecker::CheckingContext*, unsigned*);
+typedef unsigned (*QuerySelectorSelectorCheckerWithCheckingContext)(Element*, const SelectorChecker::CheckingContext*);
+
 SelectorCompilationStatus compileSelector(const CSSSelector*, JSC::VM*, SelectorContext, JSC::MacroAssemblerCodeRef& outputCodeRef);
 
-inline SimpleSelectorChecker simpleSelectorCheckerFunction(void* executableAddress, SelectorCompilationStatus compilationStatus)
+inline RuleCollectorSimpleSelectorChecker ruleCollectorSimpleSelectorCheckerFunction(void* executableAddress, SelectorCompilationStatus compilationStatus)
 {
     ASSERT_UNUSED(compilationStatus, compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker);
-    return reinterpret_cast<SimpleSelectorChecker>(executableAddress);
+    return reinterpret_cast<RuleCollectorSimpleSelectorChecker>(executableAddress);
 }
 
-inline SelectorCheckerWithCheckingContext selectorCheckerFunctionWithCheckingContext(void* executableAddress, SelectorCompilationStatus compilationStatus)
+inline QuerySelectorSimpleSelectorChecker querySelectorSimpleSelectorCheckerFunction(void* executableAddress, SelectorCompilationStatus compilationStatus)
+{
+    ASSERT_UNUSED(compilationStatus, compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker);
+    return reinterpret_cast<QuerySelectorSimpleSelectorChecker>(executableAddress);
+}
+
+inline RuleCollectorSelectorCheckerWithCheckingContext ruleCollectorSelectorCheckerFunctionWithCheckingContext(void* executableAddress, SelectorCompilationStatus compilationStatus)
 {
     ASSERT_UNUSED(compilationStatus, compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
-    return reinterpret_cast<SelectorCheckerWithCheckingContext>(executableAddress);
+    return reinterpret_cast<RuleCollectorSelectorCheckerWithCheckingContext>(executableAddress);
 }
 
+inline QuerySelectorSelectorCheckerWithCheckingContext querySelectorSelectorCheckerFunctionWithCheckingContext(void* executableAddress, SelectorCompilationStatus compilationStatus)
+{
+    ASSERT_UNUSED(compilationStatus, compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
+    return reinterpret_cast<QuerySelectorSelectorCheckerWithCheckingContext>(executableAddress);
+}
 
 } // namespace SelectorCompiler
 } // namespace WebCore
index 4e9250f..fbbe4e7 100644 (file)
@@ -117,7 +117,8 @@ inline bool SelectorDataList::selectorMatches(const SelectorData& selectorData,
     SelectorChecker selectorChecker(element.document());
     SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
     selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
-    return selectorChecker.match(selectorData.selector, &element, selectorCheckingContext);
+    unsigned ignoredSpecificity;
+    return selectorChecker.match(selectorData.selector, &element, selectorCheckingContext, ignoredSpecificity);
 }
 
 inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorData, Element& element, const ContainerNode& rootNode) const
@@ -126,7 +127,8 @@ inline Element* SelectorDataList::selectorClosest(const SelectorData& selectorDa
     SelectorChecker::CheckingContext selectorCheckingContext(SelectorChecker::Mode::QueryingRules);
     selectorCheckingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
     Element* currentNode = &element;
-    if (!selectorChecker.match(selectorData.selector, currentNode, selectorCheckingContext))
+    unsigned ignoredSpecificity;
+    if (!selectorChecker.match(selectorData.selector, currentNode, selectorCheckingContext, ignoredSpecificity))
         return nullptr;
     return currentNode;
 }
@@ -381,7 +383,7 @@ ALWAYS_INLINE void SelectorDataList::executeSingleMultiSelectorData(const Contai
 
 #if ENABLE(CSS_SELECTOR_JIT)
 template <typename SelectorQueryTrait>
-ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::SimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
+ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
 {
     for (auto& element : elementDescendants(const_cast<ContainerNode&>(searchRootNode))) {
 #if CSS_SELECTOR_JIT_PROFILING
@@ -398,7 +400,7 @@ ALWAYS_INLINE void SelectorDataList::executeCompiledSimpleSelectorChecker(const
 }
 
 template <typename SelectorQueryTrait>
-ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
+ALWAYS_INLINE void SelectorDataList::executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker, typename SelectorQueryTrait::OutputType& output, const SelectorData& selectorData) const
 {
     SelectorChecker::CheckingContext checkingContext(SelectorChecker::Mode::QueryingRules);
     checkingContext.scope = rootNode.isDocumentNode() ? nullptr : &rootNode;
@@ -431,11 +433,11 @@ ALWAYS_INLINE void SelectorDataList::executeCompiledSingleMultiSelectorData(cons
             bool matched = false;
             void* compiledSelectorChecker = m_selectors[i].compiledSelectorCodeRef.code().executableAddress();
             if (m_selectors[i].compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
-                SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, m_selectors[i].compilationStatus);
+                SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, m_selectors[i].compilationStatus);
                 matched = selectorChecker(&element);
             } else {
                 ASSERT(m_selectors[i].compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
-                SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, m_selectors[i].compilationStatus);
+                SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, m_selectors[i].compilationStatus);
                 matched = selectorChecker(&element, &checkingContext);
             }
             if (matched) {
@@ -524,11 +526,11 @@ ALWAYS_INLINE void SelectorDataList::execute(ContainerNode& rootNode, typename S
         const SelectorData& selectorData = m_selectors.first();
         void* compiledSelectorChecker = selectorData.compiledSelectorCodeRef.code().executableAddress();
         if (selectorData.compilationStatus == SelectorCompilationStatus::SimpleSelectorChecker) {
-            SelectorCompiler::SimpleSelectorChecker selectorChecker = SelectorCompiler::simpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
+            SelectorCompiler::QuerySelectorSimpleSelectorChecker selectorChecker = SelectorCompiler::querySelectorSimpleSelectorCheckerFunction(compiledSelectorChecker, selectorData.compilationStatus);
             executeCompiledSimpleSelectorChecker<SelectorQueryTrait>(*searchRootNode, selectorChecker, output, selectorData);
         } else {
             ASSERT(selectorData.compilationStatus == SelectorCompilationStatus::SelectorCheckerWithCheckingContext);
-            SelectorCompiler::SelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::selectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus);
+            SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext selectorChecker = SelectorCompiler::querySelectorSelectorCheckerFunctionWithCheckingContext(compiledSelectorChecker, selectorData.compilationStatus);
             executeCompiledSelectorCheckerWithCheckingContext<SelectorQueryTrait>(rootNode, *searchRootNode, selectorChecker, output, selectorData);
         }
         break;
index 61f077a..792b235 100644 (file)
@@ -89,8 +89,8 @@ private:
     template <typename SelectorQueryTrait> void executeSingleSelectorData(const ContainerNode& rootNode, const SelectorData&, typename SelectorQueryTrait::OutputType&) const;
     template <typename SelectorQueryTrait> void executeSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType&) const;
 #if ENABLE(CSS_SELECTOR_JIT)
-    template <typename SelectorQueryTrait> void executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::SimpleSelectorChecker, typename SelectorQueryTrait::OutputType&, const SelectorData&) const;
-    template <typename SelectorQueryTrait> void executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::SelectorCheckerWithCheckingContext, typename SelectorQueryTrait::OutputType&, const SelectorData&) const;
+    template <typename SelectorQueryTrait> void executeCompiledSimpleSelectorChecker(const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSimpleSelectorChecker, typename SelectorQueryTrait::OutputType&, const SelectorData&) const;
+    template <typename SelectorQueryTrait> void executeCompiledSelectorCheckerWithCheckingContext(const ContainerNode& rootNode, const ContainerNode& searchRootNode, SelectorCompiler::QuerySelectorSelectorCheckerWithCheckingContext, typename SelectorQueryTrait::OutputType&, const SelectorData&) const;
     template <typename SelectorQueryTrait> void executeCompiledSingleMultiSelectorData(const ContainerNode& rootNode, typename SelectorQueryTrait::OutputType&) const;
     static bool compileSelector(const SelectorData&, const ContainerNode& rootNode);
 #endif // ENABLE(CSS_SELECTOR_JIT)