⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 176307 in webkit


Ignore:
Timestamp:
Nov 18, 2014, 9:12:53 PM (12 years ago)
Author:
benjamin@webkit.org
Message:

Add the initial implementation of dynamic specificity for :matches()
https://bugs.webkit.org/show_bug.cgi?id=138822

Reviewed by Andreas Kling.

Source/WebCore:

Previously we completely ignored the selector list of :matches() when computing
the specificity of the selector.

The spec (http://dev.w3.org/csswg/selectors4/#specificity) says:
"The specificity of a :matches() pseudo-class, however, is the specificity

of the most specific complex selector that matched the given element."

This patch does just that.

In the CSS JIT, we only consider specificities that can be computed statically
for now, this should be extended later.
When the specificity is dynamic, we fall back to SelectorChecker. In that case,
we execute every selector of the selector list and we keep the maximum value
as the specificity for the whole :matches().

Tests: fast/css/matches-specificity-1.html

fast/css/matches-specificity-2.html
fast/css/matches-specificity-3.html
fast/css/matches-specificity-4.html
fast/css/matches-specificity-5.html
fast/css/matches-specificity-6.html
fast/css/matches-specificity-7.html
fast/css/matches-specificity-8.html
fast/css/matches-specificity-9.html
fast/css/matches-specificity-10.html

  • css/CSSSelector.cpp:

(WebCore::selectorSpecificity):
(WebCore::maxSpecificity):
(WebCore::CSSSelector::specificity):
(WebCore::simpleSelectorSpecificityInternal):
(WebCore::CSSSelector::simpleSelectorSpecificity):
:matches() itself should not have any specificity. The specificity of its components
is computed in SelectorChecker.

Since :matches() is no longer accounted as a class B specificity, I had to finish
the specificity of :not() or some test breaks.

  • css/CSSSelector.h:
  • css/PageRuleCollector.cpp:

(WebCore::comparePageRules):

  • css/SelectorChecker.cpp:

(WebCore::SelectorChecker::checkOne):
We can no longer shortcut the execution when context.pseudoElementEffective is false.
There is no guarantee that a following selector wouldn't match with a higher specificity.

  • cssjit/SelectorCompiler.cpp:

(WebCore::SelectorCompiler::addPseudoClassType):
(WebCore::SelectorCompiler::SelectorCodeGenerator::SelectorCodeGenerator):
(WebCore::SelectorCompiler::constructFragments):
(WebCore::SelectorCompiler::SelectorCodeGenerator::generateSelectorChecker):

LayoutTests:

  • fast/css/matches-specificity-1-expected.html: Added.
  • fast/css/matches-specificity-1.html: Added.
  • fast/css/matches-specificity-2-expected.html: Added.
  • fast/css/matches-specificity-2.html: Added.
  • fast/css/matches-specificity-3-expected.html: Added.
  • fast/css/matches-specificity-3.html: Added.
  • fast/css/matches-specificity-4-expected.html: Added.
  • fast/css/matches-specificity-4.html: Added.
  • fast/css/matches-specificity-5-expected.html: Added.
  • fast/css/matches-specificity-5.html: Added.
  • fast/css/matches-specificity-6-expected.html: Added.
  • fast/css/matches-specificity-6.html: Added.
  • fast/css/matches-specificity-7-expected.html: Added.
  • fast/css/matches-specificity-7.html: Added.
  • fast/css/matches-specificity-8-expected.html: Added.
  • fast/css/matches-specificity-8.html: Added.
  • fast/css/matches-specificity-9-expected.html: Added.
  • fast/css/matches-specificity-9.html: Added.
  • fast/css/matches-specificity-10-expected.html: Added.
  • fast/css/matches-specificity-10.html: Added.
Location:
trunk
Files:
20 added
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r176301 r176307  
     12014-11-18  Benjamin Poulain  <benjamin@webkit.org>
     2
     3        Add the initial implementation of dynamic specificity for :matches()
     4        https://bugs.webkit.org/show_bug.cgi?id=138822
     5
     6        Reviewed by Andreas Kling.
     7
     8        * fast/css/matches-specificity-1-expected.html: Added.
     9        * fast/css/matches-specificity-1.html: Added.
     10        * fast/css/matches-specificity-2-expected.html: Added.
     11        * fast/css/matches-specificity-2.html: Added.
     12        * fast/css/matches-specificity-3-expected.html: Added.
     13        * fast/css/matches-specificity-3.html: Added.
     14        * fast/css/matches-specificity-4-expected.html: Added.
     15        * fast/css/matches-specificity-4.html: Added.
     16        * fast/css/matches-specificity-5-expected.html: Added.
     17        * fast/css/matches-specificity-5.html: Added.
     18        * fast/css/matches-specificity-6-expected.html: Added.
     19        * fast/css/matches-specificity-6.html: Added.
     20        * fast/css/matches-specificity-7-expected.html: Added.
     21        * fast/css/matches-specificity-7.html: Added.
     22        * fast/css/matches-specificity-8-expected.html: Added.
     23        * fast/css/matches-specificity-8.html: Added.
     24        * fast/css/matches-specificity-9-expected.html: Added.
     25        * fast/css/matches-specificity-9.html: Added.
     26        * fast/css/matches-specificity-10-expected.html: Added.
     27        * fast/css/matches-specificity-10.html: Added.
     28
    1292014-11-18  Chris Dumez  <cdumez@apple.com>
    230
  • trunk/Source/WebCore/ChangeLog

    r176303 r176307  
     12014-11-18  Benjamin Poulain  <benjamin@webkit.org>
     2
     3        Add the initial implementation of dynamic specificity for :matches()
     4        https://bugs.webkit.org/show_bug.cgi?id=138822
     5
     6        Reviewed by Andreas Kling.
     7
     8        Previously we completely ignored the selector list of :matches() when computing
     9        the specificity of the selector.
     10
     11        The spec (http://dev.w3.org/csswg/selectors4/#specificity) says:
     12        "The specificity of a :matches() pseudo-class, however, is the specificity
     13         of the most specific complex selector that matched the given element."
     14
     15        This patch does just that.
     16
     17        In the CSS JIT, we only consider specificities that can be computed statically
     18        for now, this should be extended later.
     19        When the specificity is dynamic, we fall back to SelectorChecker. In that case,
     20        we execute every selector of the selector list and we keep the maximum value
     21        as the specificity for the whole :matches().
     22
     23        Tests: fast/css/matches-specificity-1.html
     24               fast/css/matches-specificity-2.html
     25               fast/css/matches-specificity-3.html
     26               fast/css/matches-specificity-4.html
     27               fast/css/matches-specificity-5.html
     28               fast/css/matches-specificity-6.html
     29               fast/css/matches-specificity-7.html
     30               fast/css/matches-specificity-8.html
     31               fast/css/matches-specificity-9.html
     32               fast/css/matches-specificity-10.html
     33
     34        * css/CSSSelector.cpp:
     35        (WebCore::selectorSpecificity):
     36        (WebCore::maxSpecificity):
     37        (WebCore::CSSSelector::specificity):
     38        (WebCore::simpleSelectorSpecificityInternal):
     39        (WebCore::CSSSelector::simpleSelectorSpecificity):
     40        :matches() itself should not have any specificity. The specificity of its components
     41        is computed in SelectorChecker.
     42
     43        Since :matches() is no longer accounted as a class B specificity, I had to finish
     44        the specificity of :not() or some test breaks.
     45
     46        * css/CSSSelector.h:
     47        * css/PageRuleCollector.cpp:
     48        (WebCore::comparePageRules):
     49        * css/SelectorChecker.cpp:
     50        (WebCore::SelectorChecker::checkOne):
     51        We can no longer shortcut the execution when context.pseudoElementEffective is false.
     52        There is no guarantee that a following selector wouldn't match with a higher specificity.
     53
     54        * cssjit/SelectorCompiler.cpp:
     55        (WebCore::SelectorCompiler::addPseudoClassType):
     56        (WebCore::SelectorCompiler::SelectorCodeGenerator::SelectorCodeGenerator):
     57        (WebCore::SelectorCompiler::constructFragments):
     58        (WebCore::SelectorCompiler::SelectorCodeGenerator::generateSelectorChecker):
     59
    1602014-11-18  David Kilzer  <ddkilzer@apple.com>
    261
  • trunk/Source/WebCore/css/CSSSelector.cpp

    r176241 r176307  
    6060}
    6161
     62static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity);
     63
     64static unsigned selectorSpecificity(const CSSSelector& firstSimpleSelector, bool isComputingMaximumSpecificity)
     65{
     66    unsigned total = simpleSelectorSpecificityInternal(firstSimpleSelector, isComputingMaximumSpecificity);
     67
     68    for (const CSSSelector* selector = firstSimpleSelector.tagHistory(); selector; selector = selector->tagHistory())
     69        total = CSSSelector::addSpecificities(total, simpleSelectorSpecificityInternal(*selector, isComputingMaximumSpecificity));
     70    return total;
     71}
     72
     73static unsigned maxSpecificity(const CSSSelectorList& selectorList)
     74{
     75    unsigned maxSpecificity = 0;
     76    for (const CSSSelector* subSelector = selectorList.first(); subSelector; subSelector = CSSSelectorList::next(subSelector))
     77        maxSpecificity = std::max(maxSpecificity, selectorSpecificity(*subSelector, true));
     78    return maxSpecificity;
     79}
     80
    6281unsigned CSSSelector::specificity() const
    6382{
     
    6584        return specificityForPage() & maxValueMask;
    6685
    67     unsigned total = simpleSelectorSpecificity();
    68 
    69     for (const CSSSelector* selector = this->tagHistory(); selector; selector = selector->tagHistory())
    70         total = addSpecificities(total, selector->simpleSelectorSpecificity());
    71     return total;
    72 }
    73 
    74 unsigned CSSSelector::simpleSelectorSpecificity() const
    75 {
    76     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.");
    77 
    78     switch (match()) {
    79     case Id:
     86    return selectorSpecificity(*this, false);
     87}
     88
     89static unsigned simpleSelectorSpecificityInternal(const CSSSelector& simpleSelector, bool isComputingMaximumSpecificity)
     90{
     91    ASSERT_WITH_MESSAGE(!simpleSelector.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.");
     92
     93    switch (simpleSelector.match()) {
     94    case CSSSelector::Id:
    8095        return static_cast<unsigned>(SelectorSpecificityIncrement::ClassA);
    8196
    82     case PagePseudoClass:
     97    case CSSSelector::PagePseudoClass:
    8398        break;
    84     case PseudoClass:
    85 #if ENABLE(CSS_SELECTORS_LEVEL4)
    86         if (pseudoClassType() == PseudoClassNot) {
    87             ASSERT_WITH_MESSAGE(selectorList() && selectorList()->first(), "The parser should never generate a valid selector for an empty :not().");
    88 
    89             unsigned maxSpecificity = 0;
    90             for (const CSSSelector* subSelector = selectorList()->first(); subSelector; subSelector = CSSSelectorList::next(subSelector))
    91                 maxSpecificity = std::max(maxSpecificity, subSelector->specificity());
    92             return maxSpecificity;
     99    case CSSSelector::PseudoClass:
     100#if ENABLE(CSS_SELECTORS_LEVEL4)
     101        if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassMatches) {
     102            ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :matches().");
     103            if (!isComputingMaximumSpecificity)
     104                return 0;
     105            return maxSpecificity(*simpleSelector.selectorList());
     106        }
     107
     108        if (simpleSelector.pseudoClassType() == CSSSelector::PseudoClassNot) {
     109            ASSERT_WITH_MESSAGE(simpleSelector.selectorList() && simpleSelector.selectorList()->first(), "The parser should never generate a valid selector for an empty :not().");
     110            return maxSpecificity(*simpleSelector.selectorList());
    93111        }
    94112        FALLTHROUGH;
     
    98116        FALLTHROUGH;
    99117#endif
    100     case Exact:
    101     case Class:
    102     case Set:
    103     case List:
    104     case Hyphen:
    105     case Contain:
    106     case Begin:
    107     case End:
     118    case CSSSelector::Exact:
     119    case CSSSelector::Class:
     120    case CSSSelector::Set:
     121    case CSSSelector::List:
     122    case CSSSelector::Hyphen:
     123    case CSSSelector::Contain:
     124    case CSSSelector::Begin:
     125    case CSSSelector::End:
    108126        return static_cast<unsigned>(SelectorSpecificityIncrement::ClassB);
    109     case Tag:
    110         return (tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0;
    111     case PseudoElement:
     127    case CSSSelector::Tag:
     128        return (simpleSelector.tagQName().localName() != starAtom) ? static_cast<unsigned>(SelectorSpecificityIncrement::ClassC) : 0;
     129    case CSSSelector::PseudoElement:
    112130        return static_cast<unsigned>(SelectorSpecificityIncrement::ClassC);
    113     case Unknown:
     131    case CSSSelector::Unknown:
    114132        return 0;
    115133    }
    116134    ASSERT_NOT_REACHED();
    117135    return 0;
     136}
     137
     138unsigned CSSSelector::simpleSelectorSpecificity() const
     139{
     140    return simpleSelectorSpecificityInternal(*this, false);
    118141}
    119142
  • trunk/Source/WebCore/css/CSSSelector.h

    r176241 r176307  
    6060
    6161        unsigned specificity() const;
     62        unsigned specificityForPage() const;
    6263        unsigned simpleSelectorSpecificity() const;
    6364        static unsigned addSpecificities(unsigned, unsigned);
     
    313314
    314315        unsigned simpleSelectorSpecificityForPage() const;
    315         unsigned specificityForPage() const;
    316316
    317317        // Hide.
  • trunk/Source/WebCore/css/PageRuleCollector.cpp

    r173569 r176307  
    3838static inline bool comparePageRules(const StyleRulePage* r1, const StyleRulePage* r2)
    3939{
    40     return r1->selector()->specificity() < r2->selector()->specificity();
     40    return r1->selector()->specificityForPage() < r2->selector()->specificityForPage();
    4141}
    4242
  • trunk/Source/WebCore/css/SelectorChecker.cpp

    r176241 r176307  
    727727            {
    728728                bool hasMatchedAnything = false;
     729                unsigned maxSpecificity = 0;
     730
    729731                MatchType localMatchType = MatchType::VirtualPseudoElementOnly;
    730732                for (const CSSSelector* subselector = selector->selectorList()->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
     
    738740                    MatchResult result = matchRecursively(subcontext, localDynamicPseudoIdSet, localSpecificity);
    739741                    if (result.match == Match::SelectorMatches) {
    740                         if (!context.pseudoElementEffective) {
    741                             // When pseudo elements are not effective in this fragment (e.g. it's not righmost fragment),
    742                             // it's not necessary to check all selectors to collect pseudo element ids.
    743                             ASSERT(!localDynamicPseudoIdSet);
    744                             return true;
    745                         }
     742                        maxSpecificity = std::max(maxSpecificity, localSpecificity);
    746743
    747744                        if (result.matchType == MatchType::Element)
     
    752749                    }
    753750                }
    754                 if (hasMatchedAnything)
     751                if (hasMatchedAnything) {
    755752                    matchType = localMatchType;
     753                    specificity = CSSSelector::addSpecificities(specificity, maxSpecificity);
     754                }
    756755                return hasMatchedAnything;
    757756            }
  • trunk/Source/WebCore/cssjit/SelectorCompiler.cpp

    r176275 r176307  
    219219    unsigned registerRequirements = std::numeric_limits<unsigned>::max();
    220220    unsigned stackRequirements = std::numeric_limits<unsigned>::max();
     221    unsigned staticSpecificity = 0;
    221222    bool clobberElementAddressRegister = true;
    222223};
     
    358359    StackAllocator::StackReference m_startElement;
    359360
     361#if CSS_SELECTOR_JIT_DEBUGGING
    360362    const CSSSelector* m_originalSelector;
     363#endif
    361364};
    362365
     
    463466}
    464467
    465 static inline FunctionType addPseudoClassType(const CSSSelector& selector, SelectorFragment& fragment, SelectorContext selectorContext, FragmentsLevel fragmentLevel, FragmentPositionInRootFragments positionInRootFragments, bool visitedMatchEnabled, VisitedMode& visitedMode, PseudoElementMatchingBehavior pseudoElementMatchingBehavior)
     468static inline FunctionType addPseudoClassType(const CSSSelector& selector, SelectorFragment& fragment, unsigned& internalSpecificity, SelectorContext selectorContext, FragmentsLevel fragmentLevel, FragmentPositionInRootFragments positionInRootFragments, bool visitedMatchEnabled, VisitedMode& visitedMode, PseudoElementMatchingBehavior pseudoElementMatchingBehavior)
    466469{
    467470    CSSSelector::PseudoClassType type = selector.pseudoClassType();
     
    760763            const CSSSelectorList* selectorList = selector.selectorList();
    761764            FunctionType functionType = FunctionType::SimpleSelectorChecker;
     765            unsigned firstFragmentListSpecificity = 0;
     766            bool firstFragmentListSpecificitySet = false;
    762767            for (const CSSSelector* subselector = selectorList->first(); subselector; subselector = CSSSelectorList::next(subselector)) {
    763768                SelectorFragmentList selectorFragments;
     
    777782                    return FunctionType::CannotCompile;
    778783
     784                if (firstFragmentListSpecificitySet) {
     785                    // The CSS JIT does not handle dynamic specificity yet.
     786                    if (selectorContext == SelectorContext::RuleCollector && selectorFragments.staticSpecificity != firstFragmentListSpecificity)
     787                        return FunctionType::CannotCompile;
     788                } else {
     789                    firstFragmentListSpecificitySet = true;
     790                    firstFragmentListSpecificity = selectorFragments.staticSpecificity;
     791                }
     792
    779793                functionType = mostRestrictiveFunctionType(functionType, localFunctionType);
    780794                matchesList.append(selectorFragments);
     
    784798            if (matchesList.isEmpty())
    785799                return FunctionType::CannotMatchAnything;
     800
     801            internalSpecificity = firstFragmentListSpecificity;
    786802
    787803            fragment.matchesFilters.append(matchesList);
     
    806822    , m_visitedMode(VisitedMode::None)
    807823    , m_descendantBacktrackingStartInUse(false)
     824#if CSS_SELECTOR_JIT_DEBUGGING
    808825    , m_originalSelector(rootSelector)
     826#endif
    809827{
    810828#if CSS_SELECTOR_JIT_DEBUGGING
     
    836854    FragmentRelation relationToPreviousFragment = FragmentRelation::Rightmost;
    837855    FunctionType functionType = FunctionType::SimpleSelectorChecker;
     856    unsigned specificity = 0;
    838857    for (const CSSSelector* selector = rootSelector; selector; selector = selector->tagHistory()) {
     858        specificity = CSSSelector::addSpecificities(specificity, selector->simpleSelectorSpecificity());
     859
    839860        CSSSelector::Relation relation = selector->relation();
    840861
     
    875896            if (fragment.pseudoElementSelector && isScrollbarPseudoElement(fragment.pseudoElementSelector->pseudoElementType()))
    876897                functionType = mostRestrictiveFunctionType(functionType, addScrollbarPseudoClassType(*selector, fragment));
    877             else
    878                 functionType = mostRestrictiveFunctionType(functionType, addPseudoClassType(*selector, fragment, selectorContext, fragmentLevel, subPosition, visitedMatchEnabled, visitedMode, pseudoElementMatchingBehavior));
     898            else {
     899                unsigned internalSpecificity = 0;
     900                functionType = mostRestrictiveFunctionType(functionType, addPseudoClassType(*selector, fragment, internalSpecificity, selectorContext, fragmentLevel, subPosition, visitedMatchEnabled, visitedMode, pseudoElementMatchingBehavior));
     901                specificity = CSSSelector::addSpecificities(specificity, internalSpecificity);
     902            }
    879903            if (!pseudoClassOnlyMatchesLinksInQuirksMode(*selector))
    880904                fragment.onlyMatchesLinksInQuirksMode = false;
     
    9761000        fragment = SelectorFragment();
    9771001    }
     1002
     1003    selectorFragments.staticSpecificity = specificity;
     1004
    9781005    return functionType;
    9791006}
     
    16181645
    16191646    if (m_selectorContext == SelectorContext::RuleCollector) {
    1620         unsigned specificity = m_originalSelector->specificity();
     1647        unsigned specificity = m_selectorFragments.staticSpecificity;
    16211648        if (m_functionType == FunctionType::SelectorCheckerWithCheckingContext)
    16221649            m_assembler.store32(Assembler::TrustedImm32(specificity), JSC::GPRInfo::argumentGPR2);
Note: See TracChangeset for help on using the changeset viewer.