Make ElementAttributeData a variable-sized object to reduce memory use.
authorkling@webkit.org <kling@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 25 Jul 2012 18:14:13 +0000 (18:14 +0000)
committerkling@webkit.org <kling@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 25 Jul 2012 18:14:13 +0000 (18:14 +0000)
<http://webkit.org/b/88240>

Reviewed by Ryosuke Niwa.

Source/WebCore:

Take advantage of the fact that we know at ElementAttributeData construction time how many attributes
it needs to accomodate and allocate exactly as much space as needed instead of using a Vector.
For elements that never have their attribute list mutated (the vast majority), this saves a lot of
memory and removes the indirection to Vector<Attribute>'s heap-allocated storage.

Introduced a mutability flag to ElementAttributeData and sprinkled assertions all over to make sure
that nothing tries to mutate an element with a raw attribute array.

When an Element's attribute(s) are mutated, we reconstruct the ElementAttributeData, this time using
a Vector as backing instead. This is triggered when calling Element::mutableAttributeData().

This reduces memory consumption by 3.2MB when viewing the full HTML5 spec at <http://whatwg.org/c/>.
That is a ~35% reduction in DOM attribute memory use.

Furthermore, that page ends up promoting 27% of the elements to mutable attribute storage due to dynamic
adding of "class" attributes. For more static pages, savings are even greater.

Also did away with ElementAttributeData::removeAttribute(name) and do separate index lookup where
needed. Not a big deal but avoids double lookup of attributes when removing them.

* dom/Element.cpp:
(WebCore::Element::detachAttribute):
(WebCore::Element::removeAttribute):
(WebCore::Element::attributes):
(WebCore::Element::setAttributeInternal):
(WebCore::Element::parserSetAttributes):
(WebCore::Element::hasEquivalentAttributes):
(WebCore::Element::setAttributeNode):
(WebCore::Element::removeAttributeNode):
(WebCore::Element::getAttributeNode):
(WebCore::Element::getAttributeNodeNS):
(WebCore::Element::hasAttribute):
(WebCore::Element::hasAttributeNS):
(WebCore::Element::cloneAttributesFromElement):

    Sprinkle const on ElementAttributeData* pointers.
    Switch to using mutableAttributeData() in code paths that modify attributes.

(WebCore::Element::normalizeAttributes):

    Cleaner iteration over attributes, I removed ElementAttributeData::attributeVector() since that
    was just incurring a bunch of extra Vector copying. Since ElementAttributeData already exposes
    length() and attributeItem(index), use those to iterate directly over the attributes.

(WebCore::Element::createMutableAttributeData):

    Added, converts existing ElementAttributeData to a mutable object. Otherwise creates a new
    ElementAttributeData (mutable and empty.)

* dom/Element.h:
(WebCore::Element::attributeData):
(WebCore::Element::updatedAttributeData):
(WebCore::Element::ensureAttributeData):
(WebCore::Element::ensureUpdatedAttributeData):

    Made all of these return "const ElementAttributeData*" to ensure at compile-time that nothing
    attempts to modify an ElementAttributeData that may be immutable.

    ensureUpdatedAttributeData() is still const, despite possibly calling mutableAttributeData().
    The goal of having methods be const is to prevent conversion from one ElementAttributeData
    object to another, so that pointers remain intact and no unnecessary work gets done.

(WebCore::Element::mutableAttributeData):

    Added, returns a guaranteed mutable ElementAttributeData* pointer.

* dom/ElementAttributeData.cpp:
(WebCore::ElementAttributeData::createImmutable):

    Creates a new ElementAttributeData tailored to fit the provided Vector<Attribute>.

(WebCore::ElementAttributeData::ElementAttributeData):

    Moved constructors out-of-line, getting too complex for header files.

    ElementAttributeData(const Vector<Attribute>&) creates an immutable ElementAttributeData
    containing the provided attributes in an array tacked onto the end of the object.

(WebCore::ElementAttributeData::~ElementAttributeData):

    Free the attribute vector if mutable.
    For immutable objects, manually invoke destructor on each Attribute object,
    since these won't be destroyed automatically by ~ElementAttributeData.

(WebCore::ElementAttributeData::attrIfExists):

(WebCore::ElementAttributeData::ensureAttr):
(WebCore::ElementAttributeData::setAttr):
(WebCore::ElementAttributeData::removeAttr):

    Make these const, as they should always have been- they don't affect the ElementAttributeData,
    only the global DOM Attr <-> ElementAttributeData mappings.

(WebCore::ElementAttributeData::ensureInlineStyle):
(WebCore::ElementAttributeData::ensureMutableInlineStyle):

    Sprinkle ASSERT(isMutable()). This doesn't mean that all Elements with inline styles automatically
    have mutable attribute data. There's still inlineStyle() for that, which may return null.
    These are only for use where you need a valid StylePropertySet*, even if there is no inline style
    attribute, e.g in editing code that wants to add style.

(WebCore::ElementAttributeData::updateInlineStyleAvoidingMutation):
(WebCore::ElementAttributeData::destroyInlineStyle):

    Make these const. While destroyInlineStyle() doesn't sound very const-y, immutable objects that
    contain a style attribute will have a valid inlineStyle() with that style data. This is just
    an interface for ~StyledElement to destroy the style object.
    It'd be nice to do that in ~ElementAttributeData(), but we need a StyledElement* pointer to
    clean up properly and we don't store one in this class.

(WebCore::ElementAttributeData::addAttribute):
(WebCore::ElementAttributeData::removeAttribute):
(WebCore::ElementAttributeData::clearAttributes):
(WebCore::ElementAttributeData::replaceAttribute):

    Sprinkle ASSERT(isMutable()).
    Always go straight for m_mutableAttributeVector since we know that's the storage being used.

(WebCore::ElementAttributeData::isEquivalent):
(WebCore::ElementAttributeData::detachAttrObjectsFromElement):
(WebCore::ElementAttributeData::getAttributeItemIndexSlowCase):
(WebCore::ElementAttributeData::removeAttribute):
(WebCore::ElementAttributeData::getAttributeItem):
(WebCore::ElementAttributeData::getAttributeItemIndex):

    Use length() and attributeItem(index) to iterate over the attributes.

(WebCore::ElementAttributeData::cloneDataFrom):

    Sprinkle ASSERT(isMutable()). Added a FIXME that cloning elements could create immutable
    attribute data. I'm not sure this optimization is worthwhile, as cloning elements is already
    a semi-rare occurrence.

    Updated code to handle both immutable and mutable source objects. This could

(WebCore::ElementAttributeData::getAttributeNode):

    Const correctness.

* dom/ElementAttributeData.h:
(ElementAttributeData):

    Turn attribute storage into what's effectively union { OwnPtr<Vector<Attribute>>; Attribute[]; }
    The new m_isMutable bit determines which union member should be used for access.

(WebCore::ElementAttributeData::create):
(WebCore::ElementAttributeData::createImmutable):

    Added createImmutable(const Vector<Attribute>&) as a complement to create().

(WebCore::ElementAttributeData::setClass):
(WebCore::ElementAttributeData::setIdForStyleResolution):
(WebCore::ElementAttributeData::inlineStyle):
(WebCore::ElementAttributeData::setAttributeStyle):

    Make these const, and their data member counterparts 'mutable'.
    An immutable ElementAttributeData object still has m_classNames, m_idForStyleResolution,
    m_inlineStyleDecl and m_attributeStyle.

(WebCore::ElementAttributeData::reportMemoryUsage):

    Updated for isMutable().

(WebCore::ElementAttributeData::makeMutable):

    Returns a mutable clone of itself.

(WebCore::ElementAttributeData::isEmpty):
(WebCore::ElementAttributeData::length):
(WebCore::ElementAttributeData::attributeItem):

    Check isMutable() to know how to access attribute storage.

* dom/StyledElement.cpp:
(WebCore::StyledElement::style):
(WebCore::StyledElement::setInlineStyleProperty):

    Simplify by using StyledElement::ensureInlineStyle().

(WebCore::StyledElement::classAttributeChanged):

    Use mutableAttributeData() if the attribute is being removed completely.
    In other cases, tiptoe around without causing the attribute data to go mutable.

(WebCore::StyledElement::removeInlineStyleProperty):

    Use mutableAttributeData() if/when actually removing something.

(WebCore::StyledElement::addSubresourceAttributeURLs):

    Const correctness.

* dom/StyledElement.h:
(WebCore::StyledElement::ensureInlineStyle):

    This now implies conversion to mutable attribute data. Used by codepaths that add/remove
    properties, so conversion is inevitable.

* html/parser/HTMLConstructionSite.cpp:
(WebCore::HTMLConstructionSite::mergeAttributesFromTokenIntoElement):
(WebCore::HTMLConstructionSite::createHTMLElementFromSavedElement):
* svg/properties/SVGAnimatedPropertyMacros.h:
(WebCore::SVGSynchronizableAnimatedProperty::synchronize):

    Use mutableAttributeData() as appropriate.

* xml/parser/XMLDocumentParserQt.cpp:
(WebCore::XMLDocumentParser::XMLDocumentParser):

    Const correctness.

LayoutTests:

Extended this test to cover the case where setAttributeNode() returns a lazily serialized value.

* fast/dom/attr-style-too-lazy-expected.txt:
* fast/dom/attr-style-too-lazy.html:

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

27 files changed:
LayoutTests/ChangeLog
LayoutTests/fast/dom/attr-style-too-lazy-expected.txt
LayoutTests/fast/dom/attr-style-too-lazy.html
Source/WebCore/ChangeLog
Source/WebCore/css/PropertySetCSSStyleDeclaration.cpp
Source/WebCore/css/SelectorChecker.cpp
Source/WebCore/css/SelectorChecker.h
Source/WebCore/dom/DatasetDOMStringMap.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/dom/ElementAttributeData.cpp
Source/WebCore/dom/ElementAttributeData.h
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/StyledElement.cpp
Source/WebCore/dom/StyledElement.h
Source/WebCore/editing/markup.cpp
Source/WebCore/html/HTMLEmbedElement.cpp
Source/WebCore/html/HTMLObjectElement.cpp
Source/WebCore/html/parser/HTMLConstructionSite.cpp
Source/WebCore/inspector/DOMPatchSupport.cpp
Source/WebCore/page/PageSerializer.cpp
Source/WebCore/svg/properties/SVGAnimatedPropertyMacros.h
Source/WebCore/xml/XPathFunctions.cpp
Source/WebCore/xml/parser/MarkupTokenBase.h
Source/WebCore/xml/parser/XMLDocumentParserLibxml2.cpp
Source/WebCore/xml/parser/XMLDocumentParserQt.cpp
Source/WebCore/xml/parser/XMLTreeBuilder.cpp

index 4c4f47b..17e71a3 100644 (file)
@@ -1,3 +1,15 @@
+2012-07-25  Andreas Kling  <kling@webkit.org>
+
+        Make ElementAttributeData a variable-sized object to reduce memory use.
+        <http://webkit.org/b/88240>
+
+        Reviewed by Ryosuke Niwa.
+
+        Extended this test to cover the case where setAttributeNode() returns a lazily serialized value.
+
+        * fast/dom/attr-style-too-lazy-expected.txt:
+        * fast/dom/attr-style-too-lazy.html:
+
 2012-07-25  Konrad Piascik  <kpiascik@rim.com>
 
         Web Inspector: Geolocation override
index ba53c2f..6809013 100644 (file)
@@ -5,6 +5,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
 
 PASS styleAttr.value is 'background-color: white; '
 PASS styleAttr.value is 'background-color: green; '
+PASS oldStyleAttr.value is 'background-color: red; '
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 6d23310..8f294df 100644 (file)
@@ -16,6 +16,10 @@ shouldBe("styleAttr.value", "'background-color: white; '");
 e.style.backgroundColor = 'green';
 shouldBe("styleAttr.value", "'background-color: green; '");
 
+e.style.backgroundColor = 'red';
+var oldStyleAttr = e.setAttributeNode(document.createAttribute("style"));
+shouldBe("oldStyleAttr.value", "'background-color: red; '");
+
 </script>
 <script src="../js/resources/js-test-post.js"></script>
 </body>
index 003ab8c..b50f34f 100644 (file)
@@ -1,3 +1,221 @@
+2012-07-25  Andreas Kling  <kling@webkit.org>
+
+        Make ElementAttributeData a variable-sized object to reduce memory use.
+        <http://webkit.org/b/88240>
+
+        Reviewed by Ryosuke Niwa.
+
+        Take advantage of the fact that we know at ElementAttributeData construction time how many attributes
+        it needs to accomodate and allocate exactly as much space as needed instead of using a Vector.
+        For elements that never have their attribute list mutated (the vast majority), this saves a lot of
+        memory and removes the indirection to Vector<Attribute>'s heap-allocated storage.
+
+        Introduced a mutability flag to ElementAttributeData and sprinkled assertions all over to make sure
+        that nothing tries to mutate an element with a raw attribute array.
+
+        When an Element's attribute(s) are mutated, we reconstruct the ElementAttributeData, this time using
+        a Vector as backing instead. This is triggered when calling Element::mutableAttributeData().
+
+        This reduces memory consumption by 3.2MB when viewing the full HTML5 spec at <http://whatwg.org/c/>.
+        That is a ~35% reduction in DOM attribute memory use.
+
+        Furthermore, that page ends up promoting 27% of the elements to mutable attribute storage due to dynamic
+        adding of "class" attributes. For more static pages, savings are even greater.
+
+        Also did away with ElementAttributeData::removeAttribute(name) and do separate index lookup where
+        needed. Not a big deal but avoids double lookup of attributes when removing them.
+
+        * dom/Element.cpp:
+        (WebCore::Element::detachAttribute):
+        (WebCore::Element::removeAttribute):
+        (WebCore::Element::attributes):
+        (WebCore::Element::setAttributeInternal):
+        (WebCore::Element::parserSetAttributes):
+        (WebCore::Element::hasEquivalentAttributes):
+        (WebCore::Element::setAttributeNode):
+        (WebCore::Element::removeAttributeNode):
+        (WebCore::Element::getAttributeNode):
+        (WebCore::Element::getAttributeNodeNS):
+        (WebCore::Element::hasAttribute):
+        (WebCore::Element::hasAttributeNS):
+        (WebCore::Element::cloneAttributesFromElement):
+
+            Sprinkle const on ElementAttributeData* pointers.
+            Switch to using mutableAttributeData() in code paths that modify attributes.
+
+        (WebCore::Element::normalizeAttributes):
+
+            Cleaner iteration over attributes, I removed ElementAttributeData::attributeVector() since that
+            was just incurring a bunch of extra Vector copying. Since ElementAttributeData already exposes
+            length() and attributeItem(index), use those to iterate directly over the attributes.
+
+        (WebCore::Element::createMutableAttributeData):
+
+            Added, converts existing ElementAttributeData to a mutable object. Otherwise creates a new
+            ElementAttributeData (mutable and empty.)
+
+        * dom/Element.h:
+        (WebCore::Element::attributeData):
+        (WebCore::Element::updatedAttributeData):
+        (WebCore::Element::ensureAttributeData):
+        (WebCore::Element::ensureUpdatedAttributeData):
+
+            Made all of these return "const ElementAttributeData*" to ensure at compile-time that nothing
+            attempts to modify an ElementAttributeData that may be immutable.
+
+            ensureUpdatedAttributeData() is still const, despite possibly calling mutableAttributeData().
+            The goal of having methods be const is to prevent conversion from one ElementAttributeData
+            object to another, so that pointers remain intact and no unnecessary work gets done.
+
+        (WebCore::Element::mutableAttributeData):
+
+            Added, returns a guaranteed mutable ElementAttributeData* pointer.
+
+        * dom/ElementAttributeData.cpp:
+        (WebCore::ElementAttributeData::createImmutable):
+
+            Creates a new ElementAttributeData tailored to fit the provided Vector<Attribute>.
+
+        (WebCore::ElementAttributeData::ElementAttributeData):
+
+            Moved constructors out-of-line, getting too complex for header files.
+
+            ElementAttributeData(const Vector<Attribute>&) creates an immutable ElementAttributeData
+            containing the provided attributes in an array tacked onto the end of the object.
+
+        (WebCore::ElementAttributeData::~ElementAttributeData):
+
+            Free the attribute vector if mutable.
+            For immutable objects, manually invoke destructor on each Attribute object,
+            since these won't be destroyed automatically by ~ElementAttributeData.
+
+        (WebCore::ElementAttributeData::attrIfExists):
+
+        (WebCore::ElementAttributeData::ensureAttr):
+        (WebCore::ElementAttributeData::setAttr):
+        (WebCore::ElementAttributeData::removeAttr):
+
+            Make these const, as they should always have been- they don't affect the ElementAttributeData,
+            only the global DOM Attr <-> ElementAttributeData mappings.
+
+        (WebCore::ElementAttributeData::ensureInlineStyle):
+        (WebCore::ElementAttributeData::ensureMutableInlineStyle):
+
+            Sprinkle ASSERT(isMutable()). This doesn't mean that all Elements with inline styles automatically
+            have mutable attribute data. There's still inlineStyle() for that, which may return null.
+            These are only for use where you need a valid StylePropertySet*, even if there is no inline style
+            attribute, e.g in editing code that wants to add style.
+
+        (WebCore::ElementAttributeData::updateInlineStyleAvoidingMutation):
+        (WebCore::ElementAttributeData::destroyInlineStyle):
+
+            Make these const. While destroyInlineStyle() doesn't sound very const-y, immutable objects that
+            contain a style attribute will have a valid inlineStyle() with that style data. This is just
+            an interface for ~StyledElement to destroy the style object.
+            It'd be nice to do that in ~ElementAttributeData(), but we need a StyledElement* pointer to
+            clean up properly and we don't store one in this class.
+
+        (WebCore::ElementAttributeData::addAttribute):
+        (WebCore::ElementAttributeData::removeAttribute):
+        (WebCore::ElementAttributeData::clearAttributes):
+        (WebCore::ElementAttributeData::replaceAttribute):
+
+            Sprinkle ASSERT(isMutable()).
+            Always go straight for m_mutableAttributeVector since we know that's the storage being used.
+
+        (WebCore::ElementAttributeData::isEquivalent):
+        (WebCore::ElementAttributeData::detachAttrObjectsFromElement):
+        (WebCore::ElementAttributeData::getAttributeItemIndexSlowCase):
+        (WebCore::ElementAttributeData::removeAttribute):
+        (WebCore::ElementAttributeData::getAttributeItem):
+        (WebCore::ElementAttributeData::getAttributeItemIndex):
+
+            Use length() and attributeItem(index) to iterate over the attributes.
+
+        (WebCore::ElementAttributeData::cloneDataFrom):
+
+            Sprinkle ASSERT(isMutable()). Added a FIXME that cloning elements could create immutable
+            attribute data. I'm not sure this optimization is worthwhile, as cloning elements is already
+            a semi-rare occurrence.
+
+            Updated code to handle both immutable and mutable source objects. This could 
+
+        (WebCore::ElementAttributeData::getAttributeNode):
+
+            Const correctness.
+
+        * dom/ElementAttributeData.h:
+        (ElementAttributeData):
+
+            Turn attribute storage into what's effectively union { OwnPtr<Vector<Attribute>>; Attribute[]; }
+            The new m_isMutable bit determines which union member should be used for access.
+
+        (WebCore::ElementAttributeData::create):
+        (WebCore::ElementAttributeData::createImmutable):
+
+            Added createImmutable(const Vector<Attribute>&) as a complement to create().
+
+        (WebCore::ElementAttributeData::setClass):
+        (WebCore::ElementAttributeData::setIdForStyleResolution):
+        (WebCore::ElementAttributeData::inlineStyle):
+        (WebCore::ElementAttributeData::setAttributeStyle):
+
+            Make these const, and their data member counterparts 'mutable'.
+            An immutable ElementAttributeData object still has m_classNames, m_idForStyleResolution,
+            m_inlineStyleDecl and m_attributeStyle.
+
+        (WebCore::ElementAttributeData::reportMemoryUsage):
+
+            Updated for isMutable().
+
+        (WebCore::ElementAttributeData::makeMutable):
+
+            Returns a mutable clone of itself.
+
+        (WebCore::ElementAttributeData::isEmpty):
+        (WebCore::ElementAttributeData::length):
+        (WebCore::ElementAttributeData::attributeItem):
+
+            Check isMutable() to know how to access attribute storage.
+
+        * dom/StyledElement.cpp:
+        (WebCore::StyledElement::style):
+        (WebCore::StyledElement::setInlineStyleProperty):
+
+            Simplify by using StyledElement::ensureInlineStyle().
+
+        (WebCore::StyledElement::classAttributeChanged):
+
+            Use mutableAttributeData() if the attribute is being removed completely.
+            In other cases, tiptoe around without causing the attribute data to go mutable.
+
+        (WebCore::StyledElement::removeInlineStyleProperty):
+
+            Use mutableAttributeData() if/when actually removing something.
+
+        (WebCore::StyledElement::addSubresourceAttributeURLs):
+
+            Const correctness.
+
+        * dom/StyledElement.h:
+        (WebCore::StyledElement::ensureInlineStyle):
+
+            This now implies conversion to mutable attribute data. Used by codepaths that add/remove
+            properties, so conversion is inevitable.
+
+        * html/parser/HTMLConstructionSite.cpp:
+        (WebCore::HTMLConstructionSite::mergeAttributesFromTokenIntoElement):
+        (WebCore::HTMLConstructionSite::createHTMLElementFromSavedElement):
+        * svg/properties/SVGAnimatedPropertyMacros.h:
+        (WebCore::SVGSynchronizableAnimatedProperty::synchronize):
+
+            Use mutableAttributeData() as appropriate.
+
+        * xml/parser/XMLDocumentParserQt.cpp:
+        (WebCore::XMLDocumentParser::XMLDocumentParser):
+
+            Const correctness.
+
 2012-07-25  Andrew Wilson  <atwilson@chromium.org>
 
         Unreviewed, rolling out r123560.
index 006014e..8a0afd6 100644 (file)
@@ -403,6 +403,7 @@ CSSStyleSheet* InlineCSSStyleDeclaration::parentStyleSheet() const
 StylePropertySet* InlineCSSStyleDeclaration::ensureMutablePropertySet()
 {
     ASSERT(m_propertySet);
+    ASSERT(m_propertySet->isMutable());
     return m_propertySet;
 }
 
index 299e075..ef6ea82 100644 (file)
@@ -622,7 +622,7 @@ bool htmlAttributeHasCaseInsensitiveValue(const QualifiedName& attr)
     return isPossibleHTMLAttr && htmlCaseInsensitiveAttributesSet->contains(attr.localName().impl());
 }
 
-static bool attributeValueMatches(Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
+static bool attributeValueMatches(const Attribute* attributeItem, CSSSelector::Match match, const AtomicString& selectorValue, bool caseSensitive)
 {
     const AtomicString& value = attributeItem->value();
     if (value.isNull())
@@ -689,7 +689,7 @@ static bool anyAttributeMatches(Element* element, CSSSelector::Match match, cons
 {
     ASSERT(element->hasAttributesWithoutUpdate());
     for (size_t i = 0; i < element->attributeCount(); ++i) {
-        Attribute* attributeItem = element->attributeItem(i);
+        const Attribute* attributeItem = element->attributeItem(i);
 
         if (!SelectorChecker::attributeNameMatches(attributeItem, selectorAttr))
             continue;
index 3219dea..ce7213b 100644 (file)
@@ -214,7 +214,7 @@ inline bool SelectorChecker::checkExactAttribute(const Element* element, const Q
         return false;
     unsigned size = element->attributeCount();
     for (unsigned i = 0; i < size; ++i) {
-        Attribute* attribute = element->attributeItem(i);
+        const Attribute* attribute = element->attributeItem(i);
         if (attributeNameMatches(attribute, selectorAttributeName) && (!value || attribute->value().impl() == value))
             return true;
     }
index 382022e..d82c8bc 100644 (file)
@@ -146,7 +146,7 @@ void DatasetDOMStringMap::getNames(Vector<String>& names)
 
     unsigned length = m_element->attributeCount();
     for (unsigned i = 0; i < length; i++) {
-        Attribute* attribute = m_element->attributeItem(i);
+        const Attribute* attribute = m_element->attributeItem(i);
         if (isValidAttributeName(attribute->localName()))
             names.append(convertAttributeNameToPropertyName(attribute->localName()));
     }
@@ -159,7 +159,7 @@ String DatasetDOMStringMap::item(const String& name)
 
     unsigned length = m_element->attributeCount();
     for (unsigned i = 0; i < length; i++) {
-        Attribute* attribute = m_element->attributeItem(i);
+        const Attribute* attribute = m_element->attributeItem(i);
         if (propertyNameMatchesAttributeName(name, attribute->localName()))
             return attribute->value();
     }
@@ -174,7 +174,7 @@ bool DatasetDOMStringMap::contains(const String& name)
 
     unsigned length = m_element->attributeCount();
     for (unsigned i = 0; i < length; i++) {
-        Attribute* attribute = m_element->attributeItem(i);
+        const Attribute* attribute = m_element->attributeItem(i);
         if (propertyNameMatchesAttributeName(name, attribute->localName()))
             return true;
     }
index 553de3b..b3aabac 100644 (file)
@@ -201,7 +201,7 @@ PassRefPtr<Attr> Element::detachAttribute(size_t index)
 {
     ASSERT(attributeData());
 
-    Attribute* attribute = attributeData()->attributeItem(index);
+    const Attribute* attribute = attributeData()->attributeItem(index);
     ASSERT(attribute);
 
     RefPtr<Attr> attr = attrIfExists(attribute->name());
@@ -210,7 +210,7 @@ PassRefPtr<Attr> Element::detachAttribute(size_t index)
     else
         attr = Attr::create(document(), attribute->name(), attribute->value());
 
-    attributeData()->removeAttribute(index, this);
+    mutableAttributeData()->removeAttribute(index, this);
     return attr.release();
 }
 
@@ -222,7 +222,11 @@ void Element::removeAttribute(const QualifiedName& name)
     if (RefPtr<Attr> attr = attrIfExists(name))
         attr->detachFromElementWithValue(attr->value());
 
-    attributeData()->removeAttribute(name, this);
+    size_t index = attributeData()->getAttributeItemIndex(name);
+    if (index == notFound)
+        return;
+
+    mutableAttributeData()->removeAttribute(index, this);
 }
 
 void Element::setBooleanAttribute(const QualifiedName& name, bool value)
@@ -264,8 +268,8 @@ const AtomicString& Element::getAttribute(const QualifiedName& name) const
         updateAnimatedSVGAttribute(name);
 #endif
 
-    if (m_attributeData) {
-        if (Attribute* attribute = getAttributeItem(name))
+    if (attributeData()) {
+        if (const Attribute* attribute = getAttributeItem(name))
             return attribute->value();
     }
     return nullAtom;
@@ -628,8 +632,8 @@ const AtomicString& Element::getAttribute(const AtomicString& name) const
     }
 #endif
 
-    if (m_attributeData) {
-        if (Attribute* attribute = m_attributeData->getAttributeItem(name, ignoreCase))
+    if (attributeData()) {
+        if (const Attribute* attribute = attributeData()->getAttributeItem(name, ignoreCase))
             return attribute->value();
     }
 
@@ -662,15 +666,17 @@ void Element::setAttribute(const QualifiedName& name, const AtomicString& value,
 
 inline void Element::setAttributeInternal(size_t index, const QualifiedName& name, const AtomicString& value, EInUpdateStyleAttribute inUpdateStyleAttribute)
 {
-    Attribute* old = index != notFound ? m_attributeData->attributeItem(index) : 0;
+    ElementAttributeData* attributeData = mutableAttributeData();
+
+    Attribute* old = index != notFound ? attributeData->attributeItem(index) : 0;
     if (value.isNull()) {
         if (old)
-            m_attributeData->removeAttribute(index, this, inUpdateStyleAttribute);
+            attributeData->removeAttribute(index, this, inUpdateStyleAttribute);
         return;
     }
 
     if (!old) {
-        m_attributeData->addAttribute(Attribute(name, value), this, inUpdateStyleAttribute);
+        attributeData->addAttribute(Attribute(name, value), this, inUpdateStyleAttribute);
         return;
     }
 
@@ -764,44 +770,44 @@ void Element::parserSetAttributes(const Vector<Attribute>& attributeVector, Frag
     if (attributeVector.isEmpty())
         return;
 
-    createAttributeData();
-    m_attributeData->m_attributes = attributeVector;
-    m_attributeData->m_attributes.shrinkToFit();
+    Vector<Attribute> filteredAttributes = attributeVector;
 
     // 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 == DisallowScriptingContent) {
         unsigned i = 0;
-        while (i < m_attributeData->length()) {
-            const QualifiedName& attributeName = m_attributeData->m_attributes[i].name();
-            if (isEventHandlerAttribute(attributeName)) {
-                m_attributeData->m_attributes.remove(i);
+        while (i < filteredAttributes.size()) {
+            Attribute& attribute = filteredAttributes[i];
+            if (isEventHandlerAttribute(attribute.name())) {
+                filteredAttributes.remove(i);
                 continue;
             }
 
-            if (isAttributeToRemove(attributeName, m_attributeData->m_attributes[i].value()))
-                m_attributeData->m_attributes[i].setValue(nullAtom);
+            if (isAttributeToRemove(attribute.name(), attribute.value()))
+                attribute.setValue(nullAtom);
             i++;
         }
     }
 
-    // Store the set of attributes that changed on the stack in case
+    m_attributeData = ElementAttributeData::createImmutable(filteredAttributes);
+
+    // Iterate over the set of attributes we already have on the stack in case
     // attributeChanged mutates m_attributeData.
-    Vector<Attribute> clonedAttributes = m_attributeData->clonedAttributeVector();
-    for (unsigned i = 0; i < clonedAttributes.size(); ++i)
-        attributeChanged(clonedAttributes[i]);
+    // FIXME: Find a way so we don't have to do this.
+    for (unsigned i = 0; i < filteredAttributes.size(); ++i)
+        attributeChanged(filteredAttributes[i]);
 }
 
 bool Element::hasAttributes() const
 {
     updateInvalidAttributes();
-    return m_attributeData && m_attributeData->length();
+    return attributeData() && attributeData()->length();
 }
 
 bool Element::hasEquivalentAttributes(const Element* other) const
 {
-    ElementAttributeData* attributeData = updatedAttributeData();
-    ElementAttributeData* otherAttributeData = other->updatedAttributeData();
+    const ElementAttributeData* attributeData = updatedAttributeData();
+    const ElementAttributeData* otherAttributeData = other->updatedAttributeData();
     if (attributeData)
         return attributeData->isEquivalent(otherAttributeData);
     if (otherAttributeData)
@@ -847,11 +853,6 @@ KURL Element::baseURI() const
     return KURL(parentBase, baseAttribute);
 }
 
-void Element::createAttributeData() const
-{
-    m_attributeData = ElementAttributeData::create();
-}
-
 const QualifiedName& Element::imageSourceAttributeName() const
 {
     return srcAttr;
@@ -1398,8 +1399,6 @@ PassRefPtr<Attr> Element::setAttributeNode(Attr* attr, ExceptionCode& ec)
         return 0;
     }
 
-    ElementAttributeData* attributeData = ensureUpdatedAttributeData();
-
     RefPtr<Attr> oldAttr = attrIfExists(attr->qualifiedName());
     if (oldAttr.get() == attr)
         return attr; // This Attr is already attached to the element.
@@ -1411,6 +1410,9 @@ PassRefPtr<Attr> Element::setAttributeNode(Attr* attr, ExceptionCode& ec)
         return 0;
     }
 
+    updateInvalidAttributes();
+    ElementAttributeData* attributeData = mutableAttributeData();
+
     size_t index = attributeData->getAttributeItemIndex(attr->qualifiedName());
     Attribute* oldAttribute = index != notFound ? attributeData->attributeItem(index) : 0;
 
@@ -1448,7 +1450,7 @@ PassRefPtr<Attr> Element::removeAttributeNode(Attr* attr, ExceptionCode& ec)
 
     ASSERT(document() == attr->document());
 
-    ElementAttributeData* attributeData = updatedAttributeData();
+    const ElementAttributeData* attributeData = updatedAttributeData();
     ASSERT(attributeData);
 
     size_t index = attributeData->getAttributeItemIndex(attr->qualifiedName());
@@ -1483,21 +1485,20 @@ void Element::removeAttribute(size_t index)
 {
     ASSERT(attributeData());
     ASSERT(index <= attributeCount());
-    attributeData()->removeAttribute(index, this);
+    mutableAttributeData()->removeAttribute(index, this);
 }
 
 void Element::removeAttribute(const String& name)
 {
-    ElementAttributeData* attributeData = this->attributeData();
-    if (!attributeData)
+    if (!attributeData())
         return;
 
     String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name;
-    size_t index = attributeData->getAttributeItemIndex(localName, false);
+    size_t index = attributeData()->getAttributeItemIndex(localName, false);
     if (index == notFound)
         return;
 
-    attributeData->removeAttribute(index, this);
+    mutableAttributeData()->removeAttribute(index, this);
 }
 
 void Element::removeAttributeNS(const String& namespaceURI, const String& localName)
@@ -1507,7 +1508,7 @@ void Element::removeAttributeNS(const String& namespaceURI, const String& localN
 
 PassRefPtr<Attr> Element::getAttributeNode(const String& name)
 {
-    ElementAttributeData* attributeData = updatedAttributeData();
+    const ElementAttributeData* attributeData = updatedAttributeData();
     if (!attributeData)
         return 0;
     return attributeData->getAttributeNode(name, shouldIgnoreAttributeCase(this), this);
@@ -1515,7 +1516,7 @@ PassRefPtr<Attr> Element::getAttributeNode(const String& name)
 
 PassRefPtr<Attr> Element::getAttributeNodeNS(const String& namespaceURI, const String& localName)
 {
-    ElementAttributeData* attributeData = updatedAttributeData();
+    const ElementAttributeData* attributeData = updatedAttributeData();
     if (!attributeData)
         return 0;
     return attributeData->getAttributeNode(QualifiedName(nullAtom, localName, namespaceURI), this);
@@ -1523,19 +1524,18 @@ PassRefPtr<Attr> Element::getAttributeNodeNS(const String& namespaceURI, const S
 
 bool Element::hasAttribute(const String& name) const
 {
-    ElementAttributeData* attributeData = updatedAttributeData();
-    if (!attributeData)
+    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 attributeData->getAttributeItem(localName, false);
+    return updatedAttributeData()->getAttributeItem(localName, false);
 }
 
 bool Element::hasAttributeNS(const String& namespaceURI, const String& localName) const
 {
-    ElementAttributeData* attributeData = updatedAttributeData();
+    const ElementAttributeData* attributeData = updatedAttributeData();
     if (!attributeData)
         return false;
     return attributeData->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI));
@@ -1742,12 +1742,11 @@ void Element::normalizeAttributes()
     if (!hasAttrList())
         return;
 
-    ElementAttributeData* attributeData = updatedAttributeData();
+    const ElementAttributeData* attributeData = updatedAttributeData();
     ASSERT(attributeData);
 
-    const Vector<Attribute>& attributes = attributeData->attributeVector();
-    for (size_t i = 0; i < attributes.size(); ++i) {
-        if (RefPtr<Attr> attr = attrIfExists(attributes[i].name()))
+    for (size_t i = 0; i < attributeData->length(); ++i) {
+        if (RefPtr<Attr> attr = attrIfExists(attributeData->attributeItem(i)->name()))
             attr->normalize();
     }
 }
@@ -1816,8 +1815,8 @@ DOMStringMap* Element::dataset()
 KURL Element::getURLAttribute(const QualifiedName& name) const
 {
 #if !ASSERT_DISABLED
-    if (m_attributeData) {
-        if (Attribute* attribute = getAttributeItem(name))
+    if (attributeData()) {
+        if (const Attribute* attribute = getAttributeItem(name))
             ASSERT(isURLAttribute(*attribute));
     }
 #endif
@@ -1827,8 +1826,8 @@ KURL Element::getURLAttribute(const QualifiedName& name) const
 KURL Element::getNonEmptyURLAttribute(const QualifiedName& name) const
 {
 #if !ASSERT_DISABLED
-    if (m_attributeData) {
-        if (Attribute* attribute = getAttributeItem(name))
+    if (attributeData()) {
+        if (const Attribute* attribute = getAttributeItem(name))
             ASSERT(isURLAttribute(*attribute));
     }
 #endif
@@ -2172,8 +2171,8 @@ PassRefPtr<RenderStyle> Element::customStyleForRenderer()
 
 void Element::cloneAttributesFromElement(const Element& other)
 {
-    if (ElementAttributeData* attributeData = other.updatedAttributeData())
-        ensureUpdatedAttributeData()->cloneDataFrom(*attributeData, other, *this);
+    if (const ElementAttributeData* attributeData = other.updatedAttributeData())
+        mutableAttributeData()->cloneDataFrom(*attributeData, other, *this);
     else if (m_attributeData) {
         m_attributeData->clearAttributes(this);
         m_attributeData.clear();
@@ -2186,4 +2185,12 @@ void Element::cloneDataFromElement(const Element& other)
     copyNonAttributePropertiesFromElement(other);
 }
 
+void Element::createMutableAttributeData()
+{
+    if (!m_attributeData)
+        m_attributeData = ElementAttributeData::create();
+    else
+        m_attributeData = m_attributeData->makeMutable();
+}
+
 } // namespace WebCore
index b5b2251..2ee1f5c 100644 (file)
@@ -162,8 +162,9 @@ public:
     // Internal methods that assume the existence of attribute storage, one should use hasAttributes()
     // before calling them.
     size_t attributeCount() const;
-    Attribute* attributeItem(unsigned index) const;
-    Attribute* getAttributeItem(const QualifiedName&) const;
+    const Attribute* attributeItem(unsigned index) const;
+    const Attribute* getAttributeItem(const QualifiedName&) const;
+    Attribute* getAttributeItem(const QualifiedName&);
     size_t getAttributeItemIndex(const QualifiedName& name) const { return attributeData()->getAttributeItemIndex(name); }
     size_t getAttributeItemIndex(const AtomicString& name, bool shouldIgnoreAttributeCase) const { return attributeData()->getAttributeItemIndex(name, shouldIgnoreAttributeCase); }
 
@@ -246,10 +247,11 @@ public:
     // Only called by the parser immediately after element construction.
     void parserSetAttributes(const Vector<Attribute>&, FragmentScriptingPermission);
 
-    ElementAttributeData* attributeData() const { return m_attributeData.get(); }
-    ElementAttributeData* ensureAttributeData() const;
-    ElementAttributeData* updatedAttributeData() const;
-    ElementAttributeData* ensureUpdatedAttributeData() const;
+    const ElementAttributeData* attributeData() const { return m_attributeData.get(); }
+    ElementAttributeData* mutableAttributeData();
+    const ElementAttributeData* ensureAttributeData();
+    const ElementAttributeData* updatedAttributeData() const;
+    const ElementAttributeData* ensureUpdatedAttributeData() const;
 
     // Clones attributes only.
     void cloneAttributesFromElement(const Element&);
@@ -434,7 +436,7 @@ public:
         MemoryClassInfo<Element> info(memoryObjectInfo, this, MemoryInstrumentation::DOM);
         info.visitBaseClass<ContainerNode>(this);
         info.addInstrumentedMember(m_tagName);
-        info.addInstrumentedMember(m_attributeData.get());
+        info.addInstrumentedMember(attributeData());
     }
 
 protected:
@@ -475,8 +477,6 @@ private:
 
     bool pseudoStyleCacheIsInvalid(const RenderStyle* currentStyle, RenderStyle* newStyle);
 
-    void createAttributeData() const;
-
     virtual void updateStyleAttribute() const { }
 
 #if ENABLE(SVG)
@@ -505,11 +505,13 @@ private:
 
     void unregisterNamedFlowContentNode();
 
+    void createMutableAttributeData();
+
 private:
     ElementRareData* elementRareData() const;
     ElementRareData* ensureElementRareData();
 
-    mutable OwnPtr<ElementAttributeData> m_attributeData;
+    OwnPtr<ElementAttributeData> m_attributeData;
 };
     
 inline Element* toElement(Node* node)
@@ -569,23 +571,25 @@ inline Element* Element::nextElementSibling() const
     return static_cast<Element*>(n);
 }
 
-inline ElementAttributeData* Element::ensureAttributeData() const
+inline const ElementAttributeData* Element::updatedAttributeData() const
 {
-    if (!m_attributeData)
-        createAttributeData();
-    return m_attributeData.get();
+    updateInvalidAttributes();
+    return attributeData();
 }
 
-inline ElementAttributeData* Element::updatedAttributeData() const
+inline const ElementAttributeData* Element::ensureAttributeData()
 {
-    updateInvalidAttributes();
-    return attributeData();
+    if (attributeData())
+        return attributeData();
+    return mutableAttributeData();
 }
 
-inline ElementAttributeData* Element::ensureUpdatedAttributeData() const
+inline const ElementAttributeData* Element::ensureUpdatedAttributeData() const
 {
     updateInvalidAttributes();
-    return ensureAttributeData();
+    if (attributeData())
+        return attributeData();
+    return const_cast<Element*>(this)->mutableAttributeData();
 }
 
 inline void Element::updateName(const AtomicString& oldName, const AtomicString& newName)
@@ -634,14 +638,14 @@ inline void Element::willRemoveAttribute(const QualifiedName& name, const Atomic
 inline bool Element::fastHasAttribute(const QualifiedName& name) const
 {
     ASSERT(fastAttributeLookupAllowed(name));
-    return m_attributeData && getAttributeItem(name);
+    return attributeData() && getAttributeItem(name);
 }
 
 inline const AtomicString& Element::fastGetAttribute(const QualifiedName& name) const
 {
     ASSERT(fastAttributeLookupAllowed(name));
-    if (m_attributeData) {
-        if (Attribute* attribute = getAttributeItem(name))
+    if (attributeData()) {
+        if (const Attribute* attribute = getAttributeItem(name))
             return attribute->value();
     }
     return nullAtom;
@@ -649,13 +653,13 @@ inline const AtomicString& Element::fastGetAttribute(const QualifiedName& name)
 
 inline bool Element::hasAttributesWithoutUpdate() const
 {
-    return m_attributeData && !m_attributeData->isEmpty();
+    return attributeData() && !attributeData()->isEmpty();
 }
 
 inline const AtomicString& Element::idForStyleResolution() const
 {
     ASSERT(hasID());
-    return m_attributeData->idForStyleResolution();
+    return attributeData()->idForStyleResolution();
 }
 
 inline bool Element::isIdAttributeName(const QualifiedName& attributeName) const
@@ -684,20 +688,26 @@ inline void Element::setIdAttribute(const AtomicString& value)
 
 inline size_t Element::attributeCount() const
 {
-    ASSERT(m_attributeData);
-    return m_attributeData->length();
+    ASSERT(attributeData());
+    return attributeData()->length();
 }
 
-inline Attribute* Element::attributeItem(unsigned index) const
+inline const Attribute* Element::attributeItem(unsigned index) const
 {
-    ASSERT(m_attributeData);
-    return m_attributeData->attributeItem(index);
+    ASSERT(attributeData());
+    return attributeData()->attributeItem(index);
 }
 
-inline Attribute* Element::getAttributeItem(const QualifiedName& name) const
+inline const Attribute* Element::getAttributeItem(const QualifiedName& name) const
 {
-    ASSERT(m_attributeData);
-    return m_attributeData->getAttributeItem(name);
+    ASSERT(attributeData());
+    return attributeData()->getAttributeItem(name);
+}
+
+inline Attribute* Element::getAttributeItem(const QualifiedName& name)
+{
+    ASSERT(attributeData());
+    return mutableAttributeData()->getAttributeItem(name);
 }
 
 inline void Element::updateInvalidAttributes() const
@@ -730,6 +740,13 @@ inline bool Element::hasClass() const
     return attributeData() && attributeData()->hasClass();
 }
 
+inline ElementAttributeData* Element::mutableAttributeData()
+{
+    if (!attributeData() || !attributeData()->isMutable())
+        createMutableAttributeData();
+    return m_attributeData.get();
+}
+
 // Put here to make them inline.
 inline bool Node::hasID() const
 {
index ab2768e..6fb53d5 100644 (file)
 
 namespace WebCore {
 
+PassOwnPtr<ElementAttributeData> ElementAttributeData::createImmutable(const Vector<Attribute>& attributes)
+{
+    void* slot = WTF::fastMalloc(sizeof(ElementAttributeData) - sizeof(void*) + sizeof(Attribute) * attributes.size());
+    return adoptPtr(new (slot) ElementAttributeData(attributes));
+}
+
+ElementAttributeData::ElementAttributeData()
+    : m_isMutable(true)
+    , m_arraySize(0)
+    , m_mutableAttributeVector(new Vector<Attribute, 4>)
+{
+}
+
+ElementAttributeData::ElementAttributeData(const Vector<Attribute>& attributes)
+    : m_isMutable(false)
+    , m_arraySize(attributes.size())
+{
+    Attribute* buffer = reinterpret_cast<Attribute*>(&m_attributes);
+    for (unsigned i = 0; i < attributes.size(); ++i)
+        new (&buffer[i]) Attribute(attributes[i]);
+}
+
+ElementAttributeData::ElementAttributeData(const ElementAttributeData& other)
+    : m_inlineStyleDecl(other.m_inlineStyleDecl)
+    , m_attributeStyle(other.m_attributeStyle)
+    , m_classNames(other.m_classNames)
+    , m_idForStyleResolution(other.m_idForStyleResolution)
+    , m_isMutable(true)
+    , m_arraySize(0)
+    , m_mutableAttributeVector(new Vector<Attribute, 4>)
+{
+    // This copy constructor should only be used by makeMutable() to go from immutable to mutable.
+    ASSERT(!other.m_isMutable);
+
+    const Attribute* otherBuffer = reinterpret_cast<const Attribute*>(&other.m_attributes);
+    for (unsigned i = 0; i < other.m_arraySize; ++i)
+        m_mutableAttributeVector->append(otherBuffer[i]);
+}
+
+ElementAttributeData::~ElementAttributeData()
+{
+    if (isMutable()) {
+        ASSERT(!m_arraySize);
+        delete m_mutableAttributeVector;
+    } else {
+        Attribute* buffer = reinterpret_cast<Attribute*>(&m_attributes);
+        for (unsigned i = 0; i < m_arraySize; ++i)
+            buffer[i].~Attribute();
+    }
+}
+
 typedef Vector<RefPtr<Attr> > AttrList;
 typedef HashMap<Element*, OwnPtr<AttrList> > AttrListMap;
 
@@ -81,14 +132,14 @@ static Attr* findAttrInList(AttrList* attrList, const QualifiedName& name)
     return 0;
 }
 
-PassRefPtr<Attr> ElementAttributeData::attrIfExists(Element* element, const QualifiedName& name)
+PassRefPtr<Attr> ElementAttributeData::attrIfExists(Element* element, const QualifiedName& name) const
 {
     if (AttrList* attrList = attrListForElement(element))
         return findAttrInList(attrList, name);
     return 0;
 }
 
-PassRefPtr<Attr> ElementAttributeData::ensureAttr(Element* element, const QualifiedName& name)
+PassRefPtr<Attr> ElementAttributeData::ensureAttr(Element* element, const QualifiedName& name) const
 {
     AttrList* attrList = ensureAttrListForElement(element);
     RefPtr<Attr> attr = findAttrInList(attrList, name);
@@ -99,7 +150,7 @@ PassRefPtr<Attr> ElementAttributeData::ensureAttr(Element* element, const Qualif
     return attr.release();
 }
 
-void ElementAttributeData::setAttr(Element* element, const QualifiedName& name, Attr* attr)
+void ElementAttributeData::setAttr(Element* element, const QualifiedName& name, Attr* attr) const
 {
     AttrList* attrList = ensureAttrListForElement(element);
 
@@ -110,7 +161,7 @@ void ElementAttributeData::setAttr(Element* element, const QualifiedName& name,
     attr->attachToElement(element);
 }
 
-void ElementAttributeData::removeAttr(Element* element, const QualifiedName& name)
+void ElementAttributeData::removeAttr(Element* element, const QualifiedName& name) const
 {
     AttrList* attrList = attrListForElement(element);
     ASSERT(attrList);
@@ -127,13 +178,9 @@ void ElementAttributeData::removeAttr(Element* element, const QualifiedName& nam
     ASSERT_NOT_REACHED();
 }
 
-void ElementAttributeData::setClass(const String& className, bool shouldFoldCase)
-{
-    m_classNames.set(className, shouldFoldCase);
-}
-    
 StylePropertySet* ElementAttributeData::ensureInlineStyle(StyledElement* element)
 {
+    ASSERT(isMutable());
     if (!m_inlineStyleDecl) {
         ASSERT(element->isStyledElement());
         m_inlineStyleDecl = StylePropertySet::create(strictToCSSParserMode(element->isHTMLElement() && !element->document()->inQuirksMode()));
@@ -143,6 +190,7 @@ StylePropertySet* ElementAttributeData::ensureInlineStyle(StyledElement* element
 
 StylePropertySet* ElementAttributeData::ensureMutableInlineStyle(StyledElement* element)
 {
+    ASSERT(isMutable());
     if (m_inlineStyleDecl && !m_inlineStyleDecl->isMutable()) {
         m_inlineStyleDecl = m_inlineStyleDecl->copy();
         return m_inlineStyleDecl.get();
@@ -150,7 +198,7 @@ StylePropertySet* ElementAttributeData::ensureMutableInlineStyle(StyledElement*
     return ensureInlineStyle(element);
 }
     
-void ElementAttributeData::updateInlineStyleAvoidingMutation(StyledElement* element, const String& text)
+void ElementAttributeData::updateInlineStyleAvoidingMutation(StyledElement* element, const String& text) const
 {
     // We reconstruct the property set instead of mutating if there is no CSSOM wrapper.
     // This makes wrapperless property sets immutable and so cacheable.
@@ -161,7 +209,7 @@ void ElementAttributeData::updateInlineStyleAvoidingMutation(StyledElement* elem
     m_inlineStyleDecl->parseDeclaration(text, element->document()->elementSheet()->contents());
 }
 
-void ElementAttributeData::destroyInlineStyle(StyledElement* element)
+void ElementAttributeData::destroyInlineStyle(StyledElement* element) const
 {
     if (!m_inlineStyleDecl)
         return;
@@ -171,10 +219,12 @@ void ElementAttributeData::destroyInlineStyle(StyledElement* element)
 
 void ElementAttributeData::addAttribute(const Attribute& attribute, Element* element, EInUpdateStyleAttribute inUpdateStyleAttribute)
 {
+    ASSERT(isMutable());
+
     if (element && inUpdateStyleAttribute == NotInUpdateStyleAttribute)
         element->willModifyAttribute(attribute.name(), nullAtom, attribute.value());
 
-    m_attributes.append(attribute);
+    m_mutableAttributeVector->append(attribute);
 
     if (element && inUpdateStyleAttribute == NotInUpdateStyleAttribute)
         element->didAddAttribute(attribute);
@@ -182,9 +232,10 @@ void ElementAttributeData::addAttribute(const Attribute& attribute, Element* ele
 
 void ElementAttributeData::removeAttribute(size_t index, Element* element, EInUpdateStyleAttribute inUpdateStyleAttribute)
 {
+    ASSERT(isMutable());
     ASSERT(index < length());
 
-    Attribute& attribute = m_attributes[index];
+    Attribute& attribute = m_mutableAttributeVector->at(index);
     QualifiedName name = attribute.name();
 
     if (element && inUpdateStyleAttribute == NotInUpdateStyleAttribute)
@@ -193,7 +244,7 @@ void ElementAttributeData::removeAttribute(size_t index, Element* element, EInUp
     if (RefPtr<Attr> attr = attrIfExists(element, name))
         attr->detachFromElementWithValue(attribute.value());
 
-    m_attributes.remove(index);
+    m_mutableAttributeVector->remove(index);
 
     if (element && inUpdateStyleAttribute == NotInUpdateStyleAttribute)
         element->didRemoveAttribute(name);
@@ -209,21 +260,23 @@ bool ElementAttributeData::isEquivalent(const ElementAttributeData* other) const
         return false;
 
     for (unsigned i = 0; i < len; i++) {
-        Attribute* otherAttr = other->getAttributeItem(m_attributes[i].name());
-        if (!otherAttr || m_attributes[i].value() != otherAttr->value())
+        const Attribute* attribute = attributeItem(i);
+        const Attribute* otherAttr = other->getAttributeItem(attribute->name());
+        if (!otherAttr || attribute->value() != otherAttr->value())
             return false;
     }
 
     return true;
 }
 
-void ElementAttributeData::detachAttrObjectsFromElement(Element* element)
+void ElementAttributeData::detachAttrObjectsFromElement(Element* element) const
 {
     ASSERT(element->hasAttrList());
 
-    for (unsigned i = 0; i < m_attributes.size(); ++i) {
-        if (RefPtr<Attr> attr = attrIfExists(element, m_attributes[i].name()))
-            attr->detachFromElementWithValue(m_attributes[i].value());
+    for (unsigned i = 0; i < length(); ++i) {
+        const Attribute* attribute = attributeItem(i);
+        if (RefPtr<Attr> attr = attrIfExists(element, attribute->name()))
+            attr->detachFromElementWithValue(attribute->value());
     }
 
     // The loop above should have cleaned out this element's Attr map.
@@ -233,15 +286,16 @@ void ElementAttributeData::detachAttrObjectsFromElement(Element* element)
 size_t ElementAttributeData::getAttributeItemIndexSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const
 {
     // Continue to checking case-insensitively and/or full namespaced names if necessary:
-    for (unsigned i = 0; i < m_attributes.size(); ++i) {
-        if (!m_attributes[i].name().hasPrefix()) {
-            if (shouldIgnoreAttributeCase && equalIgnoringCase(name, m_attributes[i].localName()))
+    for (unsigned i = 0; i < length(); ++i) {
+        const Attribute* attribute = attributeItem(i);
+        if (!attribute->name().hasPrefix()) {
+            if (shouldIgnoreAttributeCase && equalIgnoringCase(name, attribute->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, m_attributes[i].name().toString(), shouldIgnoreAttributeCase))
+            if (equalPossiblyIgnoringCase(name, attribute->name().toString(), shouldIgnoreAttributeCase))
                 return i;
         }
     }
@@ -250,6 +304,9 @@ size_t ElementAttributeData::getAttributeItemIndexSlowCase(const AtomicString& n
 
 void ElementAttributeData::cloneDataFrom(const ElementAttributeData& sourceData, const Element& sourceElement, Element& targetElement)
 {
+    // FIXME: Cloned elements could start out with immutable attribute data.
+    ASSERT(isMutable());
+
     const AtomicString& oldID = targetElement.getIdAttribute();
     const AtomicString& newID = sourceElement.getIdAttribute();
 
@@ -263,13 +320,24 @@ void ElementAttributeData::cloneDataFrom(const ElementAttributeData& sourceData,
         targetElement.updateName(oldName, newName);
 
     clearAttributes(&targetElement);
-    m_attributes = sourceData.m_attributes;
-    for (unsigned i = 0; i < m_attributes.size(); ++i) {
-        if (targetElement.isStyledElement() && m_attributes[i].name() == HTMLNames::styleAttr) {
-            static_cast<StyledElement&>(targetElement).styleAttributeChanged(m_attributes[i].value(), StyledElement::DoNotReparseStyleAttribute);
+
+    if (sourceData.isMutable())
+        *m_mutableAttributeVector = *sourceData.m_mutableAttributeVector;
+    else {
+        ASSERT(m_mutableAttributeVector->isEmpty());
+        m_mutableAttributeVector->reserveInitialCapacity(sourceData.m_arraySize);
+        const Attribute* sourceBuffer = reinterpret_cast<const Attribute*>(&sourceData.m_attributes);
+        for (unsigned i = 0; i < sourceData.m_arraySize; ++i)
+            m_mutableAttributeVector->uncheckedAppend(sourceBuffer[i]);
+    }
+
+    for (unsigned i = 0; i < length(); ++i) {
+        const Attribute& attribute = m_mutableAttributeVector->at(i);
+        if (targetElement.isStyledElement() && attribute.name() == HTMLNames::styleAttr) {
+            static_cast<StyledElement&>(targetElement).styleAttributeChanged(attribute.value(), StyledElement::DoNotReparseStyleAttribute);
             continue;
         }
-        targetElement.attributeChanged(m_attributes[i]);
+        targetElement.attributeChanged(attribute);
     }
 
     if (targetElement.isStyledElement() && sourceData.m_inlineStyleDecl) {
@@ -282,40 +350,42 @@ void ElementAttributeData::cloneDataFrom(const ElementAttributeData& sourceData,
 
 void ElementAttributeData::clearAttributes(Element* element)
 {
+    ASSERT(isMutable());
+
     if (element->hasAttrList())
         detachAttrObjectsFromElement(element);
 
     clearClass();
-    m_attributes.clear();
+    m_mutableAttributeVector->clear();
 }
 
 void ElementAttributeData::replaceAttribute(size_t index, const Attribute& attribute, Element* element)
 {
+    ASSERT(isMutable());
     ASSERT(element);
     ASSERT(index < length());
 
-    element->willModifyAttribute(attribute.name(), m_attributes[index].value(), attribute.value());
-    m_attributes[index] = attribute;
+    element->willModifyAttribute(attribute.name(), m_mutableAttributeVector->at(index).value(), attribute.value());
+    (*m_mutableAttributeVector)[index] = attribute;
     element->didModifyAttribute(attribute);
 }
 
 PassRefPtr<Attr> ElementAttributeData::getAttributeNode(const String& name, bool shouldIgnoreAttributeCase, Element* element) const
 {
     ASSERT(element);
-    Attribute* attribute = getAttributeItem(name, shouldIgnoreAttributeCase);
+    const Attribute* attribute = getAttributeItem(name, shouldIgnoreAttributeCase);
     if (!attribute)
         return 0;
-    return const_cast<ElementAttributeData*>(this)->ensureAttr(element, attribute->name());
+    return ensureAttr(element, attribute->name());
 }
 
 PassRefPtr<Attr> ElementAttributeData::getAttributeNode(const QualifiedName& name, Element* element) const
 {
     ASSERT(element);
-    Attribute* attribute = getAttributeItem(name);
+    const Attribute* attribute = getAttributeItem(name);
     if (!attribute)
         return 0;
-    return const_cast<ElementAttributeData*>(this)->ensureAttr(element, attribute->name());
+    return ensureAttr(element, attribute->name());
 }
 
-
 }
index 57cf368..1c2b2fe 100644 (file)
@@ -37,50 +37,43 @@ namespace WebCore {
 class Attr;
 class Element;
 
-inline Attribute* findAttributeInVector(const Vector<Attribute>& attributes, const QualifiedName& name)
-{
-    for (unsigned i = 0; i < attributes.size(); ++i) {
-        if (attributes.at(i).name().matches(name))
-            return &const_cast<Vector<Attribute>& >(attributes).at(i);
-    }
-    return 0;
-}
-
 enum EInUpdateStyleAttribute { NotInUpdateStyleAttribute, InUpdateStyleAttribute };
 
 class ElementAttributeData {
     WTF_MAKE_FAST_ALLOCATED;
 public:
-    static PassOwnPtr<ElementAttributeData> create()
-    {
-        return adoptPtr(new ElementAttributeData);
-    }
+    static PassOwnPtr<ElementAttributeData> create() { return adoptPtr(new ElementAttributeData); }
+    static PassOwnPtr<ElementAttributeData> createImmutable(const Vector<Attribute>&);
+    ~ElementAttributeData();
 
     void clearClass() { m_classNames.clear(); }
-    void setClass(const String& className, bool shouldFoldCase);
+    void setClass(const AtomicString& className, bool shouldFoldCase) const { m_classNames.set(className, shouldFoldCase); }
     const SpaceSplitString& classNames() const { return m_classNames; }
 
     const AtomicString& idForStyleResolution() const { return m_idForStyleResolution; }
-    void setIdForStyleResolution(const AtomicString& newId) { m_idForStyleResolution = newId; }
+    void setIdForStyleResolution(const AtomicString& newId) const { m_idForStyleResolution = newId; }
 
+    const StylePropertySet* inlineStyle() const { return m_inlineStyleDecl.get(); }
     StylePropertySet* inlineStyle() { return m_inlineStyleDecl.get(); }
     StylePropertySet* ensureInlineStyle(StyledElement*);
     StylePropertySet* ensureMutableInlineStyle(StyledElement*);
-    void updateInlineStyleAvoidingMutation(StyledElement*, const String& text);
-    void destroyInlineStyle(StyledElement*);
+    void updateInlineStyleAvoidingMutation(StyledElement*, const String& text) const;
+    void destroyInlineStyle(StyledElement*) const;
 
     StylePropertySet* attributeStyle() const { return m_attributeStyle.get(); }
-    void setAttributeStyle(PassRefPtr<StylePropertySet> style) { m_attributeStyle = style; }
+    void setAttributeStyle(PassRefPtr<StylePropertySet> style) const { m_attributeStyle = style; }
 
-    size_t length() const { return m_attributes.size(); }
-    bool isEmpty() const { return m_attributes.isEmpty(); }
+    size_t length() const;
+    bool isEmpty() const { return !length(); }
 
     PassRefPtr<Attr> getAttributeNode(const String&, bool shouldIgnoreAttributeCase, Element*) const;
     PassRefPtr<Attr> getAttributeNode(const QualifiedName&, Element*) const;
 
     // Internal interface.
-    Attribute* attributeItem(unsigned index) const { return &const_cast<ElementAttributeData*>(this)->m_attributes[index]; }
-    Attribute* getAttributeItem(const QualifiedName& name) const { return findAttributeInVector(m_attributes, name); }
+    const Attribute* attributeItem(unsigned index) const;
+    const Attribute* getAttributeItem(const QualifiedName&) const;
+    Attribute* attributeItem(unsigned index);
+    Attribute* getAttributeItem(const QualifiedName&);
     size_t getAttributeItemIndex(const QualifiedName&) const;
     size_t getAttributeItemIndex(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
 
@@ -95,46 +88,62 @@ public:
 
     bool isEquivalent(const ElementAttributeData* other) const;
 
-    void setAttr(Element*, const QualifiedName&, Attr*);
-    void removeAttr(Element*, const QualifiedName&);
-    PassRefPtr<Attr> attrIfExists(Element*, const QualifiedName&);
-    PassRefPtr<Attr> ensureAttr(Element*, const QualifiedName&);
+    void setAttr(Element*, const QualifiedName&, Attr*) const;
+    void removeAttr(Element*, const QualifiedName&) const;
+    PassRefPtr<Attr> attrIfExists(Element*, const QualifiedName&) const;
+    PassRefPtr<Attr> ensureAttr(Element*, const QualifiedName&) const;
+    void detachAttrObjectsFromElement(Element*) const;
 
     void reportMemoryUsage(MemoryObjectInfo* memoryObjectInfo) const
     {
-        MemoryClassInfo<ElementAttributeData> info(memoryObjectInfo, this, MemoryInstrumentation::DOM);
+        MemoryClassInfo<ElementAttributeData> info(memoryObjectInfo, this, MemoryInstrumentation::DOM, m_arraySize * sizeof(Attribute));
         info.addInstrumentedMember(m_inlineStyleDecl.get());
         info.addInstrumentedMember(m_attributeStyle.get());
         info.addMember(m_classNames);
         info.addString(m_idForStyleResolution);
-        info.addVector(m_attributes);
+        if (m_isMutable)
+            info.addVector(*m_mutableAttributeVector);
     }
 
 private:
     friend class Element;
     friend class HTMLConstructionSite;
 
-    ElementAttributeData()
-    {
-    }
-
-    const Vector<Attribute>& attributeVector() const { return m_attributes; }
-    Vector<Attribute> clonedAttributeVector() const { return m_attributes; }
+    ElementAttributeData();
+    ElementAttributeData(const ElementAttributeData&);
+    ElementAttributeData(const Vector<Attribute>&);
 
-    void detachAttrObjectsFromElement(Element*);
-    Attribute* getAttributeItem(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
+    Attribute* getAttributeItem(const AtomicString& name, bool shouldIgnoreAttributeCase);
+    const Attribute* getAttributeItem(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
     size_t getAttributeItemIndexSlowCase(const AtomicString& name, bool shouldIgnoreAttributeCase) const;
     void cloneDataFrom(const ElementAttributeData& sourceData, const Element& sourceElement, Element& targetElement);
     void clearAttributes(Element*);
     void replaceAttribute(size_t index, const Attribute&, Element*);
 
-    RefPtr<StylePropertySet> m_inlineStyleDecl;
-    RefPtr<StylePropertySet> m_attributeStyle;
-    SpaceSplitString m_classNames;
-    AtomicString m_idForStyleResolution;
-    Vector<Attribute> m_attributes;
+    bool isMutable() const { return m_isMutable; }
+    PassOwnPtr<ElementAttributeData> makeMutable() const { return adoptPtr(new ElementAttributeData(*this)); }
+
+    mutable RefPtr<StylePropertySet> m_inlineStyleDecl;
+    mutable RefPtr<StylePropertySet> m_attributeStyle;
+    mutable SpaceSplitString m_classNames;
+    mutable AtomicString m_idForStyleResolution;
+
+    unsigned m_isMutable : 1;
+    unsigned m_arraySize : 31;
+
+    union {
+        Vector<Attribute, 4>* m_mutableAttributeVector;
+        void* m_attributes;
+    };
 };
 
+inline size_t ElementAttributeData::length() const
+{
+    if (isMutable())
+        return m_mutableAttributeVector->size();
+    return m_arraySize;
+}
+
 inline void ElementAttributeData::removeAttribute(const QualifiedName& name, Element* element)
 {
     size_t index = getAttributeItemIndex(name);
@@ -142,21 +151,28 @@ inline void ElementAttributeData::removeAttribute(const QualifiedName& name, Ele
         return;
 
     removeAttribute(index, element);
-    return;
 }
 
-inline Attribute* ElementAttributeData::getAttributeItem(const AtomicString& name, bool shouldIgnoreAttributeCase) const
+inline Attribute* ElementAttributeData::getAttributeItem(const AtomicString& name, bool shouldIgnoreAttributeCase)
+{
+    size_t index = getAttributeItemIndex(name, shouldIgnoreAttributeCase);
+    if (index != notFound)
+        return attributeItem(index);
+    return 0;
+}
+
+inline const Attribute* ElementAttributeData::getAttributeItem(const AtomicString& name, bool shouldIgnoreAttributeCase) const
 {
     size_t index = getAttributeItemIndex(name, shouldIgnoreAttributeCase);
     if (index != notFound)
-        return &const_cast<ElementAttributeData*>(this)->m_attributes[index];
+        return attributeItem(index);
     return 0;
 }
 
 inline size_t ElementAttributeData::getAttributeItemIndex(const QualifiedName& name) const
 {
-    for (unsigned i = 0; i < m_attributes.size(); ++i) {
-        if (m_attributes.at(i).name().matches(name))
+    for (unsigned i = 0; i < length(); ++i) {
+        if (attributeItem(i)->name().matches(name))
             return i;
     }
     return notFound;
@@ -171,8 +187,9 @@ inline size_t ElementAttributeData::getAttributeItemIndex(const AtomicString& na
 
     // Optimize for the case where the attribute exists and its name exactly matches.
     for (unsigned i = 0; i < len; ++i) {
-        if (!m_attributes[i].name().hasPrefix()) {
-            if (name == m_attributes[i].localName())
+        const Attribute* attribute = attributeItem(i);
+        if (!attribute->name().hasPrefix()) {
+            if (name == attribute->localName())
                 return i;
         } else
             doSlowCheck = true;
@@ -183,6 +200,40 @@ inline size_t ElementAttributeData::getAttributeItemIndex(const AtomicString& na
     return notFound;
 }
 
+inline const Attribute* ElementAttributeData::getAttributeItem(const QualifiedName& name) const
+{
+    for (unsigned i = 0; i < length(); ++i) {
+        if (attributeItem(i)->name().matches(name))
+            return attributeItem(i);
+    }
+    return 0;
+}
+
+inline Attribute* ElementAttributeData::getAttributeItem(const QualifiedName& name)
+{
+    for (unsigned i = 0; i < length(); ++i) {
+        if (attributeItem(i)->name().matches(name))
+            return attributeItem(i);
+    }
+    return 0;
+}
+
+inline const Attribute* ElementAttributeData::attributeItem(unsigned index) const
+{
+    ASSERT(index < length());
+    if (isMutable())
+        return &m_mutableAttributeVector->at(index);
+    const Attribute* buffer = reinterpret_cast<const Attribute*>(&m_attributes);
+    return &buffer[index];
+}
+
+inline Attribute* ElementAttributeData::attributeItem(unsigned index)
+{
+    ASSERT(isMutable());
+    ASSERT(index < length());
+    return &m_mutableAttributeVector->at(index);
+}
+
 }
 
 #endif // ElementAttributeData_h
index 2e3f413..f30c3fc 100644 (file)
@@ -1703,7 +1703,7 @@ bool Node::isDefaultNamespace(const AtomicString& namespaceURIMaybeEmpty) const
 
             if (elem->hasAttributes()) {
                 for (unsigned i = 0; i < elem->attributeCount(); i++) {
-                    Attribute* attr = elem->attributeItem(i);
+                    const Attribute* attr = elem->attributeItem(i);
                     
                     if (attr->localName() == xmlnsAtom)
                         return attr->value() == namespaceURI;
@@ -1787,7 +1787,7 @@ String Node::lookupNamespaceURI(const String &prefix) const
             
             if (elem->hasAttributes()) {
                 for (unsigned i = 0; i < elem->attributeCount(); i++) {
-                    Attribute* attr = elem->attributeItem(i);
+                    const Attribute* attr = elem->attributeItem(i);
                     
                     if (attr->prefix() == xmlnsAtom && attr->localName() == prefix) {
                         if (!attr->value().isEmpty())
@@ -1842,7 +1842,7 @@ String Node::lookupNamespacePrefix(const AtomicString &_namespaceURI, const Elem
     const Element* thisElement = toElement(this);
     if (thisElement->hasAttributes()) {
         for (unsigned i = 0; i < thisElement->attributeCount(); i++) {
-            Attribute* attr = thisElement->attributeItem(i);
+            const Attribute* attr = thisElement->attributeItem(i);
             
             if (attr->prefix() == xmlnsAtom && attr->value() == _namespaceURI
                     && originalElement->lookupNamespaceURI(attr->localName()) == _namespaceURI)
@@ -1991,7 +1991,7 @@ unsigned short Node::compareDocumentPosition(Node* otherNode)
             // the same nodeType are inserted into or removed from the direct container. This would be the case, for example, 
             // when comparing two attributes of the same element, and inserting or removing additional attributes might change 
             // the order between existing attributes.
-            Attribute* attribute = owner1->attributeItem(i);
+            const Attribute* attribute = owner1->attributeItem(i);
             if (attr1->qualifiedName() == attribute->name())
                 return DOCUMENT_POSITION_IMPLEMENTATION_SPECIFIC | DOCUMENT_POSITION_FOLLOWING;
             if (attr2->qualifiedName() == attribute->name())
index d1265d9..9ae672f 100644 (file)
@@ -142,7 +142,7 @@ StyledElement::~StyledElement()
 
 CSSStyleDeclaration* StyledElement::style()
 {
-    return ensureAttributeData()->ensureMutableInlineStyle(this)->ensureInlineCSSStyleDeclaration(this); 
+    return ensureInlineStyle()->ensureInlineCSSStyleDeclaration(this);
 }
 
 void StyledElement::attributeChanged(const Attribute& attribute)
@@ -173,7 +173,7 @@ void StyledElement::classAttributeChanged(const AtomicString& newClassString)
         if (DOMTokenList* classList = optionalClassList())
             static_cast<ClassList*>(classList)->reset(newClassString);
     } else if (attributeData())
-        attributeData()->clearClass();
+        mutableAttributeData()->clearClass();
     setNeedsStyleRecalc();
 }
 
@@ -210,21 +210,21 @@ void StyledElement::inlineStyleChanged()
     
 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, int identifier, bool important)
 {
-    ensureAttributeData()->ensureMutableInlineStyle(this)->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important);
+    ensureInlineStyle()->setProperty(propertyID, cssValuePool().createIdentifierValue(identifier), important);
     inlineStyleChanged();
     return true;
 }
 
 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, double value, CSSPrimitiveValue::UnitTypes unit, bool important)
 {
-    ensureAttributeData()->ensureMutableInlineStyle(this)->setProperty(propertyID, cssValuePool().createValue(value, unit), important);
+    ensureInlineStyle()->setProperty(propertyID, cssValuePool().createValue(value, unit), important);
     inlineStyleChanged();
     return true;
 }
 
 bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, const String& value, bool important)
 {
-    bool changes = ensureAttributeData()->ensureMutableInlineStyle(this)->setProperty(propertyID, value, important, document()->elementSheet()->contents());
+    bool changes = ensureInlineStyle()->setProperty(propertyID, value, important, document()->elementSheet()->contents());
     if (changes)
         inlineStyleChanged();
     return changes;
@@ -232,10 +232,9 @@ bool StyledElement::setInlineStyleProperty(CSSPropertyID propertyID, const Strin
 
 bool StyledElement::removeInlineStyleProperty(CSSPropertyID propertyID)
 {
-    StylePropertySet* inlineStyle = attributeData() ? attributeData()->inlineStyle() : 0;
-    if (!inlineStyle)
+    if (!attributeData() || !attributeData()->inlineStyle())
         return false;
-    bool changes = inlineStyle->removeProperty(propertyID);
+    bool changes = ensureInlineStyle()->removeProperty(propertyID);
     if (changes)
         inlineStyleChanged();
     return changes;
@@ -243,7 +242,7 @@ bool StyledElement::removeInlineStyleProperty(CSSPropertyID propertyID)
 
 void StyledElement::addSubresourceAttributeURLs(ListHashSet<KURL>& urls) const
 {
-    if (StylePropertySet* inlineStyle = attributeData() ? attributeData()->inlineStyle() : 0)
+    if (const StylePropertySet* inlineStyle = attributeData() ? attributeData()->inlineStyle() : 0)
         inlineStyle->addSubresourceStyleURLs(urls, document()->elementSheet()->contents());
 }
 
@@ -263,7 +262,7 @@ void StyledElement::makePresentationAttributeCacheKey(PresentationAttributeCache
         return;
     unsigned size = attributeCount();
     for (unsigned i = 0; i < size; ++i) {
-        Attribute* attribute = attributeItem(i);
+        const Attribute* attribute = attributeItem(i);
         if (!isPresentationAttribute(attribute->name()))
             continue;
         if (!attribute->namespaceURI().isNull())
@@ -313,7 +312,7 @@ void StyledElement::updateAttributeStyle()
         style = StylePropertySet::create(isSVGElement() ? SVGAttributeMode : CSSQuirksMode);
         unsigned size = attributeCount();
         for (unsigned i = 0; i < size; ++i) {
-            Attribute* attribute = attributeItem(i);
+            const Attribute* attribute = attributeItem(i);
             collectStyleForAttribute(*attribute, style.get());
         }
     }
index abd49ad..0f61d1c 100644 (file)
@@ -41,7 +41,7 @@ public:
     void invalidateStyleAttribute();
 
     const StylePropertySet* inlineStyle() const { return attributeData() ? attributeData()->inlineStyle() : 0; }
-    const StylePropertySet* ensureInlineStyle() { return ensureAttributeData()->ensureInlineStyle(this); }
+    StylePropertySet* ensureInlineStyle() { return mutableAttributeData()->ensureMutableInlineStyle(this); }
     
     // Unlike StylePropertySet setters, these implement invalidation.
     bool setInlineStyleProperty(CSSPropertyID, int identifier, bool important = false);
index f0ba766..a39e88b 100644 (file)
@@ -110,7 +110,7 @@ static void completeURLs(Node* node, const String& baseURL)
                 continue;
             unsigned length = e->attributeCount();
             for (unsigned i = 0; i < length; i++) {
-                Attribute* attribute = e->attributeItem(i);
+                const Attribute* attribute = e->attributeItem(i);
                 if (e->isURLAttribute(*attribute))
                     changes.append(AttributeChange(e, attribute->name(), KURL(parsedBaseURL, attribute->value()).string()));
             }
@@ -294,7 +294,7 @@ void StyledMarkupAccumulator::appendElement(StringBuilder& out, Element* element
     const bool shouldAnnotateOrForceInline = element->isHTMLElement() && (shouldAnnotate() || addDisplayInline);
     const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element);
     for (unsigned int i = 0; i < length; i++) {
-        Attribute* attribute = element->attributeItem(i);
+        const Attribute* attribute = element->attributeItem(i);
         // We'll handle the style attribute separately, below.
         if (attribute->name() == styleAttr && shouldOverrideStyleAttr)
             continue;
index b9c6e41..15e32ee 100644 (file)
@@ -120,9 +120,9 @@ void HTMLEmbedElement::parametersForPlugin(Vector<String>& paramNames, Vector<St
         return;
 
     for (unsigned i = 0; i < attributeCount(); ++i) {
-        Attribute* it = attributeItem(i);
-        paramNames.append(it->localName().string());
-        paramValues.append(it->value().string());
+        const Attribute* attribute = attributeItem(i);
+        paramNames.append(attribute->localName().string());
+        paramValues.append(attribute->value().string());
     }
 }
 
index 8c5b817..f72be0f 100644 (file)
@@ -192,11 +192,11 @@ void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<S
     // Turn the attributes of the <object> element into arrays, but don't override <param> values.
     if (hasAttributes()) {
         for (unsigned i = 0; i < attributeCount(); ++i) {
-            Attribute* it = attributeItem(i);
-            const AtomicString& name = it->name().localName();
+            const Attribute* attribute = attributeItem(i);
+            const AtomicString& name = attribute->name().localName();
             if (!uniqueParamNames.contains(name.impl())) {
                 paramNames.append(name.string());
-                paramValues.append(it->value().string());
+                paramValues.append(attribute->value().string());
             }
         }
     }
index 87a518e..6267872 100644 (file)
@@ -206,7 +206,7 @@ void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken*
     if (token->attributes().isEmpty())
         return;
 
-    ElementAttributeData* elementAttributeData = element->ensureAttributeData();
+    ElementAttributeData* elementAttributeData = element->mutableAttributeData();
 
     for (unsigned i = 0; i < token->attributes().size(); ++i) {
         const Attribute& tokenAttribute = token->attributes().at(i);
index 775c464..c03dce4 100644 (file)
@@ -177,8 +177,8 @@ bool DOMPatchSupport::innerPatchNode(Digest* oldDigest, Digest* newDigest, Excep
         // FIXME: Create a function in Element for removing all properties. Take in account whether did/willModifyAttribute are important.
         if (oldElement->hasAttributesWithoutUpdate()) {
             while (oldElement->attributeCount()) {
-                Attribute* attr = oldElement->attributeItem(0);
-                if (!m_domEditor->removeAttribute(oldElement, attr->localName(), ec))
+                const Attribute* attribute = oldElement->attributeItem(0);
+                if (!m_domEditor->removeAttribute(oldElement, attribute->localName(), ec))
                     return false;
             }
         }
index 9fb5d7e..c911d54 100644 (file)
@@ -73,9 +73,9 @@ static bool isCharsetSpecifyingNode(Node* node)
     HTMLMetaCharsetParser::AttributeList attributes;
     if (element->hasAttributes()) {
         for (unsigned i = 0; i < element->attributeCount(); ++i) {
-            Attribute* item = element->attributeItem(i);
+            const Attribute* attribute = element->attributeItem(i);
             // FIXME: We should deal appropriately with the attribute if they have a namespace.
-            attributes.append(std::make_pair(item->name().toString(), item->value().string()));
+            attributes.append(std::make_pair(attribute->name().toString(), attribute->value().string()));
         }
     }
     TextEncoding textEncoding = HTMLMetaCharsetParser::encodingFromMetaAttributes(attributes);
index 2adb8b6..261a400 100644 (file)
@@ -59,7 +59,7 @@ struct SVGSynchronizableAnimatedProperty {
         // 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.
-        ElementAttributeData* attributeData = ownerElement->ensureUpdatedAttributeData();
+        ElementAttributeData* attributeData = ownerElement->mutableAttributeData();
         Attribute* old = attributeData->getAttributeItem(attrName);
         if (old && value.isNull())
             attributeData->removeAttribute(old->name(), ownerElement);
index 707cc7f..3c90e3b 100644 (file)
@@ -587,7 +587,7 @@ Value FunLang::evaluate() const
 {
     String lang = arg(0)->evaluate().toString();
 
-    Attribute* languageAttribute = 0;
+    const Attribute* languageAttribute = 0;
     Node* node = evaluationContext().node.get();
     while (node) {
         if (node->isElementNode()) {
index cb54726..6bb0cb8 100644 (file)
 
 namespace WebCore {
 
+static inline Attribute* findAttributeInVector(Vector<Attribute>& attributes, const QualifiedName& name)
+{
+    for (unsigned i = 0; i < attributes.size(); ++i) {
+        if (attributes.at(i).name().matches(name))
+            return &attributes.at(i);
+    }
+    return 0;
+}
+
+
 class DoctypeDataBase {
     WTF_MAKE_NONCOPYABLE(DoctypeDataBase);
 public:
index e3db632..9324bd7 100644 (file)
@@ -605,11 +605,11 @@ XMLDocumentParser::XMLDocumentParser(DocumentFragment* fragment, Element* parent
         Element* element = elemStack.last();
         if (element->hasAttributes()) {
             for (unsigned i = 0; i < element->attributeCount(); i++) {
-                Attribute* attr = element->attributeItem(i);
-                if (attr->localName() == xmlnsAtom)
-                    m_defaultNamespaceURI = attr->value();
-                else if (attr->prefix() == xmlnsAtom)
-                    m_prefixToNamespaceMap.set(attr->localName(), attr->value());
+                const Attribute* attribute = element->attributeItem(i);
+                if (attribute->localName() == xmlnsAtom)
+                    m_defaultNamespaceURI = attribute->value();
+                else if (attribute->prefix() == xmlnsAtom)
+                    m_prefixToNamespaceMap.set(attribute->localName(), attribute->value());
             }
         }
     }
index 59d9449..cbec8a0 100644 (file)
@@ -138,9 +138,9 @@ XMLDocumentParser::XMLDocumentParser(DocumentFragment* fragment, Element* parent
 
     QXmlStreamNamespaceDeclarations namespaces;
     for (Element* element = elemStack.last(); !elemStack.isEmpty(); elemStack.removeLast()) {
-        if (ElementAttributeData* attrs = element->updatedAttributeData()) {
+        if (const ElementAttributeData* attrs = element->updatedAttributeData()) {
             for (unsigned i = 0; i < attrs->length(); i++) {
-                Attribute* attr = attrs->attributeItem(i);
+                const Attribute* attr = attrs->attributeItem(i);
                 if (attr->localName() == "xmlns")
                     m_defaultNamespaceURI = attr->value();
                 else if (attr->prefix() == "xmlns")
index db213cd..4cd3ac9 100644 (file)
@@ -82,11 +82,11 @@ XMLTreeBuilder::XMLTreeBuilder(NewXMLDocumentParser* parser, DocumentFragment* f
         element = nodeStack.last();
         if (element->hasAttributes()) {
             for (size_t i = 0; i < element->attributeCount(); ++i) {
-                Attribute* attr = element->attributeItem(i);
-                if (attr->localName() == xmlnsAtom)
-                    stackItem.setNamespaceURI(attr->value());
-                else if (attr->prefix() == xmlnsAtom)
-                    stackItem.setNamespaceURI(attr->localName(), attr->value());
+                const Attribute* attribute = element->attributeItem(i);
+                if (attribute->localName() == xmlnsAtom)
+                    stackItem.setNamespaceURI(attribute->value());
+                else if (attribute->prefix() == xmlnsAtom)
+                    stackItem.setNamespaceURI(attribute->localName(), attribute->value());
             }
         }
     }