Bug 934788 - Faster selector matching for attribute selectors by not counting the number of attributes, r=bz
authorOlli Pettay <Olli.Pettay@helsinki.fi>
Tue, 05 Nov 2013 13:53:57 +0200
changeset 153614 d1ea27ce8789a308fd3483413686c13ba358b968
parent 153613 070eda49057771f25413371a71d15793216eaefa
child 153615 b7b720064c9d6f6e52de6027c6e2cb4172ccf6b6
push id25596
push userryanvm@gmail.com
push dateTue, 05 Nov 2013 20:28:59 +0000
treeherdermozilla-central@8b89e6626298 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbz
bugs934788
milestone28.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 934788 - Faster selector matching for attribute selectors by not counting the number of attributes, r=bz
content/base/public/Element.h
content/base/src/nsAttrAndChildArray.h
layout/style/nsCSSRuleProcessor.cpp
--- a/content/base/public/Element.h
+++ b/content/base/public/Element.h
@@ -533,16 +533,18 @@ protected:
       val->ToString(aResult);
       return true;
     }
     // else DOMString comes pre-emptied.
     return false;
   }
 
 public:
+  bool HasAttrs() const { return mAttrsAndChildren.HasAttrs(); }
+
   inline bool GetAttr(const nsAString& aName, DOMString& aResult) const
   {
     MOZ_ASSERT(aResult.HasStringBuffer() && aResult.StringBufferLength() == 0,
                "Should have empty string coming in");
     const nsAttrValue* val = mAttrsAndChildren.GetAttr(aName);
     if (val) {
       val->ToString(aResult);
       return true;
@@ -1185,17 +1187,17 @@ inline mozilla::dom::Element* nsINode::A
 inline const mozilla::dom::Element* nsINode::AsElement() const
 {
   MOZ_ASSERT(IsElement());
   return static_cast<const mozilla::dom::Element*>(this);
 }
 
 inline bool nsINode::HasAttributes() const
 {
-  return IsElement() && AsElement()->GetAttrCount() > 0;
+  return IsElement() && AsElement()->HasAttrs();
 }
 
 /**
  * Macros to implement Clone(). _elementName is the class for which to implement
  * Clone.
  */
 #define NS_IMPL_ELEMENT_CLONE(_elementName)                                 \
 nsresult                                                                    \
--- a/content/base/src/nsAttrAndChildArray.h
+++ b/content/base/src/nsAttrAndChildArray.h
@@ -66,16 +66,21 @@ public:
   }
   nsresult InsertChildAt(nsIContent* aChild, uint32_t aPos);
   void RemoveChildAt(uint32_t aPos);
   // Like RemoveChildAt but hands the reference to the child being
   // removed back to the caller instead of just releasing it.
   already_AddRefed<nsIContent> TakeChildAt(uint32_t aPos);
   int32_t IndexOfChild(const nsINode* aPossibleChild) const;
 
+  bool HasAttrs() const
+  {
+    return MappedAttrCount() || (AttrSlotCount() && AttrSlotIsTaken(0));
+  }
+
   uint32_t AttrCount() const;
   const nsAttrValue* GetAttr(nsIAtom* aLocalName,
                              int32_t aNamespaceID = kNameSpaceID_None) const;
   // As above but using a string attr name and always using
   // kNameSpaceID_None.  This is always case-sensitive.
   const nsAttrValue* GetAttr(const nsAString& aName) const;
   // Get an nsAttrValue by qualified name.  Can optionally do
   // ASCII-case-insensitive name matching.
--- a/layout/style/nsCSSRuleProcessor.cpp
+++ b/layout/style/nsCSSRuleProcessor.cpp
@@ -2133,18 +2133,17 @@ static bool SelectorMatches(Element* aEl
         }
       }
     }
   }
 
   bool result = true;
   if (aSelector->mAttrList) {
     // test for attribute match
-    uint32_t attrCount = aElement->GetAttrCount();
-    if (attrCount == 0) {
+    if (!aElement->HasAttrs()) {
       // if no attributes on the content, no match
       return false;
     } else {
       result = true;
       nsAttrSelector* attr = aSelector->mAttrList;
       nsIAtom* matchAttribute;
 
       do {
@@ -2155,19 +2154,18 @@ static bool SelectorMatches(Element* aEl
           // Attr selector with a wildcard namespace.  We have to examine all
           // the attributes on our content node....  This sort of selector is
           // essentially a boolean OR, over all namespaces, of equivalent attr
           // selectors with those namespaces.  So to evaluate whether it
           // matches, evaluate for each namespace (the only namespaces that
           // have a chance at matching, of course, are ones that the element
           // actually has attributes in), short-circuiting if we ever match.
           result = false;
-          for (uint32_t i = 0; i < attrCount; ++i) {
-            const nsAttrName* attrName = aElement->GetAttrNameAt(i);
-            NS_ASSERTION(attrName, "GetAttrCount lied or GetAttrNameAt failed");
+          const nsAttrName* attrName;
+          for (uint32_t i = 0; (attrName = aElement->GetAttrNameAt(i)); ++i) {
             if (attrName->LocalName() != matchAttribute) {
               continue;
             }
             if (attr->mFunction == NS_ATTR_FUNC_SET) {
               result = true;
             } else {
               nsAutoString value;
 #ifdef DEBUG