Title: [176152] trunk/Source/WebCore
Revision
176152
Author
benja...@webkit.org
Date
2014-11-14 18:32:37 -0800 (Fri, 14 Nov 2014)

Log Message

Compute the selector specificity as we match simple selectors
https://bugs.webkit.org/show_bug.cgi?id=138718

Reviewed by Andreas Kling.

This is an other tiny step toward dynamic specificity. Instead of computing
the entire specificity at the end, compute it dynamically as we are matching
each individual simple selector.

* css/CSSSelector.cpp:
(WebCore::CSSSelector::specificity):
(WebCore::CSSSelector::simpleSelectorSpecificity):
(WebCore::CSSSelector::addSpecificities):
(WebCore::CSSSelector::specificityForPage):
(WebCore::CSSSelector::specificityForOneSelector): Deleted.
* css/CSSSelector.h:
* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::match):
(WebCore::SelectorChecker::matchRecursively):
(WebCore::SelectorChecker::checkOne):
(WebCore::SelectorChecker::matchSelectorList):
* css/SelectorChecker.h:

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (176151 => 176152)


--- trunk/Source/WebCore/ChangeLog	2014-11-15 02:31:28 UTC (rev 176151)
+++ trunk/Source/WebCore/ChangeLog	2014-11-15 02:32:37 UTC (rev 176152)
@@ -1,3 +1,28 @@
+2014-11-14  Benjamin Poulain  <benja...@webkit.org>
+
+        Compute the selector specificity as we match simple selectors
+        https://bugs.webkit.org/show_bug.cgi?id=138718
+
+        Reviewed by Andreas Kling.
+
+        This is an other tiny step toward dynamic specificity. Instead of computing
+        the entire specificity at the end, compute it dynamically as we are matching
+        each individual simple selector.
+
+        * css/CSSSelector.cpp:
+        (WebCore::CSSSelector::specificity):
+        (WebCore::CSSSelector::simpleSelectorSpecificity):
+        (WebCore::CSSSelector::addSpecificities):
+        (WebCore::CSSSelector::specificityForPage):
+        (WebCore::CSSSelector::specificityForOneSelector): Deleted.
+        * css/CSSSelector.h:
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::match):
+        (WebCore::SelectorChecker::matchRecursively):
+        (WebCore::SelectorChecker::checkOne):
+        (WebCore::SelectorChecker::matchSelectorList):
+        * css/SelectorChecker.h:
+
 2014-11-14  Andreas Kling  <akl...@apple.com>
 
         [mac] Only images that are actually purgeable should be advertised as such.

Modified: trunk/Source/WebCore/css/CSSSelector.cpp (176151 => 176152)


--- trunk/Source/WebCore/css/CSSSelector.cpp	2014-11-15 02:31:28 UTC (rev 176151)
+++ trunk/Source/WebCore/css/CSSSelector.cpp	2014-11-15 02:32:37 UTC (rev 176152)
@@ -64,34 +64,17 @@
     if (isForPage())
         return specificityForPage() & maxValueMask;
 
-    unsigned total = specificityForOneSelector();
+    unsigned total = simpleSelectorSpecificity();
 
-    for (const CSSSelector* selector = this->tagHistory(); selector; selector = selector->tagHistory()) {
-        unsigned selectorSpecificity = selector->specificityForOneSelector();
-
-        unsigned newIdValue = (selectorSpecificity & idMask);
-        if (((total & idMask) + newIdValue) & ~idMask)
-            total |= idMask;
-        else
-            total += newIdValue;
-
-        unsigned newClassValue = (selectorSpecificity & classMask);
-        if (((total & classMask) + newClassValue) & ~classMask)
-            total |= classMask;
-        else
-            total += newClassValue;
-
-        unsigned newElementValue = (selectorSpecificity & elementMask);
-        if (((total & elementMask) + newElementValue) & ~elementMask)
-            total |= elementMask;
-        else
-            total += newElementValue;
-    }
+    for (const CSSSelector* selector = this->tagHistory(); selector; selector = selector->tagHistory())
+        total = addSpecificities(total, selector->simpleSelectorSpecificity());
     return total;
 }
 
-inline unsigned CSSSelector::specificityForOneSelector() const
+unsigned CSSSelector::simpleSelectorSpecificity() const
 {
+    ASSERT_WITH_MESSAGE(!isForPage(), "At the time of this writing, page selectors are not treated as real selectors that are matched. The value computed here only account for real selectors.");
+
     switch (match()) {
     case Id:
         return static_cast<unsigned>(SelectorSpecificityIncrement::ClassA);
@@ -99,9 +82,6 @@
     case PagePseudoClass:
         break;
     case PseudoClass:
-        // FIXME: PseudoAny should base the specificity on the sub-selectors.
-        // See http://lists.w3.org/Archives/Public/www-style/2010Sep/0530.html
-
 #if ENABLE(CSS_SELECTORS_LEVEL4)
         if (pseudoClassType() == PseudoClassNot) {
             ASSERT_WITH_MESSAGE(selectorList() && selectorList()->first(), "The parser should never generate a valid selector for an empty :not().");
@@ -114,7 +94,7 @@
         FALLTHROUGH;
 #else
         if (pseudoClassType() == PseudoClassNot && selectorList())
-            return selectorList()->first()->specificityForOneSelector();
+            return selectorList()->first()->simpleSelectorSpecificity();
         FALLTHROUGH;
 #endif
     case Exact:
@@ -137,8 +117,35 @@
     return 0;
 }
 
+unsigned CSSSelector::addSpecificities(unsigned a, unsigned b)
+{
+    unsigned total = a;
+
+    unsigned newIdValue = (b & idMask);
+    if (((total & idMask) + newIdValue) & ~idMask)
+        total |= idMask;
+    else
+        total += newIdValue;
+
+    unsigned newClassValue = (b & classMask);
+    if (((total & classMask) + newClassValue) & ~classMask)
+        total |= classMask;
+    else
+        total += newClassValue;
+
+    unsigned newElementValue = (b & elementMask);
+    if (((total & elementMask) + newElementValue) & ~elementMask)
+        total |= elementMask;
+    else
+        total += newElementValue;
+
+    return total;
+}
+
 unsigned CSSSelector::specificityForPage() const
 {
+    ASSERT(isForPage());
+
     // See http://dev.w3.org/csswg/css3-page/#cascading-and-page-context
     unsigned s = 0;
 

Modified: trunk/Source/WebCore/css/CSSSelector.h (176151 => 176152)


--- trunk/Source/WebCore/css/CSSSelector.h	2014-11-15 02:31:28 UTC (rev 176151)
+++ trunk/Source/WebCore/css/CSSSelector.h	2014-11-15 02:32:37 UTC (rev 176152)
@@ -59,6 +59,8 @@
         static const unsigned elementMask = 0xff;
 
         unsigned specificity() const;
+        unsigned simpleSelectorSpecificity() const;
+        static unsigned addSpecificities(unsigned, unsigned);
 
         /* how the attribute value has to match.... Default is Exact */
         enum Match {
@@ -306,7 +308,7 @@
         unsigned m_isForPage             : 1;
         unsigned m_tagIsForNamespaceRule : 1;
 
-        unsigned specificityForOneSelector() const;
+        unsigned simpleSelectorSpecificityForPage() const;
         unsigned specificityForPage() const;
 
         // Hide.

Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (176151 => 176152)


--- trunk/Source/WebCore/css/SelectorChecker.cpp	2014-11-15 02:31:28 UTC (rev 176151)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp	2014-11-15 02:32:37 UTC (rev 176152)
@@ -173,15 +173,16 @@
 
 bool SelectorChecker::match(const CSSSelector* selector, Element* element, const CheckingContext& providedContext, unsigned& specificity) const
 {
+    specificity = 0;
+
     CheckingContextWithStatus context(providedContext, selector, element);
     PseudoIdSet pseudoIdSet;
-    MatchResult result = matchRecursively(context, pseudoIdSet);
+    MatchResult result = matchRecursively(context, pseudoIdSet, specificity);
     if (result.match != Match::SelectorMatches)
         return false;
     if (context.pseudoId != NOPSEUDO && !pseudoIdSet.has(context.pseudoId))
         return false;
 
-    specificity = selector->specificity();
     if (context.pseudoId == NOPSEUDO && pseudoIdSet) {
         PseudoIdSet publicPseudoIdSet = pseudoIdSet & PseudoIdSet::fromMask(PUBLIC_PSEUDOID_MASK);
         if (context.resolvingMode == Mode::ResolvingStyle && publicPseudoIdSet)
@@ -221,12 +222,12 @@
 // * 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::MatchResult SelectorChecker::matchRecursively(const CheckingContextWithStatus& context, PseudoIdSet& dynamicPseudoIdSet) const
+SelectorChecker::MatchResult SelectorChecker::matchRecursively(const CheckingContextWithStatus& context, PseudoIdSet& dynamicPseudoIdSet, unsigned& specificity) const
 {
     MatchType matchType = MatchType::Element;
 
     // The first selector has to match.
-    if (!checkOne(context, dynamicPseudoIdSet, matchType))
+    if (!checkOne(context, dynamicPseudoIdSet, matchType, specificity))
         return MatchResult::fails(Match::SelectorFailsLocally);
 
     if (context.selector->match() == CSSSelector::PseudoElement) {
@@ -289,8 +290,13 @@
         nextContext.elementStyle = nullptr;
         for (; nextContext.element; nextContext = checkingContextForParent(nextContext)) {
             PseudoIdSet ignoreDynamicPseudo;
-            MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo);
+            unsigned descendantsSpecificity = 0;
+            MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo, descendantsSpecificity);
             ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
+
+            if (result.match == Match::SelectorMatches)
+                specificity = CSSSelector::addSpecificities(specificity, descendantsSpecificity);
+
             if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsCompletely)
                 return MatchResult::updateWithMatchType(result, matchType);
         }
@@ -304,8 +310,13 @@
             nextContext.firstSelectorOfTheFragment = nextContext.selector;
             nextContext.elementStyle = nullptr;
             PseudoIdSet ignoreDynamicPseudo;
-            MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo);
+            unsigned childSpecificity = 0;
+            MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo, childSpecificity);
             ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
+
+            if (result.match == Match::SelectorMatches)
+                specificity = CSSSelector::addSpecificities(specificity, childSpecificity);
+
             if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsCompletely)
                 return MatchResult::updateWithMatchType(result, matchType);
             return MatchResult::fails(Match::SelectorFailsAllSiblings);
@@ -325,7 +336,14 @@
             nextContext.firstSelectorOfTheFragment = nextContext.selector;
             nextContext.elementStyle = nullptr;
             PseudoIdSet ignoreDynamicPseudo;
-            return MatchResult::updateWithMatchType(matchRecursively(nextContext, ignoreDynamicPseudo), matchType);
+            unsigned adjacentSpecificity = 0;
+            MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo, adjacentSpecificity);
+            ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
+
+            if (result.match == Match::SelectorMatches)
+                specificity = CSSSelector::addSpecificities(specificity, adjacentSpecificity);
+
+            return MatchResult::updateWithMatchType(result, matchType);
         }
     case CSSSelector::IndirectAdjacent:
         if (context.resolvingMode == Mode::ResolvingStyle)
@@ -338,25 +356,38 @@
                 context.element->setAffectsNextSiblingElementStyle();
 
             PseudoIdSet ignoreDynamicPseudo;
-            MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo);
+            unsigned indirectAdjacentSpecificity = 0;
+            MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo, indirectAdjacentSpecificity);
             ASSERT(!nextContext.pseudoElementEffective && !ignoreDynamicPseudo);
+
+            if (result.match == Match::SelectorMatches)
+                specificity = CSSSelector::addSpecificities(specificity, indirectAdjacentSpecificity);
+
             if (result.match == Match::SelectorMatches || result.match == Match::SelectorFailsAllSiblings || result.match == Match::SelectorFailsCompletely)
                 return MatchResult::updateWithMatchType(result, matchType);
         };
         return MatchResult::fails(Match::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(dynamicPseudoIdSet);
-        nextContext.hasSelectionPseudo = dynamicPseudoIdSet.has(SELECTION);
-        if ((context.elementStyle || context.resolvingMode == Mode::CollectingRules) && dynamicPseudoIdSet
-            && !nextContext.hasSelectionPseudo
-            && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
-            return MatchResult::fails(Match::SelectorFailsCompletely);
-        return MatchResult::updateWithMatchType(matchRecursively(nextContext, dynamicPseudoIdSet), matchType);
+        {
+            // 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(dynamicPseudoIdSet);
+            nextContext.hasSelectionPseudo = dynamicPseudoIdSet.has(SELECTION);
+            if ((context.elementStyle || context.resolvingMode == Mode::CollectingRules) && dynamicPseudoIdSet
+                && !nextContext.hasSelectionPseudo
+                && !(nextContext.hasScrollbarPseudo && nextContext.selector->match() == CSSSelector::PseudoClass))
+                return MatchResult::fails(Match::SelectorFailsCompletely);
 
+            unsigned subselectorSpecificity = 0;
+            MatchResult result = matchRecursively(nextContext, dynamicPseudoIdSet, subselectorSpecificity);
+
+            if (result.match == Match::SelectorMatches)
+                specificity = CSSSelector::addSpecificities(specificity, subselectorSpecificity);
+
+            return MatchResult::updateWithMatchType(result, matchType);
+        }
     case CSSSelector::ShadowDescendant:
         {
             Element* shadowHostNode = context.element->shadowHost();
@@ -366,7 +397,13 @@
             nextContext.firstSelectorOfTheFragment = nextContext.selector;
             nextContext.elementStyle = nullptr;
             PseudoIdSet ignoreDynamicPseudo;
-            return MatchResult::updateWithMatchType(matchRecursively(nextContext, ignoreDynamicPseudo), matchType);
+            unsigned shadowDescendantSpecificity = 0;
+            MatchResult result = matchRecursively(nextContext, ignoreDynamicPseudo, shadowDescendantSpecificity);
+
+            if (result.match == Match::SelectorMatches)
+                specificity = CSSSelector::addSpecificities(specificity, shadowDescendantSpecificity);
+
+            return MatchResult::updateWithMatchType(result, matchType);
         }
     }
 
@@ -506,13 +543,15 @@
     return false;
 }
 
-bool SelectorChecker::checkOne(const CheckingContextWithStatus& context, PseudoIdSet& dynamicPseudoIdSet, MatchType& matchType) const
+bool SelectorChecker::checkOne(const CheckingContextWithStatus& context, PseudoIdSet& dynamicPseudoIdSet, MatchType& matchType, unsigned& specificity) const
 {
     Element* const & element = context.element;
     const CSSSelector* const & selector = context.selector;
     ASSERT(element);
     ASSERT(selector);
 
+    specificity = CSSSelector::addSpecificities(specificity, selector->simpleSelectorSpecificity());
+
     if (selector->match() == CSSSelector::Tag)
         return SelectorChecker::tagMatches(element, selector->tagQName());
 
@@ -551,8 +590,9 @@
                 subcontext.firstSelectorOfTheFragment = selectorList->first();
                 PseudoIdSet ignoreDynamicPseudo;
 
+                unsigned ignoredSpecificity;
 #if ENABLE(CSS_SELECTORS_LEVEL4)
-                if (matchRecursively(subcontext, ignoreDynamicPseudo).match == Match::SelectorMatches) {
+                if (matchRecursively(subcontext, ignoreDynamicPseudo, ignoredSpecificity).match == Match::SelectorMatches) {
                     ASSERT(!ignoreDynamicPseudo);
                     return false;
                 }
@@ -568,7 +608,7 @@
                 }
                 // Since :not cannot contain pseudo elements, there's no effect on matchType.
                 MatchType ignoreMatchType = MatchType::Element;
-                if (!checkOne(subcontext, ignoreDynamicPseudo, ignoreMatchType))
+                if (!checkOne(subcontext, ignoreDynamicPseudo, ignoreMatchType, ignoredSpecificity))
                     return true;
 #endif
             }
@@ -694,7 +734,8 @@
                     subcontext.selector = subselector;
                     subcontext.firstSelectorOfTheFragment = subselector;
                     PseudoIdSet localDynamicPseudoIdSet;
-                    MatchResult result = matchRecursively(subcontext, localDynamicPseudoIdSet);
+                    unsigned localSpecificity = 0;
+                    MatchResult result = matchRecursively(subcontext, localDynamicPseudoIdSet, localSpecificity);
                     if (result.match == Match::SelectorMatches) {
                         if (!context.pseudoElementEffective) {
                             // When pseudo elements are not effective in this fragment (e.g. it's not righmost fragment),
@@ -838,7 +879,8 @@
                 for (subContext.selector = selector->selectorList()->first(); subContext.selector; subContext.selector = CSSSelectorList::next(subContext.selector)) {
                     subContext.firstSelectorOfTheFragment = subContext.selector;
                     PseudoIdSet ignoreDynamicPseudo;
-                    if (matchRecursively(subContext, ignoreDynamicPseudo).match == Match::SelectorMatches)
+                    unsigned ingoredSpecificity = 0;
+                    if (matchRecursively(subContext, ignoreDynamicPseudo, ingoredSpecificity).match == Match::SelectorMatches)
                         return true;
                 }
             }
@@ -993,8 +1035,9 @@
             subContext.firstSelectorOfTheFragment = subContext.selector;
             subContext.inFunctionalPseudoClass = true;
             subContext.pseudoElementEffective = false;
-            PseudoIdSet ignoreDynamicPseudo;
-            if (matchRecursively(subContext, ignoreDynamicPseudo).match == Match::SelectorMatches)
+            PseudoIdSet ignoredDynamicPseudo;
+            unsigned ignoredSpecificity = 0;
+            if (matchRecursively(subContext, ignoredDynamicPseudo, ignoredSpecificity).match == Match::SelectorMatches)
                 return true;
         }
         return false;
@@ -1014,7 +1057,8 @@
         subcontext.pseudoElementEffective = false;
         subcontext.firstSelectorOfTheFragment = subselector;
         PseudoIdSet ignoreDynamicPseudo;
-        if (matchRecursively(subcontext, ignoreDynamicPseudo).match == Match::SelectorMatches) {
+        unsigned localSpecificity = 0;
+        if (matchRecursively(subcontext, ignoreDynamicPseudo, localSpecificity).match == Match::SelectorMatches) {
             ASSERT(!ignoreDynamicPseudo);
             return true;
         }

Modified: trunk/Source/WebCore/css/SelectorChecker.h (176151 => 176152)


--- trunk/Source/WebCore/css/SelectorChecker.h	2014-11-15 02:31:28 UTC (rev 176151)
+++ trunk/Source/WebCore/css/SelectorChecker.h	2014-11-15 02:32:37 UTC (rev 176152)
@@ -107,8 +107,8 @@
     static unsigned determineLinkMatchType(const CSSSelector*);
 
 private:
-    MatchResult matchRecursively(const CheckingContextWithStatus&, PseudoIdSet&) const;
-    bool checkOne(const CheckingContextWithStatus&, PseudoIdSet&, MatchType&) const;
+    MatchResult matchRecursively(const CheckingContextWithStatus&, PseudoIdSet&, unsigned& specificity) const;
+    bool checkOne(const CheckingContextWithStatus&, PseudoIdSet&, MatchType&, unsigned& specificity) const;
     bool matchSelectorList(const CheckingContextWithStatus&, Element&, const CSSSelectorList&) const;
 
     bool checkScrollbarPseudoClass(const CheckingContextWithStatus&, const CSSSelector*) const;
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to