Changeset 96517 in webkit


Ignore:
Timestamp:
Oct 3, 2011, 10:40:18 AM (14 years ago)
Author:
Antti Koivisto
Message:

Add exact match attribute selectors to the fast path
https://bugs.webkit.org/show_bug.cgi?id=69159

Reviewed by Sam Weinig.

Attribute selectors are increasingly common and we have them in our default style sheet too.
[foo] and [foo="bar"] type selectors can be resolved quickly and easily in the fast path.

  • Implement fast path checking for simple attribute selectors.
  • Get rid of the ill-defined CSSSelector::hasAttribute(), inline CSSSelector::attribute()


This is ~20% progression in styleForElement() performance loading the full HTML5 spec (~0.8s).

  • css/CSSSelector.cpp:

(WebCore::CSSSelector::selectorText):

  • css/CSSSelector.h:

(WebCore::CSSSelector::hasTag):
(WebCore::CSSSelector::attribute):
(WebCore::CSSSelector::isAttributeSelector):

  • css/CSSSelectorList.cpp:

(WebCore::SelectorNeedsNamespaceResolutionFunctor::operator()):

  • css/CSSStyleSelector.cpp:

(WebCore::CSSStyleSelector::checkSelector):

  • css/SelectorChecker.cpp:

(WebCore::SelectorChecker::fastCheckRightmostSelector):
(WebCore::SelectorChecker::fastCheckSelector):
(WebCore::isFastCheckableMatch):
(WebCore::isFastCheckableRightmostSelector):
(WebCore::SelectorChecker::isFastCheckableSelector):
(WebCore::SelectorChecker::checkSelector):
(WebCore::htmlAttributeHasCaseInsensitiveValue):
(WebCore::anyAttributeMatches):
(WebCore::SelectorChecker::checkOneSelector):

  • css/SelectorChecker.h:

(WebCore::SelectorChecker::attributeNameMatches):
(WebCore::SelectorChecker::checkExactAttribute):
(WebCore::SelectorChecker::fastCheckRightmostAttributeSelector):

Location:
trunk/Source/WebCore
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebCore/ChangeLog

    r96515 r96517  
     12011-09-30  Antti Koivisto  <antti@apple.com>
     2
     3        Add exact match attribute selectors to the fast path
     4        https://bugs.webkit.org/show_bug.cgi?id=69159
     5
     6        Reviewed by Sam Weinig.
     7
     8        Attribute selectors are increasingly common and we have them in our default style sheet too.
     9        [foo] and [foo="bar"] type selectors can be resolved quickly and easily in the fast path.
     10       
     11        - Implement fast path checking for simple attribute selectors.
     12        - Get rid of the ill-defined CSSSelector::hasAttribute(), inline CSSSelector::attribute()
     13       
     14        This is ~20% progression in styleForElement() performance loading the full HTML5 spec (~0.8s).
     15
     16        * css/CSSSelector.cpp:
     17        (WebCore::CSSSelector::selectorText):
     18        * css/CSSSelector.h:
     19        (WebCore::CSSSelector::hasTag):
     20        (WebCore::CSSSelector::attribute):
     21        (WebCore::CSSSelector::isAttributeSelector):
     22        * css/CSSSelectorList.cpp:
     23        (WebCore::SelectorNeedsNamespaceResolutionFunctor::operator()):
     24        * css/CSSStyleSelector.cpp:
     25        (WebCore::CSSStyleSelector::checkSelector):
     26        * css/SelectorChecker.cpp:
     27        (WebCore::SelectorChecker::fastCheckRightmostSelector):
     28        (WebCore::SelectorChecker::fastCheckSelector):
     29        (WebCore::isFastCheckableMatch):
     30        (WebCore::isFastCheckableRightmostSelector):
     31        (WebCore::SelectorChecker::isFastCheckableSelector):
     32        (WebCore::SelectorChecker::checkSelector):
     33        (WebCore::htmlAttributeHasCaseInsensitiveValue):
     34        (WebCore::anyAttributeMatches):
     35        (WebCore::SelectorChecker::checkOneSelector):
     36        * css/SelectorChecker.h:
     37        (WebCore::SelectorChecker::attributeNameMatches):
     38        (WebCore::SelectorChecker::checkExactAttribute):
     39        (WebCore::SelectorChecker::fastCheckRightmostAttributeSelector):
     40
    1412011-10-03  Mike Reed  <reed@google.com>
    242
  • trunk/Source/WebCore/css/CSSSelector.cpp

    r90971 r96517  
    581581            str += "::";
    582582            str += cs->value();
    583         } else if (cs->hasAttribute()) {
     583        } else if (cs->isAttributeSelector()) {
    584584            str += "[";
    585585            const AtomicString& prefix = cs->attribute().prefix();
     
    641641}
    642642
    643 const QualifiedName& CSSSelector::attribute() const
    644 {
    645     switch (m_match) {
    646     case Id:
    647         return idAttr;
    648     case Class:
    649         return classAttr;
    650     default:
    651         return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
    652     }
    653 }
    654 
    655643void CSSSelector::setAttribute(const QualifiedName& value)
    656644{
  • trunk/Source/WebCore/css/CSSSelector.h

    r96393 r96517  
    221221
    222222        bool hasTag() const { return m_tag != anyQName(); }
    223         bool hasAttribute() const { return m_match == Id || m_match == Class || (m_hasRareData && m_data.m_rareData->m_attribute != anyQName()); }
    224223       
    225224        const QualifiedName& tag() const { return m_tag; }
     
    298297        QualifiedName m_tag;
    299298    };
     299
     300inline const QualifiedName& CSSSelector::attribute() const
     301{
     302    ASSERT(isAttributeSelector());
     303    ASSERT(m_hasRareData);
     304    return m_data.m_rareData->m_attribute;
     305}
    300306   
    301307inline bool CSSSelector::matchesPseudoElement() const
     
    331337inline bool CSSSelector::isAttributeSelector() const
    332338{
    333     if (!hasAttribute())
    334         return false;
    335339    return m_match == CSSSelector::Exact
    336340        || m_match ==  CSSSelector::Set
  • trunk/Source/WebCore/css/CSSSelectorList.cpp

    r95901 r96517  
    143143        if (selector->hasTag() && selector->tag().prefix() != nullAtom && selector->tag().prefix() != starAtom)
    144144            return true;
    145         if (selector->hasAttribute() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
     145        if (selector->isAttributeSelector() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
    146146            return true;
    147147        return false;
  • trunk/Source/WebCore/css/CSSStyleSelector.cpp

    r96433 r96517  
    18281828                return true;
    18291829        } else if (!SelectorChecker::tagMatches(m_element, ruleData.selector()))
     1830            return false;
     1831        if (!SelectorChecker::fastCheckRightmostAttributeSelector(m_element, ruleData.selector()))
    18301832            return false;
    18311833        return m_checker.fastCheckSelector(ruleData.selector(), m_element);
  • trunk/Source/WebCore/css/SelectorChecker.cpp

    r96393 r96517  
    6464   
    6565using namespace HTMLNames;
     66   
     67static bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr);
    6668
    6769SelectorChecker::SelectorChecker(Document* document, bool strictParsing)
     
    262264namespace {
    263265
    264 template <bool checkValue(const Element*, AtomicStringImpl*)>
     266template <bool checkValue(const Element*, AtomicStringImpl*, const QualifiedName&), bool initAttributeName>
    265267inline bool fastCheckSingleSelector(const CSSSelector*& selector, const Element*& element, const CSSSelector*& topChildOrSubselector, const Element*& topChildOrSubselectorMatchElement)
    266268{
    267269    AtomicStringImpl* value = selector->value().impl();
     270    const QualifiedName& attribute = initAttributeName ? selector->attribute() : anyQName();
    268271    for (; element; element = element->parentElement()) {
    269         if (checkValue(element, value) && SelectorChecker::tagMatches(element, selector)) {
     272        if (checkValue(element, value, attribute) && SelectorChecker::tagMatches(element, selector)) {
    270273            if (selector->relation() == CSSSelector::Descendant)
    271274                topChildOrSubselector = 0;
     
    297300}
    298301
    299 inline bool checkClassValue(const Element* element, AtomicStringImpl* value)
     302inline bool checkClassValue(const Element* element, AtomicStringImpl* value, const QualifiedName&)
    300303{
    301304    return element->hasClass() && static_cast<const StyledElement*>(element)->classNames().contains(value);
    302305}
    303306
    304 inline bool checkIDValue(const Element* element, AtomicStringImpl* value)
     307inline bool checkIDValue(const Element* element, AtomicStringImpl* value, const QualifiedName&)
    305308{
    306309    return element->hasID() && element->idForStyleResolution().impl() == value;
    307310}
    308311
    309 inline bool checkTagValue(const Element*, AtomicStringImpl*)
     312inline bool checkExactAttributeValue(const Element* element, AtomicStringImpl* value, const QualifiedName& attributeName)
     313{
     314    return SelectorChecker::checkExactAttribute(element, attributeName, value);
     315}
     316
     317inline bool checkTagValue(const Element*, AtomicStringImpl*, const QualifiedName&)
    310318{
    311319    return true;
     
    324332        return true;
    325333    case CSSSelector::Class:
    326         return element->hasClass() && static_cast<const StyledElement*>(element)->classNames().contains(selector->value());
     334        return checkClassValue(element, selector->value().impl(), anyQName());
    327335    case CSSSelector::Id:
    328         return element->hasID() && element->idForStyleResolution() == selector->value();
     336        return checkIDValue(element, selector->value().impl(), anyQName());
     337    case CSSSelector::Exact:
     338    case CSSSelector::Set:
     339        return checkExactAttributeValue(element, selector->value().impl(), selector->attribute());
    329340    case CSSSelector::PseudoClass:
    330341        return commonPseudoClassSelectorMatches(element, selector);
     
    353364        switch (selector->m_match) {
    354365        case CSSSelector::Class:
    355             if (!fastCheckSingleSelector<checkClassValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
     366            if (!fastCheckSingleSelector<checkClassValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
    356367                return false;
    357368            break;
    358369        case CSSSelector::Id:
    359             if (!fastCheckSingleSelector<checkIDValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
     370            if (!fastCheckSingleSelector<checkIDValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
    360371                return false;
    361372            break;
    362373        case CSSSelector::None:
    363             if (!fastCheckSingleSelector<checkTagValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
     374            if (!fastCheckSingleSelector<checkTagValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
     375                return false;
     376            break;
     377        case CSSSelector::Set:
     378        case CSSSelector::Exact:
     379            if (!fastCheckSingleSelector<checkExactAttributeValue, true>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
    364380                return false;
    365381            break;
     
    376392}
    377393
    378 static inline bool isFastCheckableMatch(CSSSelector::Match match)
    379 {
    380     return match == CSSSelector::None || match == CSSSelector::Id || match == CSSSelector::Class;
     394static inline bool isFastCheckableMatch(const CSSSelector* selector)
     395{
     396    if (selector->m_match == CSSSelector::Set)
     397        return true;
     398    if (selector->m_match == CSSSelector::Exact)
     399        return !htmlAttributeHasCaseInsensitiveValue(selector->attribute());
     400    return selector->m_match == CSSSelector::None || selector->m_match == CSSSelector::Id || selector->m_match == CSSSelector::Class;
    381401}
    382402
     
    385405    if (!isFastCheckableRelation(selector->relation()))
    386406        return false;
    387     return isFastCheckableMatch(static_cast<CSSSelector::Match>(selector->m_match)) || SelectorChecker::isCommonPseudoClassSelector(selector);
     407    return isFastCheckableMatch(selector) || SelectorChecker::isCommonPseudoClassSelector(selector);
    388408}
    389409
     
    395415        if (!isFastCheckableRelation(selector->relation()))
    396416            return false;
    397         if (!isFastCheckableMatch(static_cast<CSSSelector::Match>(selector->m_match)))
     417        if (!isFastCheckableMatch(selector))
    398418            return false;
    399419    }
     
    579599}
    580600
    581 static bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr)
     601bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr)
    582602{
    583603    static HashSet<AtomicStringImpl*>* htmlCaseInsensitiveAttributesSet = createHtmlCaseInsensitiveAttributesSet();
    584604    bool isPossibleHTMLAttr = !attr.hasPrefix() && (attr.namespaceURI() == nullAtom);
    585605    return isPossibleHTMLAttr && htmlCaseInsensitiveAttributesSet->contains(attr.localName().impl());
    586 }
    587 
    588 static bool attributeQualifiedNameMatches(Attribute* attribute, const QualifiedName& selectorAttr)
    589 {
    590     if (selectorAttr.localName() != attribute->localName())
    591         return false;
    592    
    593     return selectorAttr.prefix() == starAtom || selectorAttr.namespaceURI() == attribute->namespaceURI();
    594606}
    595607
     
    662674        Attribute* attributeItem = attributes->attributeItem(i);
    663675       
    664         if (!attributeQualifiedNameMatches(attributeItem, selectorAttr))
     676        if (!SelectorChecker::attributeNameMatches(attributeItem, selectorAttr))
    665677            continue;
    666678       
     
    678690        return false;
    679691   
    680     if (sel->hasAttribute()) {
    681         if (sel->m_match == CSSSelector::Class)
    682             return e->hasClass() && static_cast<StyledElement*>(e)->classNames().contains(sel->value());
    683        
    684         if (sel->m_match == CSSSelector::Id)
    685             return e->hasID() && e->idForStyleResolution() == sel->value();
    686        
     692    if (sel->m_match == CSSSelector::Class)
     693        return e->hasClass() && static_cast<StyledElement*>(e)->classNames().contains(sel->value());
     694   
     695    if (sel->m_match == CSSSelector::Id)
     696        return e->hasID() && e->idForStyleResolution() == sel->value();
     697   
     698    if (sel->isAttributeSelector()) {
    687699        const QualifiedName& attr = sel->attribute();
    688700       
  • trunk/Source/WebCore/css/SelectorChecker.h

    r95966 r96517  
    2929#define SelectorChecker_h
    3030
     31#include "Attribute.h"
    3132#include "CSSSelector.h"
    3233#include "Element.h"
     
    8485   
    8586    static bool tagMatches(const Element*, const CSSSelector*);
     87    static bool attributeNameMatches(const Attribute*, const QualifiedName&);
    8688    static bool isCommonPseudoClassSelector(const CSSSelector*);
    8789    bool commonPseudoClassSelectorMatches(const Element*, const CSSSelector*) const;
    8890    bool linkMatchesVisitedPseudoClass(const Element*) const;
    8991    bool matchesFocusPseudoClass(const Element*) const;
     92    static bool fastCheckRightmostAttributeSelector(const Element*, const CSSSelector*);
     93    static bool checkExactAttribute(const Element*, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value);
    9094
    9195private:
     
    175179    return namespaceURI == starAtom || namespaceURI == element->namespaceURI();
    176180}
     181   
     182inline bool SelectorChecker::attributeNameMatches(const Attribute* attribute, const QualifiedName& selectorAttributeName)
     183{
     184    if (selectorAttributeName.localName() != attribute->localName())
     185        return false;
     186    return selectorAttributeName.prefix() == starAtom || selectorAttributeName.namespaceURI() == attribute->namespaceURI();
     187}
     188   
     189inline bool SelectorChecker::checkExactAttribute(const Element* element, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value)
     190{
     191    NamedNodeMap* attributeMap = element->attributeMap();
     192    if (!attributeMap)
     193        return false;
     194    unsigned size = attributeMap->length();
     195    for (unsigned i = 0; i < size; ++i) {
     196        Attribute* attribute = attributeMap->attributeItem(i);
     197        if (attributeNameMatches(attribute, selectorAttributeName) && (!value || attribute->value().impl() == value))
     198            return true;
     199    }
     200    return false;
     201}
     202   
     203inline bool SelectorChecker::fastCheckRightmostAttributeSelector(const Element* element, const CSSSelector* selector)
     204{
     205    if (selector->m_match == CSSSelector::Exact || selector->m_match == CSSSelector::Set)
     206        return checkExactAttribute(element, selector->attribute(), selector->value().impl());
     207    ASSERT(!selector->isAttributeSelector());
     208    return true;
     209}
    177210
    178211}
Note: See TracChangeset for help on using the changeset viewer.