Move attribute storage from NamedNodeMap to ElementAttributeData
authorcaio.oliveira@openbossa.org <caio.oliveira@openbossa.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Feb 2012 15:02:44 +0000 (15:02 +0000)
committercaio.oliveira@openbossa.org <caio.oliveira@openbossa.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 13 Feb 2012 15:02:44 +0000 (15:02 +0000)
https://bugs.webkit.org/show_bug.cgi?id=77674

Reviewed by Andreas Kling.

Move m_attributes vector from NamedNodeMap to ElementAttributeData. Make the
remaining callsites interact with ElementAttributeData if possible. The parsing
code is still interacting with NamedNodeMap, so some functions remained as
wrappers there. A next change will it use ElementAttributeData instead.

The code for DOM exported functions remained in NamedNodeMap. This implementation
should move to Element in a next change, too.

* dom/Attr.h:
(Attr):
* dom/Element.cpp:
(WebCore::Element::setAttribute):
(WebCore::Element::setAttributeInternal):
(WebCore::Element::parserSetAttributeMap):
(WebCore::Element::removeAttribute):
(WebCore::Element::hasAttribute):
(WebCore::Element::hasAttributeNS):
(WebCore::Element::normalizeAttributes):
* dom/Element.h:
(Element):
(WebCore::Element::ensureUpdatedAttributes):
(WebCore::Element::ensureAttributeData):
(WebCore):
(WebCore::Element::updatedAttributeData):
(WebCore::Element::ensureUpdatedAttributeData):
These *AttributeData functions are the correct correct way for callers to touch
the details of attribute storage. The "updated" variants will update invalid
attributes before return.

(WebCore::Element::setAttributesFromElement):
* dom/ElementAttributeData.cpp:
(WebCore::ElementAttributeData::~ElementAttributeData):
(WebCore):
(WebCore::ElementAttributeData::ensureInlineStyleDecl):
(WebCore::ElementAttributeData::addAttribute):
(WebCore::ElementAttributeData::removeAttribute):
(WebCore::ElementAttributeData::detachAttributesFromElement):
(WebCore::ElementAttributeData::copyAttributesToVector):
(WebCore::ElementAttributeData::getAttributeItemIndexSlowCase):
(WebCore::ElementAttributeData::setAttributes): Make use of the assumption that
this will only be called with a valid Element, since the only call site is an
Element method.
(WebCore::ElementAttributeData::clearAttributes):
(WebCore::ElementAttributeData::replaceAttribute): Make use of the assumption
this will only be called with a valid Element, since the only call site return
early if there's no Element.
* dom/ElementAttributeData.h:
(ElementAttributeData):
(WebCore::ElementAttributeData::length):
(WebCore::ElementAttributeData::isEmpty):
(WebCore::ElementAttributeData::attributeItem):
(WebCore::ElementAttributeData::removeAttribute):
(WebCore):
(WebCore::ElementAttributeData::getAttributeItem):
(WebCore::ElementAttributeData::getAttributeItemIndex):
* dom/NamedNodeMap.cpp:
(WebCore::NamedNodeMap::getNamedItem):
(WebCore::NamedNodeMap::removeNamedItem):
(WebCore::NamedNodeMap::setNamedItem):
(WebCore::NamedNodeMap::item):
(WebCore::NamedNodeMap::detachFromElement):
* dom/NamedNodeMap.h:
(WebCore::NamedNodeMap::length):
(WebCore::NamedNodeMap::isEmpty):
(WebCore::NamedNodeMap::attributeItem):
(WebCore::NamedNodeMap::getAttributeItem):
(WebCore::NamedNodeMap::getAttributeItemIndex):
(WebCore::NamedNodeMap::shrinkToLength):
(WebCore::NamedNodeMap::reserveInitialCapacity):
(WebCore::NamedNodeMap::addAttribute):
(WebCore::NamedNodeMap::removeAttribute):
(NamedNodeMap):
* dom/StyledElement.cpp:
(WebCore::StyledElement::updateAttributeStyle):
* dom/StyledElement.h:
(WebCore::StyledElement::ensureInlineStyleDecl):
* html/HTMLInputElement.cpp:
(WebCore::HTMLInputElement::updateType):
* svg/properties/SVGAnimatedPropertySynchronizer.h:
* xml/parser/XMLDocumentParserQt.cpp:
(WebCore::XMLDocumentParser::XMLDocumentParser):

git-svn-id: http://svn.webkit.org/repository/webkit/trunk@107570 268f45cc-cd09-0410-ab3c-d52691b4dbfc

13 files changed:
Source/WebCore/ChangeLog
Source/WebCore/dom/Attr.h
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/dom/ElementAttributeData.cpp
Source/WebCore/dom/ElementAttributeData.h
Source/WebCore/dom/NamedNodeMap.cpp
Source/WebCore/dom/NamedNodeMap.h
Source/WebCore/dom/StyledElement.cpp
Source/WebCore/dom/StyledElement.h
Source/WebCore/html/HTMLInputElement.cpp
Source/WebCore/svg/properties/SVGAnimatedPropertySynchronizer.h
Source/WebCore/xml/parser/XMLDocumentParserQt.cpp

index 1fa88ea..1d7de59 100644 (file)
@@ -1,3 +1,92 @@
+2012-02-13  Caio Marcelo de Oliveira Filho  <caio.oliveira@openbossa.org>
+
+        Move attribute storage from NamedNodeMap to ElementAttributeData
+        https://bugs.webkit.org/show_bug.cgi?id=77674
+
+        Reviewed by Andreas Kling.
+
+        Move m_attributes vector from NamedNodeMap to ElementAttributeData. Make the
+        remaining callsites interact with ElementAttributeData if possible. The parsing
+        code is still interacting with NamedNodeMap, so some functions remained as
+        wrappers there. A next change will it use ElementAttributeData instead.
+
+        The code for DOM exported functions remained in NamedNodeMap. This implementation
+        should move to Element in a next change, too.
+
+        * dom/Attr.h:
+        (Attr):
+        * dom/Element.cpp:
+        (WebCore::Element::setAttribute):
+        (WebCore::Element::setAttributeInternal):
+        (WebCore::Element::parserSetAttributeMap):
+        (WebCore::Element::removeAttribute):
+        (WebCore::Element::hasAttribute):
+        (WebCore::Element::hasAttributeNS):
+        (WebCore::Element::normalizeAttributes):
+        * dom/Element.h:
+        (Element):
+        (WebCore::Element::ensureUpdatedAttributes):
+        (WebCore::Element::ensureAttributeData):
+        (WebCore):
+        (WebCore::Element::updatedAttributeData):
+        (WebCore::Element::ensureUpdatedAttributeData):
+        These *AttributeData functions are the correct correct way for callers to touch
+        the details of attribute storage. The "updated" variants will update invalid
+        attributes before return.
+
+        (WebCore::Element::setAttributesFromElement):
+        * dom/ElementAttributeData.cpp:
+        (WebCore::ElementAttributeData::~ElementAttributeData):
+        (WebCore):
+        (WebCore::ElementAttributeData::ensureInlineStyleDecl):
+        (WebCore::ElementAttributeData::addAttribute):
+        (WebCore::ElementAttributeData::removeAttribute):
+        (WebCore::ElementAttributeData::detachAttributesFromElement):
+        (WebCore::ElementAttributeData::copyAttributesToVector):
+        (WebCore::ElementAttributeData::getAttributeItemIndexSlowCase):
+        (WebCore::ElementAttributeData::setAttributes): Make use of the assumption that
+        this will only be called with a valid Element, since the only call site is an
+        Element method.
+        (WebCore::ElementAttributeData::clearAttributes):
+        (WebCore::ElementAttributeData::replaceAttribute): Make use of the assumption
+        this will only be called with a valid Element, since the only call site return
+        early if there's no Element.
+        * dom/ElementAttributeData.h:
+        (ElementAttributeData):
+        (WebCore::ElementAttributeData::length):
+        (WebCore::ElementAttributeData::isEmpty):
+        (WebCore::ElementAttributeData::attributeItem):
+        (WebCore::ElementAttributeData::removeAttribute):
+        (WebCore):
+        (WebCore::ElementAttributeData::getAttributeItem):
+        (WebCore::ElementAttributeData::getAttributeItemIndex):
+        * dom/NamedNodeMap.cpp:
+        (WebCore::NamedNodeMap::getNamedItem):
+        (WebCore::NamedNodeMap::removeNamedItem):
+        (WebCore::NamedNodeMap::setNamedItem):
+        (WebCore::NamedNodeMap::item):
+        (WebCore::NamedNodeMap::detachFromElement):
+        * dom/NamedNodeMap.h:
+        (WebCore::NamedNodeMap::length):
+        (WebCore::NamedNodeMap::isEmpty):
+        (WebCore::NamedNodeMap::attributeItem):
+        (WebCore::NamedNodeMap::getAttributeItem):
+        (WebCore::NamedNodeMap::getAttributeItemIndex):
+        (WebCore::NamedNodeMap::shrinkToLength):
+        (WebCore::NamedNodeMap::reserveInitialCapacity):
+        (WebCore::NamedNodeMap::addAttribute):
+        (WebCore::NamedNodeMap::removeAttribute):
+        (NamedNodeMap):
+        * dom/StyledElement.cpp:
+        (WebCore::StyledElement::updateAttributeStyle):
+        * dom/StyledElement.h:
+        (WebCore::StyledElement::ensureInlineStyleDecl):
+        * html/HTMLInputElement.cpp:
+        (WebCore::HTMLInputElement::updateType):
+        * svg/properties/SVGAnimatedPropertySynchronizer.h:
+        * xml/parser/XMLDocumentParserQt.cpp:
+        (WebCore::XMLDocumentParser::XMLDocumentParser):
+
 2012-02-13  Alexei Filippov  <alexeif@chromium.org>
 
         Web Inspector: wrong percent calculations for empty snapshot.
index 0209f90..b703574 100644 (file)
@@ -39,6 +39,7 @@ class CSSStyleDeclaration;
 // destruction. however, this is not yet implemented.
 
 class Attr : public ContainerNode {
+    friend class ElementAttributeData;
     friend class NamedNodeMap;
 public:
     static PassRefPtr<Attr> create(Element*, Document*, PassRefPtr<Attribute>);
index 4c5e41c..452898a 100644 (file)
@@ -616,27 +616,28 @@ void Element::setAttribute(const AtomicString& name, const AtomicString& value,
 
     const AtomicString& localName = shouldIgnoreAttributeCase(this) ? name.lower() : name;
 
-    size_t index = ensureUpdatedAttributes()->getAttributeItemIndex(localName, false);
-    const QualifiedName& qName = index != notFound ? m_attributeMap->attributeItem(index)->name() : QualifiedName(nullAtom, localName, nullAtom);
+    size_t index = ensureUpdatedAttributeData()->getAttributeItemIndex(localName, false);
+    const QualifiedName& qName = index != notFound ? attributeItem(index)->name() : QualifiedName(nullAtom, localName, nullAtom);
     setAttributeInternal(index, qName, value);
 }
 
 void Element::setAttribute(const QualifiedName& name, const AtomicString& value)
 {
-    setAttributeInternal(ensureUpdatedAttributes()->getAttributeItemIndex(name), name, value);
+    setAttributeInternal(ensureUpdatedAttributeData()->getAttributeItemIndex(name), name, value);
 }
 
 inline void Element::setAttributeInternal(size_t index, const QualifiedName& name, const AtomicString& value)
 {
-    Attribute* old = index != notFound ? m_attributeMap->attributeItem(index) : 0;
+    ElementAttributeData* attributeData = &m_attributeMap->m_attributeData;
+    Attribute* old = index != notFound ? attributeData->attributeItem(index) : 0;
     if (value.isNull()) {
         if (old)
-            m_attributeMap->removeAttribute(index);
+            attributeData->removeAttribute(index, this);
         return;
     }
 
     if (!old) {
-        m_attributeMap->addAttribute(createAttribute(name, value));
+        attributeData->addAttribute(createAttribute(name, value), this);
         return;
     }
 
@@ -750,27 +751,28 @@ void Element::parserSetAttributeMap(PassOwnPtr<NamedNodeMap> list, FragmentScrip
     m_attributeMap = list;
 
     if (m_attributeMap) {
+        ElementAttributeData* attributeData = &m_attributeMap->m_attributeData;
         m_attributeMap->m_element = this;
         // If the element is created as result of a paste or drag-n-drop operation
         // we want to remove all the script and event handlers.
         if (scriptingPermission == FragmentScriptingNotAllowed) {
             unsigned i = 0;
             while (i < m_attributeMap->length()) {
-                const QualifiedName& attributeName = m_attributeMap->m_attributes[i]->name();
+                const QualifiedName& attributeName = attributeData->m_attributes[i]->name();
                 if (isEventHandlerAttribute(attributeName)) {
-                    m_attributeMap->m_attributes.remove(i);
+                    attributeData->m_attributes.remove(i);
                     continue;
                 }
 
-                if (isAttributeToRemove(attributeName, m_attributeMap->m_attributes[i]->value()))
-                    m_attributeMap->m_attributes[i]->setValue(nullAtom);
+                if (isAttributeToRemove(attributeName, attributeData->m_attributes[i]->value()))
+                    attributeData->m_attributes[i]->setValue(nullAtom);
                 i++;
             }
         }
         // Store the set of attributes that changed on the stack in case
         // attributeChanged mutates m_attributeMap.
         Vector<RefPtr<Attribute> > attributes;
-        m_attributeMap->copyAttributesToVector(attributes);
+        attributeData->copyAttributesToVector(attributes);
         for (Vector<RefPtr<Attribute> >::iterator iter = attributes.begin(); iter != attributes.end(); ++iter)
             attributeChanged(iter->get());
     }
@@ -1491,15 +1493,16 @@ void Element::setAttributeNS(const AtomicString& namespaceURI, const AtomicStrin
 
 void Element::removeAttribute(const String& name)
 {
-    if (!m_attributeMap)
+    ElementAttributeData* attributeData = this->attributeData();
+    if (!attributeData)
         return;
 
     String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name;
-    size_t index = m_attributeMap->getAttributeItemIndex(localName, false);
+    size_t index = attributeData->getAttributeItemIndex(localName, false);
     if (index == notFound)
         return;
 
-    m_attributeMap->removeAttribute(index);
+    attributeData->removeAttribute(index, this);
 }
 
 void Element::removeAttributeNS(const String& namespaceURI, const String& localName)
@@ -1526,22 +1529,22 @@ PassRefPtr<Attr> Element::getAttributeNodeNS(const String& namespaceURI, const S
 
 bool Element::hasAttribute(const String& name) const
 {
-    NamedNodeMap* attrs = updatedAttributes();
-    if (!attrs)
+    ElementAttributeData* attributeData = updatedAttributeData();
+    if (!attributeData)
         return false;
 
     // This call to String::lower() seems to be required but
     // there may be a way to remove it.
     String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name;
-    return attrs->getAttributeItem(localName, false);
+    return attributeData->getAttributeItem(localName, false);
 }
 
 bool Element::hasAttributeNS(const String& namespaceURI, const String& localName) const
 {
-    NamedNodeMap* attrs = updatedAttributes();
-    if (!attrs)
+    ElementAttributeData* attributeData = updatedAttributeData();
+    if (!attributeData)
         return false;
-    return attrs->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI));
+    return attributeData->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI));
 }
 
 CSSStyleDeclaration *Element::style()
@@ -1728,15 +1731,12 @@ void Element::cancelFocusAppearanceUpdate()
 
 void Element::normalizeAttributes()
 {
-    NamedNodeMap* attrs = updatedAttributes();
-    if (!attrs)
-        return;
-
-    if (attrs->isEmpty())
+    ElementAttributeData* attributeData = updatedAttributeData();
+    if (!attributeData || attributeData->isEmpty())
         return;
 
     Vector<RefPtr<Attribute> > attributeVector;
-    attrs->copyAttributesToVector(attributeVector);
+    attributeData->copyAttributesToVector(attributeVector);
     size_t numAttrs = attributeVector.size();
     for (size_t i = 0; i < numAttrs; ++i) {
         if (Attr* attr = attributeVector[i]->attr())
index 16fd567..2dd63e1 100644 (file)
@@ -239,14 +239,10 @@ public:
     // Only called by the parser immediately after element construction.
     void parserSetAttributeMap(PassOwnPtr<NamedNodeMap>, FragmentScriptingPermission);
 
-    NamedNodeMap* attributeMap() const { return m_attributeMap.get(); }
-    NamedNodeMap* ensureAttributeMap() const;
-
     ElementAttributeData* attributeData() const { return m_attributeMap ? m_attributeMap->attributeData() : 0; }
-    ElementAttributeData* ensureAttributeData() const { return ensureUpdatedAttributes()->attributeData(); }
-
-    // FIXME: This method should be removed once AttributeData is moved to Element.
-    ElementAttributeData* ensureAttributeDataWithoutUpdate() const { return ensureAttributeMap()->attributeData(); }
+    ElementAttributeData* ensureAttributeData() const;
+    ElementAttributeData* updatedAttributeData() const;
+    ElementAttributeData* ensureUpdatedAttributeData() const;
 
     void setAttributesFromElement(const Element&);
 
@@ -547,7 +543,9 @@ inline Element* Element::nextElementSibling() const
 inline NamedNodeMap* Element::ensureUpdatedAttributes() const
 {
     updateInvalidAttributes();
-    return ensureAttributeMap();
+    if (!m_attributeMap)
+        createAttributeMap();
+    return m_attributeMap.get();
 }
 
 inline NamedNodeMap* Element::updatedAttributes() const
@@ -556,10 +554,29 @@ inline NamedNodeMap* Element::updatedAttributes() const
     return m_attributeMap.get();
 }
 
+inline ElementAttributeData* Element::ensureAttributeData() const
+{
+    if (!m_attributeMap)
+        createAttributeMap();
+    return m_attributeMap->attributeData();
+}
+
+inline ElementAttributeData* Element::updatedAttributeData() const
+{
+    updateInvalidAttributes();
+    return attributeData();
+}
+
+inline ElementAttributeData* Element::ensureUpdatedAttributeData() const
+{
+    updateInvalidAttributes();
+    return ensureAttributeData();
+}
+
 inline void Element::setAttributesFromElement(const Element& other)
 {
-    if (NamedNodeMap* attributeMap = other.updatedAttributes())
-        ensureUpdatedAttributes()->setAttributes(*attributeMap);
+    if (ElementAttributeData* attributeData = other.updatedAttributeData())
+        ensureUpdatedAttributeData()->setAttributes(*attributeData, this);
 }
 
 inline void Element::updateName(const AtomicString& oldName, const AtomicString& newName)
@@ -673,13 +690,6 @@ inline void Element::removeAttribute(unsigned index)
     m_attributeMap->removeAttribute(index);
 }
 
-inline NamedNodeMap* Element::ensureAttributeMap() const
-{
-    if (!m_attributeMap)
-        createAttributeMap();
-    return m_attributeMap.get();
-}
-
 inline void Element::updateInvalidAttributes() const
 {
     if (!isStyleAttributeValid())
index 4d1b639..dc2799f 100644 (file)
 #include "config.h"
 #include "ElementAttributeData.h"
 
+#include "Attr.h"
 #include "StyledElement.h"
 
 namespace WebCore {
 
+ElementAttributeData::~ElementAttributeData()
+{
+    detachAttributesFromElement();
+}
+
 void ElementAttributeData::setClass(const String& className, bool shouldFoldCase)
 {
     m_classNames.set(className, shouldFoldCase);
 }
 
-StylePropertySet* ElementAttributeData::ensureInlineStyleDecl(Element* element)
+StylePropertySet* ElementAttributeData::ensureInlineStyleDecl(StyledElement* element)
 {
     if (!m_inlineStyleDecl) {
         ASSERT(element->isStyledElement());
@@ -53,4 +59,126 @@ void ElementAttributeData::destroyInlineStyleDecl()
     m_inlineStyleDecl = 0;
 }
 
+void ElementAttributeData::addAttribute(PassRefPtr<Attribute> prpAttribute, Element* element)
+{
+    RefPtr<Attribute> attribute = prpAttribute;
+
+    if (element)
+        element->willModifyAttribute(attribute->name(), nullAtom, attribute->value());
+
+    m_attributes.append(attribute);
+    if (Attr* attr = attribute->attr())
+        attr->m_element = element;
+
+    if (element)
+        element->didModifyAttribute(attribute.get());
+}
+
+void ElementAttributeData::removeAttribute(size_t index, Element* element)
+{
+    ASSERT(index < length());
+
+    RefPtr<Attribute> attribute = m_attributes[index];
+
+    if (element)
+        element->willRemoveAttribute(attribute->name(), attribute->value());
+
+    if (Attr* attr = attribute->attr())
+        attr->m_element = 0;
+    m_attributes.remove(index);
+
+    if (element)
+        element->didRemoveAttribute(attribute.get());
+}
+
+void ElementAttributeData::detachAttributesFromElement()
+{
+    size_t size = m_attributes.size();
+    for (size_t i = 0; i < size; i++) {
+        if (Attr* attr = m_attributes[i]->attr())
+            attr->m_element = 0;
+    }
+}
+
+void ElementAttributeData::copyAttributesToVector(Vector<RefPtr<Attribute> >& copy)
+{
+    copy = m_attributes;
+}
+
+size_t ElementAttributeData::getAttributeItemIndexSlowCase(const String& name, bool shouldIgnoreAttributeCase) const
+{
+    unsigned len = length();
+
+    // Continue to checking case-insensitively and/or full namespaced names if necessary:
+    for (unsigned i = 0; i < len; ++i) {
+        const QualifiedName& attrName = m_attributes[i]->name();
+        if (!attrName.hasPrefix()) {
+            if (shouldIgnoreAttributeCase && equalIgnoringCase(name, attrName.localName()))
+                return i;
+        } else {
+            // FIXME: Would be faster to do this comparison without calling toString, which
+            // generates a temporary string by concatenation. But this branch is only reached
+            // if the attribute name has a prefix, which is rare in HTML.
+            if (equalPossiblyIgnoringCase(name, attrName.toString(), shouldIgnoreAttributeCase))
+                return i;
+        }
+    }
+    return notFound;
+}
+
+void ElementAttributeData::setAttributes(const ElementAttributeData& other, Element* element)
+{
+    ASSERT(element);
+
+    // If assigning the map changes the id attribute, we need to call
+    // updateId.
+    Attribute* oldId = getAttributeItem(element->document()->idAttributeName());
+    Attribute* newId = other.getAttributeItem(element->document()->idAttributeName());
+
+    if (oldId || newId)
+        element->updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom);
+
+    Attribute* oldName = getAttributeItem(HTMLNames::nameAttr);
+    Attribute* newName = other.getAttributeItem(HTMLNames::nameAttr);
+
+    if (oldName || newName)
+        element->updateName(oldName ? oldName->value() : nullAtom, newName ? newName->value() : nullAtom);
+
+    clearAttributes();
+    unsigned newLength = other.length();
+    m_attributes.resize(newLength);
+
+    // FIXME: These loops can probably be combined.
+    for (unsigned i = 0; i < newLength; i++)
+        m_attributes[i] = other.m_attributes[i]->clone();
+    for (unsigned i = 0; i < newLength; i++)
+        element->attributeChanged(m_attributes[i].get());
+}
+
+void ElementAttributeData::clearAttributes()
+{
+    clearClass();
+    detachAttributesFromElement();
+    m_attributes.clear();
+}
+
+void ElementAttributeData::replaceAttribute(size_t index, PassRefPtr<Attribute> prpAttribute, Element* element)
+{
+    ASSERT(element);
+    ASSERT(index < length());
+
+    RefPtr<Attribute> attribute = prpAttribute;
+    Attribute* old = m_attributes[index].get();
+
+    element->willModifyAttribute(attribute->name(), old->value(), attribute->value());
+
+    if (Attr* attr = old->attr())
+        attr->m_element = 0;
+    m_attributes[index] = attribute;
+    if (Attr* attr = attribute->attr())
+        attr->m_element = element;
+
+    element->didModifyAttribute(attribute.get());
+}
+
 }
index 8203d2f..76d96fd 100644 (file)
 #ifndef ElementAttributeData_h
 #define ElementAttributeData_h
 
+#include "Attribute.h"
 #include "SpaceSplitString.h"
 #include "StylePropertySet.h"
+#include <wtf/NotFound.h>
 
 namespace WebCore {
 
@@ -35,6 +37,8 @@ class Element;
 
 class ElementAttributeData {
 public:
+    ~ElementAttributeData();
+
     void clearClass() { m_classNames.clear(); }
     void setClass(const String& className, bool shouldFoldCase);
     const SpaceSplitString& classNames() const { return m_classNames; }
@@ -43,25 +47,106 @@ public:
     void setIdForStyleResolution(const AtomicString& newId) { m_idForStyleResolution = newId; }
 
     StylePropertySet* inlineStyleDecl() { return m_inlineStyleDecl.get(); }
-    StylePropertySet* ensureInlineStyleDecl(Element*);
+    StylePropertySet* ensureInlineStyleDecl(StyledElement*);
     void destroyInlineStyleDecl();
 
     StylePropertySet* attributeStyle() const { return m_attributeStyle.get(); }
     void setAttributeStyle(PassRefPtr<StylePropertySet> style) { m_attributeStyle = style; }
 
+    size_t length() const { return m_attributes.size(); }
+    bool isEmpty() const { return m_attributes.isEmpty(); }
+
+    // Internal interface.
+    Attribute* attributeItem(unsigned index) const { return m_attributes[index].get(); }
+    Attribute* getAttributeItem(const QualifiedName&) const;
+    size_t getAttributeItemIndex(const QualifiedName&) const;
+
+    // These functions do no error checking.
+    void addAttribute(PassRefPtr<Attribute>, Element*);
+    void removeAttribute(const QualifiedName&, Element*);
+    void removeAttribute(size_t index, Element*);
+
 private:
+    friend class Element;
     friend class NamedNodeMap;
 
     ElementAttributeData()
     {
     }
 
+    void detachAttributesFromElement();
+    void copyAttributesToVector(Vector<RefPtr<Attribute> >&);
+    Attribute* getAttributeItem(const String& name, bool shouldIgnoreAttributeCase) const;
+    size_t getAttributeItemIndex(const String& name, bool shouldIgnoreAttributeCase) const;
+    size_t getAttributeItemIndexSlowCase(const String& name, bool shouldIgnoreAttributeCase) const;
+    void setAttributes(const ElementAttributeData& other, Element*);
+    void clearAttributes();
+    void replaceAttribute(size_t index, PassRefPtr<Attribute>, Element*);
+
     RefPtr<StylePropertySet> m_inlineStyleDecl;
     RefPtr<StylePropertySet> m_attributeStyle;
     SpaceSplitString m_classNames;
     AtomicString m_idForStyleResolution;
+    Vector<RefPtr<Attribute>, 4> m_attributes;
 };
 
+inline void ElementAttributeData::removeAttribute(const QualifiedName& name, Element* element)
+{
+    size_t index = getAttributeItemIndex(name);
+    if (index == notFound)
+        return;
+
+    removeAttribute(index, element);
+}
+
+inline Attribute* ElementAttributeData::getAttributeItem(const String& name, bool shouldIgnoreAttributeCase) const
+{
+    size_t index = getAttributeItemIndex(name, shouldIgnoreAttributeCase);
+    if (index != notFound)
+        return m_attributes[index].get();
+    return 0;
+}
+
+inline Attribute* ElementAttributeData::getAttributeItem(const QualifiedName& name) const
+{
+    size_t index = getAttributeItemIndex(name);
+    if (index != notFound)
+        return m_attributes[index].get();
+    return 0;
+}
+
+// We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller
+// can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not).
+inline size_t ElementAttributeData::getAttributeItemIndex(const QualifiedName& name) const
+{
+    size_t len = length();
+    for (unsigned i = 0; i < len; ++i) {
+        if (m_attributes[i]->name().matches(name))
+            return i;
+    }
+    return notFound;
+}
+
+inline size_t ElementAttributeData::getAttributeItemIndex(const String& name, bool shouldIgnoreAttributeCase) const
+{
+    unsigned len = length();
+    bool doSlowCheck = shouldIgnoreAttributeCase;
+
+    // Optimize for the case where the attribute exists and its name exactly matches.
+    for (unsigned i = 0; i < len; ++i) {
+        const QualifiedName& attrName = m_attributes[i]->name();
+        if (!attrName.hasPrefix()) {
+            if (name == attrName.localName())
+                return i;
+        } else
+            doSlowCheck = true;
+    }
+
+    if (doSlowCheck)
+        return getAttributeItemIndexSlowCase(name, shouldIgnoreAttributeCase);
+    return notFound;
+}
+
 }
 
 #endif // ElementAttributeData_h
index 0a32a66..4714597 100644 (file)
@@ -40,15 +40,6 @@ static inline bool shouldIgnoreAttributeCase(const Element* e)
     return e && e->document()->isHTMLDocument() && e->isHTMLElement();
 }
 
-inline void NamedNodeMap::detachAttributesFromElement()
-{
-    size_t size = m_attributes.size();
-    for (size_t i = 0; i < size; i++) {
-        if (Attr* attr = m_attributes[i]->attr())
-            attr->m_element = 0;
-    }
-}
-
 void NamedNodeMap::ref()
 {
     ASSERT(m_element);
@@ -61,14 +52,9 @@ void NamedNodeMap::deref()
     m_element->deref();
 }
 
-NamedNodeMap::~NamedNodeMap()
-{
-    detachAttributesFromElement();
-}
-
 PassRefPtr<Node> NamedNodeMap::getNamedItem(const String& name) const
 {
-    Attribute* a = getAttributeItem(name, shouldIgnoreAttributeCase(m_element));
+    Attribute* a = m_attributeData.getAttributeItem(name, shouldIgnoreAttributeCase(m_element));
     if (!a)
         return 0;
     
@@ -82,7 +68,7 @@ PassRefPtr<Node> NamedNodeMap::getNamedItemNS(const String& namespaceURI, const
 
 PassRefPtr<Node> NamedNodeMap::removeNamedItem(const String& name, ExceptionCode& ec)
 {
-    Attribute* a = getAttributeItem(name, shouldIgnoreAttributeCase(m_element));
+    Attribute* a = m_attributeData.getAttributeItem(name, shouldIgnoreAttributeCase(m_element));
     if (!a) {
         ec = NOT_FOUND_ERR;
         return 0;
@@ -135,9 +121,9 @@ PassRefPtr<Node> NamedNodeMap::setNamedItem(Node* node, ExceptionCode& ec)
     RefPtr<Attr> oldAttr;
     if (oldAttribute) {
         oldAttr = oldAttribute->createAttrIfNeeded(m_element);
-        replaceAttribute(index, attribute);
+        m_attributeData.replaceAttribute(index, attribute, m_element);
     } else
-        addAttribute(attribute);
+        m_attributeData.addAttribute(attribute, m_element);
 
     return oldAttr.release();
 }
@@ -157,7 +143,7 @@ PassRefPtr<Node> NamedNodeMap::removeNamedItem(const QualifiedName& name, Except
         return 0;
     }
 
-    RefPtr<Attr> attr = m_attributes[index]->createAttrIfNeeded(m_element);
+    RefPtr<Attr> attr = m_attributeData.m_attributes[index]->createAttrIfNeeded(m_element);
 
     removeAttribute(index);
 
@@ -169,40 +155,7 @@ PassRefPtr<Node> NamedNodeMap::item(unsigned index) const
     if (index >= length())
         return 0;
 
-    return m_attributes[index]->createAttrIfNeeded(m_element);
-}
-
-void NamedNodeMap::copyAttributesToVector(Vector<RefPtr<Attribute> >& copy)
-{
-    copy = m_attributes;
-}
-
-size_t NamedNodeMap::getAttributeItemIndexSlowCase(const String& name, bool shouldIgnoreAttributeCase) const
-{
-    unsigned len = length();
-
-    // Continue to checking case-insensitively and/or full namespaced names if necessary:
-    for (unsigned i = 0; i < len; ++i) {
-        const QualifiedName& attrName = m_attributes[i]->name();
-        if (!attrName.hasPrefix()) {
-            if (shouldIgnoreAttributeCase && equalIgnoringCase(name, attrName.localName()))
-                return i;
-        } else {
-            // FIXME: Would be faster to do this comparison without calling toString, which
-            // generates a temporary string by concatenation. But this branch is only reached
-            // if the attribute name has a prefix, which is rare in HTML.
-            if (equalPossiblyIgnoringCase(name, attrName.toString(), shouldIgnoreAttributeCase))
-                return i;
-        }
-    }
-    return notFound;
-}
-
-void NamedNodeMap::clearAttributes()
-{
-    attributeData()->clearClass();
-    detachAttributesFromElement();
-    m_attributes.clear();
+    return m_attributeData.m_attributes[index]->createAttrIfNeeded(m_element);
 }
 
 void NamedNodeMap::detachFromElement()
@@ -212,90 +165,7 @@ void NamedNodeMap::detachFromElement()
     // of that, we can simply clear all the attributes to avoid accessing stale
     // pointers to do things like create Attr objects.
     m_element = 0;
-    clearAttributes();
-}
-
-void NamedNodeMap::setAttributes(const NamedNodeMap& other)
-{
-    // clone all attributes in the other map, but attach to our element
-    if (!m_element)
-        return;
-
-    // If assigning the map changes the id attribute, we need to call
-    // updateId.
-    Attribute* oldId = getAttributeItem(m_element->document()->idAttributeName());
-    Attribute* newId = other.getAttributeItem(m_element->document()->idAttributeName());
-
-    if (oldId || newId)
-        m_element->updateId(oldId ? oldId->value() : nullAtom, newId ? newId->value() : nullAtom);
-
-    Attribute* oldName = getAttributeItem(HTMLNames::nameAttr);
-    Attribute* newName = other.getAttributeItem(HTMLNames::nameAttr);
-
-    if (oldName || newName)
-        m_element->updateName(oldName ? oldName->value() : nullAtom, newName ? newName->value() : nullAtom);
-
-    clearAttributes();
-    unsigned newLength = other.length();
-    m_attributes.resize(newLength);
-
-    // FIXME: These loops can probably be combined.
-    for (unsigned i = 0; i < newLength; i++)
-        m_attributes[i] = other.m_attributes[i]->clone();
-    for (unsigned i = 0; i < newLength; i++)
-        m_element->attributeChanged(m_attributes[i].get());
-}
-
-void NamedNodeMap::addAttribute(PassRefPtr<Attribute> prpAttribute)
-{
-    RefPtr<Attribute> attribute = prpAttribute;
-
-    if (m_element)
-        m_element->willModifyAttribute(attribute->name(), nullAtom, attribute->value());
-
-    m_attributes.append(attribute);
-    if (Attr* attr = attribute->attr())
-        attr->m_element = m_element;
-
-    if (m_element)
-        m_element->didModifyAttribute(attribute.get());
-}
-
-void NamedNodeMap::removeAttribute(size_t index)
-{
-    ASSERT(index < length());
-
-    RefPtr<Attribute> attribute = m_attributes[index];
-
-    if (m_element)
-        m_element->willRemoveAttribute(attribute->name(), attribute->value());
-
-    if (Attr* attr = attribute->attr())
-        attr->m_element = 0;
-    m_attributes.remove(index);
-
-    if (m_element)
-        m_element->didRemoveAttribute(attribute.get());
-}
-
-void NamedNodeMap::replaceAttribute(size_t index, PassRefPtr<Attribute> prpAttribute)
-{
-    ASSERT(index < length());
-
-    RefPtr<Attribute> attribute = prpAttribute;
-    Attribute* old = m_attributes[index].get();
-
-    if (m_element)
-        m_element->willModifyAttribute(attribute->name(), old->value(), attribute->value());
-
-    if (Attr* attr = old->attr())
-        attr->m_element = 0;
-    m_attributes[index] = attribute;
-    if (Attr* attr = attribute->attr())
-        attr->m_element = m_element;
-
-    if (m_element)
-        m_element->didModifyAttribute(attribute.get());
+    m_attributeData.clearAttributes();
 }
 
 bool NamedNodeMap::mapsEquivalent(const NamedNodeMap* otherMap) const
index 6bdde0f..3201fcd 100644 (file)
 #ifndef NamedNodeMap_h
 #define NamedNodeMap_h
 
-#include "Attribute.h"
 #include "ElementAttributeData.h"
 #include "SpaceSplitString.h"
-#include <wtf/NotFound.h>
 
 namespace WebCore {
 
@@ -44,8 +42,6 @@ public:
         return adoptPtr(new NamedNodeMap(element));
     }
 
-    ~NamedNodeMap();
-
     void ref();
     void deref();
 
@@ -63,19 +59,17 @@ public:
     PassRefPtr<Node> setNamedItemNS(Node*, ExceptionCode&);
 
     PassRefPtr<Node> item(unsigned index) const;
-    size_t length() const { return m_attributes.size(); }
-    bool isEmpty() const { return !length(); }
+    size_t length() const { return m_attributeData.length(); }
+    bool isEmpty() const { return m_attributeData.isEmpty(); }
 
     // Internal interface.
 
-    Attribute* attributeItem(unsigned index) const { return m_attributes[index].get(); }
-    Attribute* getAttributeItem(const QualifiedName&) const;
-    size_t getAttributeItemIndex(const QualifiedName&) const;
-
-    void copyAttributesToVector(Vector<RefPtr<Attribute> >&);
+    Attribute* attributeItem(unsigned index) const { return m_attributeData.attributeItem(index); }
+    Attribute* getAttributeItem(const QualifiedName& name) const { return m_attributeData.getAttributeItem(name); }
+    size_t getAttributeItemIndex(const QualifiedName& name) const { return m_attributeData.getAttributeItemIndex(name); }
 
-    void shrinkToLength() { m_attributes.shrinkCapacity(length()); }
-    void reserveInitialCapacity(unsigned capacity) { m_attributes.reserveInitialCapacity(capacity); }
+    void shrinkToLength() { m_attributeData.m_attributes.shrinkCapacity(length()); }
+    void reserveInitialCapacity(unsigned capacity) { m_attributeData.m_attributes.reserveInitialCapacity(capacity); }
 
     // Used during parsing: only inserts if not already there. No error checking!
     void insertAttribute(PassRefPtr<Attribute> newAttribute, bool allowDuplicates)
@@ -88,9 +82,9 @@ public:
     bool mapsEquivalent(const NamedNodeMap* otherMap) const;
 
     // These functions do no error checking.
-    void addAttribute(PassRefPtr<Attribute>);
-    void removeAttribute(const QualifiedName&);
-    void removeAttribute(size_t index);
+    void addAttribute(PassRefPtr<Attribute> attribute) { m_attributeData.addAttribute(attribute, m_element); }
+    void removeAttribute(const QualifiedName& name) { m_attributeData.removeAttribute(name, m_element); }
+    void removeAttribute(size_t index) { m_attributeData.removeAttribute(index, m_element); }
 
     Element* element() const { return m_element; }
 
@@ -103,14 +97,8 @@ private:
     {
     }
 
-    void detachAttributesFromElement();
     void detachFromElement();
-    Attribute* getAttributeItem(const String& name, bool shouldIgnoreAttributeCase) const;
-    size_t getAttributeItemIndex(const String& name, bool shouldIgnoreAttributeCase) const;
-    size_t getAttributeItemIndexSlowCase(const String& name, bool shouldIgnoreAttributeCase) const;
-    void setAttributes(const NamedNodeMap&);
-    void clearAttributes();
-    void replaceAttribute(size_t index, PassRefPtr<Attribute>);
+    Attribute* getAttributeItem(const String& name, bool shouldIgnoreAttributeCase) const { return m_attributeData.getAttributeItem(name, shouldIgnoreAttributeCase); }
 
     // FIXME: NamedNodeMap is being broken up into two classes, one containing data
     //        for elements with attributes, and one for exposure to the DOM.
@@ -118,66 +106,8 @@ private:
     ElementAttributeData m_attributeData;
 
     Element* m_element;
-    Vector<RefPtr<Attribute>, 4> m_attributes;
 };
 
-inline Attribute* NamedNodeMap::getAttributeItem(const QualifiedName& name) const
-{
-    size_t index = getAttributeItemIndex(name);
-    if (index != notFound)
-        return m_attributes[index].get();
-    return 0;
-}
-
-inline size_t NamedNodeMap::getAttributeItemIndex(const QualifiedName& name) const
-{
-    size_t len = length();
-    for (unsigned i = 0; i < len; ++i) {
-        if (m_attributes[i]->name().matches(name))
-            return i;
-    }
-    return notFound;
-}
-
-inline Attribute* NamedNodeMap::getAttributeItem(const String& name, bool shouldIgnoreAttributeCase) const
-{
-    size_t index = getAttributeItemIndex(name, shouldIgnoreAttributeCase);
-    if (index != notFound)
-        return m_attributes[index].get();
-    return 0;
-}
-
-// We use a boolean parameter instead of calling shouldIgnoreAttributeCase so that the caller
-// can tune the behavior (hasAttribute is case sensitive whereas getAttribute is not).
-inline size_t NamedNodeMap::getAttributeItemIndex(const String& name, bool shouldIgnoreAttributeCase) const
-{
-    unsigned len = length();
-    bool doSlowCheck = shouldIgnoreAttributeCase;
-
-    // Optimize for the case where the attribute exists and its name exactly matches.
-    for (unsigned i = 0; i < len; ++i) {
-        const QualifiedName& attrName = m_attributes[i]->name();
-        if (!attrName.hasPrefix()) {
-            if (name == attrName.localName())
-                return i;
-        } else
-            doSlowCheck = true;
-    }
-
-    if (doSlowCheck)
-        return getAttributeItemIndexSlowCase(name, shouldIgnoreAttributeCase);
-    return notFound;
-}
-
-inline void NamedNodeMap::removeAttribute(const QualifiedName& name)
-{
-    size_t index = getAttributeItemIndex(name);
-    if (index == notFound)
-        return;
-
-    removeAttribute(index);
-}
-
 } // namespace WebCore
 
 #endif // NamedNodeMap_h
index b311dfa..e9d8585 100644 (file)
@@ -124,7 +124,7 @@ void StyledElement::updateAttributeStyle()
         collectStyleForAttribute(attribute, style.get());
     }
     clearAttributeStyleDirty();
-    ensureAttributeDataWithoutUpdate()->setAttributeStyle(style.release());
+    ensureAttributeData()->setAttributeStyle(style.release());
 }
 
 }
index be7a17e..7a76a5f 100644 (file)
@@ -40,7 +40,7 @@ public:
     void invalidateStyleAttribute();
 
     StylePropertySet* inlineStyleDecl() const { return attributeData() ? attributeData()->inlineStyleDecl() : 0; }
-    StylePropertySet* ensureInlineStyleDecl() { return ensureAttributeDataWithoutUpdate()->ensureInlineStyleDecl(this); }
+    StylePropertySet* ensureInlineStyleDecl() { return ensureAttributeData()->ensureInlineStyleDecl(this); }
     virtual CSSStyleDeclaration* style() OVERRIDE { return ensureInlineStyleDecl()->ensureCSSStyleDeclaration(); }
 
     StylePropertySet* attributeStyle();
index 06dc550..d2974f0 100644 (file)
@@ -547,7 +547,7 @@ void HTMLInputElement::updateType()
         registerForSuspensionCallbackIfNeeded();
 
     if (didRespectHeightAndWidth != m_inputType->shouldRespectHeightAndWidthAttributes()) {
-        ASSERT(attributeMap());
+        ASSERT(attributeData());
         if (Attribute* height = getAttributeItem(heightAttr))
             attributeChanged(height);
         if (Attribute* width = getAttributeItem(widthAttr))
index e6bd013..d6e5710 100644 (file)
@@ -37,15 +37,14 @@ struct SVGAnimatedPropertySynchronizer<true> {
         // Attribute directly to avoid a call to Element::attributeChanged
         // that could cause the SVGElement to erroneously reset its properties.
         // svg/dom/SVGStringList-basics.xhtml exercises this behavior.
-        NamedNodeMap* namedAttrMap = ownerElement->ensureUpdatedAttributes();
-        Attribute* old = namedAttrMap->getAttributeItem(attrName);
+        ElementAttributeData* attributeData = ownerElement->ensureUpdatedAttributeData();
+        Attribute* old = attributeData->getAttributeItem(attrName);
         if (old && value.isNull())
-            namedAttrMap->removeAttribute(old->name());
+            attributeData->removeAttribute(old->name(), ownerElement);
         else if (!old && !value.isNull())
-            namedAttrMap->addAttribute(ownerElement->createAttribute(attrName, value));
+            attributeData->addAttribute(ownerElement->createAttribute(attrName, value), ownerElement);
         else if (old && !value.isNull())
             old->setValue(value);
-
     }
 };
 
index cd6ea14..75b319f 100644 (file)
@@ -138,7 +138,7 @@ XMLDocumentParser::XMLDocumentParser(DocumentFragment* fragment, Element* parent
 
     QXmlStreamNamespaceDeclarations namespaces;
     for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) {
-        if (NamedNodeMap* attrs = element->updatedAttributes()) {
+        if (ElementAttributeData* attrs = element->updatedAttributeData()) {
             for (unsigned i = 0; i < attrs->length(); i++) {
                 Attribute* attr = attrs->attributeItem(i);
                 if (attr->localName() == "xmlns")