Title: [96517] trunk/Source/WebCore
Revision
96517
Author
an...@apple.com
Date
2011-10-03 10:40:18 -0700 (Mon, 03 Oct 2011)

Log 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):

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (96516 => 96517)


--- trunk/Source/WebCore/ChangeLog	2011-10-03 17:37:05 UTC (rev 96516)
+++ trunk/Source/WebCore/ChangeLog	2011-10-03 17:40:18 UTC (rev 96517)
@@ -1,3 +1,43 @@
+2011-09-30  Antti Koivisto  <an...@apple.com>
+
+        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):
+
 2011-10-03  Mike Reed  <r...@google.com>
 
         respect other paint flags when setting flags for the font. No need to fiddle with DC(0) in paintSkiaText.

Modified: trunk/Source/WebCore/css/CSSSelector.cpp (96516 => 96517)


--- trunk/Source/WebCore/css/CSSSelector.cpp	2011-10-03 17:37:05 UTC (rev 96516)
+++ trunk/Source/WebCore/css/CSSSelector.cpp	2011-10-03 17:40:18 UTC (rev 96517)
@@ -580,7 +580,7 @@
         } else if (cs->m_match == CSSSelector::PseudoElement) {
             str += "::";
             str += cs->value();
-        } else if (cs->hasAttribute()) {
+        } else if (cs->isAttributeSelector()) {
             str += "[";
             const AtomicString& prefix = cs->attribute().prefix();
             if (!prefix.isNull()) {
@@ -640,18 +640,6 @@
     return str;
 }
 
-const QualifiedName& CSSSelector::attribute() const
-{ 
-    switch (m_match) {
-    case Id:
-        return idAttr;
-    case Class:
-        return classAttr;
-    default:
-        return m_hasRareData ? m_data.m_rareData->m_attribute : anyQName();
-    }
-}
-
 void CSSSelector::setAttribute(const QualifiedName& value) 
 { 
     createRareData(); 

Modified: trunk/Source/WebCore/css/CSSSelector.h (96516 => 96517)


--- trunk/Source/WebCore/css/CSSSelector.h	2011-10-03 17:37:05 UTC (rev 96516)
+++ trunk/Source/WebCore/css/CSSSelector.h	2011-10-03 17:40:18 UTC (rev 96517)
@@ -220,7 +220,6 @@
         CSSSelector* tagHistory() const { return m_isLastInTagHistory ? 0 : const_cast<CSSSelector*>(this + 1); }
 
         bool hasTag() const { return m_tag != anyQName(); }
-        bool hasAttribute() const { return m_match == Id || m_match == Class || (m_hasRareData && m_data.m_rareData->m_attribute != anyQName()); }
         
         const QualifiedName& tag() const { return m_tag; }
         // AtomicString is really just an AtomicStringImpl* so the cast below is safe.
@@ -297,6 +296,13 @@
         
         QualifiedName m_tag;
     };
+
+inline const QualifiedName& CSSSelector::attribute() const
+{ 
+    ASSERT(isAttributeSelector());
+    ASSERT(m_hasRareData);
+    return m_data.m_rareData->m_attribute;
+}
     
 inline bool CSSSelector::matchesPseudoElement() const 
 { 
@@ -330,8 +336,6 @@
 
 inline bool CSSSelector::isAttributeSelector() const
 {
-    if (!hasAttribute())
-        return false;
     return m_match == CSSSelector::Exact
         || m_match ==  CSSSelector::Set
         || m_match == CSSSelector::List

Modified: trunk/Source/WebCore/css/CSSSelectorList.cpp (96516 => 96517)


--- trunk/Source/WebCore/css/CSSSelectorList.cpp	2011-10-03 17:37:05 UTC (rev 96516)
+++ trunk/Source/WebCore/css/CSSSelectorList.cpp	2011-10-03 17:40:18 UTC (rev 96517)
@@ -142,7 +142,7 @@
     {
         if (selector->hasTag() && selector->tag().prefix() != nullAtom && selector->tag().prefix() != starAtom)
             return true;
-        if (selector->hasAttribute() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
+        if (selector->isAttributeSelector() && selector->attribute().prefix() != nullAtom && selector->attribute().prefix() != starAtom)
             return true;
         return false;
     }

Modified: trunk/Source/WebCore/css/CSSStyleSelector.cpp (96516 => 96517)


--- trunk/Source/WebCore/css/CSSStyleSelector.cpp	2011-10-03 17:37:05 UTC (rev 96516)
+++ trunk/Source/WebCore/css/CSSStyleSelector.cpp	2011-10-03 17:40:18 UTC (rev 96517)
@@ -1828,6 +1828,8 @@
                 return true;
         } else if (!SelectorChecker::tagMatches(m_element, ruleData.selector()))
             return false;
+        if (!SelectorChecker::fastCheckRightmostAttributeSelector(m_element, ruleData.selector()))
+            return false;
         return m_checker.fastCheckSelector(ruleData.selector(), m_element);
     }
 

Modified: trunk/Source/WebCore/css/SelectorChecker.cpp (96516 => 96517)


--- trunk/Source/WebCore/css/SelectorChecker.cpp	2011-10-03 17:37:05 UTC (rev 96516)
+++ trunk/Source/WebCore/css/SelectorChecker.cpp	2011-10-03 17:40:18 UTC (rev 96517)
@@ -63,6 +63,8 @@
 namespace WebCore {
     
 using namespace HTMLNames;
+    
+static bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr);
 
 SelectorChecker::SelectorChecker(Document* document, bool strictParsing)
     : m_document(document)
@@ -261,12 +263,13 @@
 
 namespace {
 
-template <bool checkValue(const Element*, AtomicStringImpl*)>
+template <bool checkValue(const Element*, AtomicStringImpl*, const QualifiedName&), bool initAttributeName>
 inline bool fastCheckSingleSelector(const CSSSelector*& selector, const Element*& element, const CSSSelector*& topChildOrSubselector, const Element*& topChildOrSubselectorMatchElement)
 {
     AtomicStringImpl* value = selector->value().impl();
+    const QualifiedName& attribute = initAttributeName ? selector->attribute() : anyQName();
     for (; element; element = element->parentElement()) {
-        if (checkValue(element, value) && SelectorChecker::tagMatches(element, selector)) {
+        if (checkValue(element, value, attribute) && SelectorChecker::tagMatches(element, selector)) {
             if (selector->relation() == CSSSelector::Descendant)
                 topChildOrSubselector = 0;
             else if (!topChildOrSubselector) {
@@ -296,18 +299,23 @@
     return false;
 }
 
-inline bool checkClassValue(const Element* element, AtomicStringImpl* value) 
+inline bool checkClassValue(const Element* element, AtomicStringImpl* value, const QualifiedName&) 
 {
     return element->hasClass() && static_cast<const StyledElement*>(element)->classNames().contains(value);
 }
 
-inline bool checkIDValue(const Element* element, AtomicStringImpl* value) 
+inline bool checkIDValue(const Element* element, AtomicStringImpl* value, const QualifiedName&) 
 {
     return element->hasID() && element->idForStyleResolution().impl() == value;
 }
 
-inline bool checkTagValue(const Element*, AtomicStringImpl*)
+inline bool checkExactAttributeValue(const Element* element, AtomicStringImpl* value, const QualifiedName& attributeName)
 {
+    return SelectorChecker::checkExactAttribute(element, attributeName, value);
+}
+
+inline bool checkTagValue(const Element*, AtomicStringImpl*, const QualifiedName&)
+{
     return true;
 }
     
@@ -323,9 +331,12 @@
     case CSSSelector::None:
         return true;
     case CSSSelector::Class:
-        return element->hasClass() && static_cast<const StyledElement*>(element)->classNames().contains(selector->value());
+        return checkClassValue(element, selector->value().impl(), anyQName());
     case CSSSelector::Id:
-        return element->hasID() && element->idForStyleResolution() == selector->value();
+        return checkIDValue(element, selector->value().impl(), anyQName());
+    case CSSSelector::Exact:
+    case CSSSelector::Set:
+        return checkExactAttributeValue(element, selector->value().impl(), selector->attribute());
     case CSSSelector::PseudoClass:
         return commonPseudoClassSelectorMatches(element, selector);
     default:
@@ -352,17 +363,22 @@
     while (selector) {
         switch (selector->m_match) {
         case CSSSelector::Class:
-            if (!fastCheckSingleSelector<checkClassValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
+            if (!fastCheckSingleSelector<checkClassValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
                 return false;
             break;
         case CSSSelector::Id:
-            if (!fastCheckSingleSelector<checkIDValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
+            if (!fastCheckSingleSelector<checkIDValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
                 return false;
             break;
         case CSSSelector::None:
-            if (!fastCheckSingleSelector<checkTagValue>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
+            if (!fastCheckSingleSelector<checkTagValue, false>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
                 return false;
             break;
+        case CSSSelector::Set:
+        case CSSSelector::Exact:
+            if (!fastCheckSingleSelector<checkExactAttributeValue, true>(selector, element, topChildOrSubselector, topChildOrSubselectorMatchElement))
+                return false;
+            break;
         default:
             ASSERT_NOT_REACHED();
         }
@@ -375,16 +391,20 @@
     return relation == CSSSelector::Descendant || relation == CSSSelector::Child || relation == CSSSelector::SubSelector;
 }
 
-static inline bool isFastCheckableMatch(CSSSelector::Match match)
+static inline bool isFastCheckableMatch(const CSSSelector* selector)
 {
-    return match == CSSSelector::None || match == CSSSelector::Id || match == CSSSelector::Class;
+    if (selector->m_match == CSSSelector::Set)
+        return true;
+    if (selector->m_match == CSSSelector::Exact)
+        return !htmlAttributeHasCaseInsensitiveValue(selector->attribute());
+    return selector->m_match == CSSSelector::None || selector->m_match == CSSSelector::Id || selector->m_match == CSSSelector::Class;
 }
 
 static inline bool isFastCheckableRightmostSelector(const CSSSelector* selector)
 {
     if (!isFastCheckableRelation(selector->relation()))
         return false;
-    return isFastCheckableMatch(static_cast<CSSSelector::Match>(selector->m_match)) || SelectorChecker::isCommonPseudoClassSelector(selector);
+    return isFastCheckableMatch(selector) || SelectorChecker::isCommonPseudoClassSelector(selector);
 }
 
 bool SelectorChecker::isFastCheckableSelector(const CSSSelector* selector)
@@ -394,7 +414,7 @@
     for (selector = selector->tagHistory(); selector; selector = selector->tagHistory()) {
         if (!isFastCheckableRelation(selector->relation()))
             return false;
-        if (!isFastCheckableMatch(static_cast<CSSSelector::Match>(selector->m_match)))
+        if (!isFastCheckableMatch(selector))
             return false;
     }
     return true;
@@ -578,21 +598,13 @@
     return attrSet;
 }
 
-static bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr)
+bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr)
 {
     static HashSet<AtomicStringImpl*>* htmlCaseInsensitiveAttributesSet = createHtmlCaseInsensitiveAttributesSet();
     bool isPossibleHTMLAttr = !attr.hasPrefix() && (attr.namespaceURI() == nullAtom);
     return isPossibleHTMLAttr && htmlCaseInsensitiveAttributesSet->contains(attr.localName().impl());
 }
 
-static bool attributeQualifiedNameMatches(Attribute* attribute, const QualifiedName& selectorAttr)
-{
-    if (selectorAttr.localName() != attribute->localName())
-        return false;
-    
-    return selectorAttr.prefix() == starAtom || selectorAttr.namespaceURI() == attribute->namespaceURI();
-}
-
 static bool attributeValueMatches(Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
 {
     const AtomicString& value = attributeItem->value();
@@ -661,7 +673,7 @@
     for (size_t i = 0; i < attributes->length(); ++i) {
         Attribute* attributeItem = attributes->attributeItem(i);
         
-        if (!attributeQualifiedNameMatches(attributeItem, selectorAttr))
+        if (!SelectorChecker::attributeNameMatches(attributeItem, selectorAttr))
             continue;
         
         if (attributeValueMatches(attributeItem, match, selectorValue, caseSensitive))
@@ -677,13 +689,13 @@
     if (!SelectorChecker::tagMatches(e, sel))
         return false;
     
-    if (sel->hasAttribute()) {
-        if (sel->m_match == CSSSelector::Class)
-            return e->hasClass() && static_cast<StyledElement*>(e)->classNames().contains(sel->value());
-        
-        if (sel->m_match == CSSSelector::Id)
-            return e->hasID() && e->idForStyleResolution() == sel->value();
-        
+    if (sel->m_match == CSSSelector::Class)
+        return e->hasClass() && static_cast<StyledElement*>(e)->classNames().contains(sel->value());
+    
+    if (sel->m_match == CSSSelector::Id)
+        return e->hasID() && e->idForStyleResolution() == sel->value();
+    
+    if (sel->isAttributeSelector()) {
         const QualifiedName& attr = sel->attribute();
         
         NamedNodeMap* attributes = e->attributes(true);

Modified: trunk/Source/WebCore/css/SelectorChecker.h (96516 => 96517)


--- trunk/Source/WebCore/css/SelectorChecker.h	2011-10-03 17:37:05 UTC (rev 96516)
+++ trunk/Source/WebCore/css/SelectorChecker.h	2011-10-03 17:40:18 UTC (rev 96517)
@@ -28,6 +28,7 @@
 #ifndef SelectorChecker_h
 #define SelectorChecker_h
 
+#include "Attribute.h"
 #include "CSSSelector.h"
 #include "Element.h"
 #include "InspectorInstrumentation.h"
@@ -83,10 +84,13 @@
     void clearHasUnknownPseudoElements() { m_hasUnknownPseudoElements = false; }
     
     static bool tagMatches(const Element*, const CSSSelector*);
+    static bool attributeNameMatches(const Attribute*, const QualifiedName&);
     static bool isCommonPseudoClassSelector(const CSSSelector*);
     bool commonPseudoClassSelectorMatches(const Element*, const CSSSelector*) const;
     bool linkMatchesVisitedPseudoClass(const Element*) const;
     bool matchesFocusPseudoClass(const Element*) const;
+    static bool fastCheckRightmostAttributeSelector(const Element*, const CSSSelector*);
+    static bool checkExactAttribute(const Element*, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value);
 
 private:
     bool checkOneSelector(CSSSelector*, Element*, PseudoId& dynamicPseudo, bool isSubSelector, bool encounteredLink, RenderStyle*, RenderStyle* elementParentStyle) const;
@@ -174,6 +178,35 @@
     const AtomicString& namespaceURI = selector->tag().namespaceURI();
     return namespaceURI == starAtom || namespaceURI == element->namespaceURI();
 }
+    
+inline bool SelectorChecker::attributeNameMatches(const Attribute* attribute, const QualifiedName& selectorAttributeName)
+{
+    if (selectorAttributeName.localName() != attribute->localName())
+        return false;
+    return selectorAttributeName.prefix() == starAtom || selectorAttributeName.namespaceURI() == attribute->namespaceURI();
+}
+    
+inline bool SelectorChecker::checkExactAttribute(const Element* element, const QualifiedName& selectorAttributeName, const AtomicStringImpl* value)
+{
+    NamedNodeMap* attributeMap = element->attributeMap();
+    if (!attributeMap)
+        return false;
+    unsigned size = attributeMap->length();
+    for (unsigned i = 0; i < size; ++i) {
+        Attribute* attribute = attributeMap->attributeItem(i);
+        if (attributeNameMatches(attribute, selectorAttributeName) && (!value || attribute->value().impl() == value))
+            return true;
+    }
+    return false;
+}
+    
+inline bool SelectorChecker::fastCheckRightmostAttributeSelector(const Element* element, const CSSSelector* selector)
+{
+    if (selector->m_match == CSSSelector::Exact || selector->m_match == CSSSelector::Set)
+        return checkExactAttribute(element, selector->attribute(), selector->value().impl());
+    ASSERT(!selector->isAttributeSelector());
+    return true;
+}
 
 }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
http://lists.webkit.org/mailman/listinfo.cgi/webkit-changes

Reply via email to