Avoid creating NamedNodeMap unnecessarily
authorcaio.oliveira@openbossa.org <caio.oliveira@openbossa.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 2 Feb 2012 00:20:36 +0000 (00:20 +0000)
committercaio.oliveira@openbossa.org <caio.oliveira@openbossa.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 2 Feb 2012 00:20:36 +0000 (00:20 +0000)
https://bugs.webkit.org/show_bug.cgi?id=77574

Reviewed by Ryosuke Niwa.

Source/WebCore:

The method Element::attributes() was being used for multiple things in our
codebase: (1) as the getter for NamedNodeMap exposed to DOM, (2) as a way to other WebCore
code get the "attribute storage" (currently inside NamedNodeMap), and (3) as a way to
get the attribute storage creating one if necessary.

This commit separate the jobs in different functions:

1) attributes() keeps being the DOM getter, and loses its boolean parameter.

2) updatedAttributes() updates the invalid attributes and returns the attribute
storage. If we don't have one, return 0.

3) ensureUpdatedAttributes() updates the invalid attributes and forces the
creation of attribute storage to return.

There is also another way to get to the attribute storage currently, via
attributeMap(), which doesn't update the attributes for possible changes in Style
or SVG attributes.

Note that the new functions are not available in Node class, so C++ code manipulating
attributes should cast to Element.

This separation also made easier to spot and fix some places where we do not
need to create the attribute storage if it doesn't exist.

No new tests, this commit shouldn't change the behavior of existing code.

* css/SelectorChecker.cpp:
(WebCore::SelectorChecker::checkOneSelector):
* dom/DatasetDOMStringMap.cpp:
(WebCore::DatasetDOMStringMap::getNames):
(WebCore::DatasetDOMStringMap::item):
(WebCore::DatasetDOMStringMap::contains):
* dom/Document.cpp:
(WebCore::Document::importNode):
* dom/Element.cpp:
(WebCore::Element::setAttribute):
(WebCore::Element::hasAttributes):
(WebCore::Element::setAttributeNode):
(WebCore::Element::setAttributeNodeNS):
(WebCore::Element::removeAttributeNode):
(WebCore::Element::getAttributeNode):
(WebCore::Element::getAttributeNodeNS):
(WebCore::Element::hasAttribute):
(WebCore::Element::hasAttributeNS):
(WebCore::Element::normalizeAttributes):
* dom/Element.h:
(Element):
(WebCore::Element::attributes):
(WebCore::Element::ensureAttributeData):
(WebCore::Element::ensureUpdatedAttributes):
(WebCore::Element::updatedAttributes):
(WebCore::Element::setAttributesFromElement):
(WebCore::Element::ensureAttributeMap): Made const to be reused by ensureUpdatedAttributes().
(WebCore::Element::updateInvalidAttributes):
(WebCore):
* dom/NamedNodeMap.cpp:
(WebCore::NamedNodeMap::mapsEquivalent): Having no attributes is equivalent to
not having an attribute storage because the attribute storage is lazily created.
* dom/Node.cpp:
(WebCore::Node::isEqualNode): Do not force the creation of attribute storage to
compare two nodes.
(WebCore::Node::isDefaultNamespace): Use updatedAttributes(). Since we iterate
using length, it's OK if the attribute storage is empty.
(WebCore::Node::lookupNamespaceURI): Ditto.
(WebCore::Node::lookupNamespacePrefix): Ditto.
(WebCore::Node::compareDocumentPosition): Ditto.
* editing/ApplyStyleCommand.cpp:
(WebCore::hasNoAttributeOrOnlyStyleAttribute):
(WebCore::isEmptyFontTag):
* editing/CompositeEditCommand.cpp:
(WebCore::CompositeEditCommand::isRemovableBlock): Use isElementNode() explicitly
to identify non-Element nodes, then use hasAttributes() if is Element.
* editing/InsertParagraphSeparatorCommand.cpp:
(WebCore::highestVisuallyEquivalentDivBelowRoot):
* editing/MarkupAccumulator.cpp:
(WebCore::MarkupAccumulator::appendElement): Do not create the attribute storage
unnecessarily.
* editing/htmlediting.cpp:
(WebCore::areIdenticalElements): Do not create the attribute storage
unnecessarily. Use mapsEquivalent() for comparing the attributes.
* editing/markup.cpp:
(WebCore::completeURLs): Do not create the attribute storage unnecessarily.
(WebCore::StyledMarkupAccumulator::appendElement): Ditto.
(WebCore::isPlainTextMarkup): hasAttributes() will avoid creating the attribute
storage unnecessarily.
* html/HTMLEmbedElement.cpp:
(WebCore::HTMLEmbedElement::parametersForPlugin):
* html/HTMLObjectElement.cpp:
(WebCore::HTMLObjectElement::parametersForPlugin):
* html/HTMLParamElement.cpp:
(WebCore::HTMLParamElement::isURLAttribute): Do not create the attribute storage
unnecessarily.
* html/parser/HTMLConstructionSite.cpp:
(WebCore::HTMLConstructionSite::mergeAttributesFromTokenIntoElement): Use
ensureUpdatedAttributes() since we will add new attributes.
(WebCore):
* inspector/InspectorCSSAgent.cpp:
(WebCore::InspectorCSSAgent::buildArrayForAttributeStyles):
* inspector/InspectorDOMAgent.cpp:
(WebCore::InspectorDOMAgent::setAttributesAsText):
(WebCore::InspectorDOMAgent::performSearch):
(WebCore::InspectorDOMAgent::buildArrayForElementAttributes):
* page/PageSerializer.cpp:
(WebCore::isCharsetSpecifyingNode): Do not assume attributeMap will exist.
* svg/properties/SVGAnimatedPropertySynchronizer.h: Use ensureUpdatedAttributes()
since we will add new attributes.
* xml/XPathFunctions.cpp:
(WebCore::XPath::FunLang::evaluate): Do not create the attribute storage
unnecessarily.
* xml/XPathNodeSet.cpp:
(WebCore::XPath::NodeSet::traversalSort):
* xml/XPathStep.cpp:
(WebCore::XPath::Step::nodesInAxis): Use isElementNode() instead of comparing
nodeType() manually. Do not create the attribute storage unnecessarily.
* xml/parser/XMLDocumentParserLibxml2.cpp:
(WebCore::XMLDocumentParser::XMLDocumentParser): Do not create the attribute
storage unnecessarily.
* xml/parser/XMLDocumentParserQt.cpp:
(WebCore::XMLDocumentParser::XMLDocumentParser): Ditto.
* xml/parser/XMLTreeBuilder.cpp:
(WebCore::XMLTreeBuilder::XMLTreeBuilder): Ditto.

Source/WebKit/chromium:

* src/WebPageSerializerImpl.cpp:
(WebKit::WebPageSerializerImpl::openTagToString): use updatedAttributes().

Source/WebKit/qt:

* Api/qwebelement.cpp:
(QWebElement::attributeNames): use updateAttributes().

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

32 files changed:
Source/WebCore/ChangeLog
Source/WebCore/css/SelectorChecker.cpp
Source/WebCore/dom/DatasetDOMStringMap.cpp
Source/WebCore/dom/Document.cpp
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Element.h
Source/WebCore/dom/NamedNodeMap.cpp
Source/WebCore/dom/Node.cpp
Source/WebCore/editing/ApplyStyleCommand.cpp
Source/WebCore/editing/CompositeEditCommand.cpp
Source/WebCore/editing/InsertParagraphSeparatorCommand.cpp
Source/WebCore/editing/MarkupAccumulator.cpp
Source/WebCore/editing/htmlediting.cpp
Source/WebCore/editing/markup.cpp
Source/WebCore/html/HTMLEmbedElement.cpp
Source/WebCore/html/HTMLObjectElement.cpp
Source/WebCore/html/HTMLParamElement.cpp
Source/WebCore/html/parser/HTMLConstructionSite.cpp
Source/WebCore/inspector/InspectorCSSAgent.cpp
Source/WebCore/inspector/InspectorDOMAgent.cpp
Source/WebCore/page/PageSerializer.cpp
Source/WebCore/svg/properties/SVGAnimatedPropertySynchronizer.h
Source/WebCore/xml/XPathFunctions.cpp
Source/WebCore/xml/XPathNodeSet.cpp
Source/WebCore/xml/XPathStep.cpp
Source/WebCore/xml/parser/XMLDocumentParserLibxml2.cpp
Source/WebCore/xml/parser/XMLDocumentParserQt.cpp
Source/WebCore/xml/parser/XMLTreeBuilder.cpp
Source/WebKit/chromium/ChangeLog
Source/WebKit/chromium/src/WebPageSerializerImpl.cpp
Source/WebKit/qt/Api/qwebelement.cpp
Source/WebKit/qt/ChangeLog

index ee1614a..81bea14 100644 (file)
@@ -1,3 +1,133 @@
+2012-02-01  Caio Marcelo de Oliveira Filho  <caio.oliveira@openbossa.org>
+
+        Avoid creating NamedNodeMap unnecessarily
+        https://bugs.webkit.org/show_bug.cgi?id=77574
+
+        Reviewed by Ryosuke Niwa.
+
+        The method Element::attributes() was being used for multiple things in our
+        codebase: (1) as the getter for NamedNodeMap exposed to DOM, (2) as a way to other WebCore
+        code get the "attribute storage" (currently inside NamedNodeMap), and (3) as a way to
+        get the attribute storage creating one if necessary.
+
+        This commit separate the jobs in different functions:
+
+        1) attributes() keeps being the DOM getter, and loses its boolean parameter.
+
+        2) updatedAttributes() updates the invalid attributes and returns the attribute
+        storage. If we don't have one, return 0.
+
+        3) ensureUpdatedAttributes() updates the invalid attributes and forces the
+        creation of attribute storage to return.
+
+        There is also another way to get to the attribute storage currently, via
+        attributeMap(), which doesn't update the attributes for possible changes in Style
+        or SVG attributes.
+
+        Note that the new functions are not available in Node class, so C++ code manipulating
+        attributes should cast to Element.
+
+        This separation also made easier to spot and fix some places where we do not
+        need to create the attribute storage if it doesn't exist.
+
+        No new tests, this commit shouldn't change the behavior of existing code.
+
+        * css/SelectorChecker.cpp:
+        (WebCore::SelectorChecker::checkOneSelector):
+        * dom/DatasetDOMStringMap.cpp:
+        (WebCore::DatasetDOMStringMap::getNames):
+        (WebCore::DatasetDOMStringMap::item):
+        (WebCore::DatasetDOMStringMap::contains):
+        * dom/Document.cpp:
+        (WebCore::Document::importNode):
+        * dom/Element.cpp:
+        (WebCore::Element::setAttribute):
+        (WebCore::Element::hasAttributes):
+        (WebCore::Element::setAttributeNode):
+        (WebCore::Element::setAttributeNodeNS):
+        (WebCore::Element::removeAttributeNode):
+        (WebCore::Element::getAttributeNode):
+        (WebCore::Element::getAttributeNodeNS):
+        (WebCore::Element::hasAttribute):
+        (WebCore::Element::hasAttributeNS):
+        (WebCore::Element::normalizeAttributes):
+        * dom/Element.h:
+        (Element):
+        (WebCore::Element::attributes):
+        (WebCore::Element::ensureAttributeData):
+        (WebCore::Element::ensureUpdatedAttributes):
+        (WebCore::Element::updatedAttributes):
+        (WebCore::Element::setAttributesFromElement):
+        (WebCore::Element::ensureAttributeMap): Made const to be reused by ensureUpdatedAttributes().
+        (WebCore::Element::updateInvalidAttributes):
+        (WebCore):
+        * dom/NamedNodeMap.cpp:
+        (WebCore::NamedNodeMap::mapsEquivalent): Having no attributes is equivalent to
+        not having an attribute storage because the attribute storage is lazily created.
+        * dom/Node.cpp:
+        (WebCore::Node::isEqualNode): Do not force the creation of attribute storage to
+        compare two nodes.
+        (WebCore::Node::isDefaultNamespace): Use updatedAttributes(). Since we iterate
+        using length, it's OK if the attribute storage is empty.
+        (WebCore::Node::lookupNamespaceURI): Ditto.
+        (WebCore::Node::lookupNamespacePrefix): Ditto.
+        (WebCore::Node::compareDocumentPosition): Ditto.
+        * editing/ApplyStyleCommand.cpp:
+        (WebCore::hasNoAttributeOrOnlyStyleAttribute):
+        (WebCore::isEmptyFontTag):
+        * editing/CompositeEditCommand.cpp:
+        (WebCore::CompositeEditCommand::isRemovableBlock): Use isElementNode() explicitly
+        to identify non-Element nodes, then use hasAttributes() if is Element.
+        * editing/InsertParagraphSeparatorCommand.cpp:
+        (WebCore::highestVisuallyEquivalentDivBelowRoot):
+        * editing/MarkupAccumulator.cpp:
+        (WebCore::MarkupAccumulator::appendElement): Do not create the attribute storage
+        unnecessarily.
+        * editing/htmlediting.cpp:
+        (WebCore::areIdenticalElements): Do not create the attribute storage
+        unnecessarily. Use mapsEquivalent() for comparing the attributes.
+        * editing/markup.cpp:
+        (WebCore::completeURLs): Do not create the attribute storage unnecessarily.
+        (WebCore::StyledMarkupAccumulator::appendElement): Ditto.
+        (WebCore::isPlainTextMarkup): hasAttributes() will avoid creating the attribute
+        storage unnecessarily.
+        * html/HTMLEmbedElement.cpp:
+        (WebCore::HTMLEmbedElement::parametersForPlugin):
+        * html/HTMLObjectElement.cpp:
+        (WebCore::HTMLObjectElement::parametersForPlugin):
+        * html/HTMLParamElement.cpp:
+        (WebCore::HTMLParamElement::isURLAttribute): Do not create the attribute storage
+        unnecessarily.
+        * html/parser/HTMLConstructionSite.cpp:
+        (WebCore::HTMLConstructionSite::mergeAttributesFromTokenIntoElement): Use
+        ensureUpdatedAttributes() since we will add new attributes.
+        (WebCore):
+        * inspector/InspectorCSSAgent.cpp:
+        (WebCore::InspectorCSSAgent::buildArrayForAttributeStyles):
+        * inspector/InspectorDOMAgent.cpp:
+        (WebCore::InspectorDOMAgent::setAttributesAsText):
+        (WebCore::InspectorDOMAgent::performSearch):
+        (WebCore::InspectorDOMAgent::buildArrayForElementAttributes):
+        * page/PageSerializer.cpp:
+        (WebCore::isCharsetSpecifyingNode): Do not assume attributeMap will exist.
+        * svg/properties/SVGAnimatedPropertySynchronizer.h: Use ensureUpdatedAttributes()
+        since we will add new attributes.
+        * xml/XPathFunctions.cpp:
+        (WebCore::XPath::FunLang::evaluate): Do not create the attribute storage
+        unnecessarily.
+        * xml/XPathNodeSet.cpp:
+        (WebCore::XPath::NodeSet::traversalSort):
+        * xml/XPathStep.cpp:
+        (WebCore::XPath::Step::nodesInAxis): Use isElementNode() instead of comparing
+        nodeType() manually. Do not create the attribute storage unnecessarily.
+        * xml/parser/XMLDocumentParserLibxml2.cpp:
+        (WebCore::XMLDocumentParser::XMLDocumentParser): Do not create the attribute
+        storage unnecessarily.
+        * xml/parser/XMLDocumentParserQt.cpp:
+        (WebCore::XMLDocumentParser::XMLDocumentParser): Ditto.
+        * xml/parser/XMLTreeBuilder.cpp:
+        (WebCore::XMLTreeBuilder::XMLTreeBuilder): Ditto.
+
 2012-02-01  Adam Barth  <abarth@webkit.org>
 
         contentDispositionType misparses the Content-Disposition header in some obscure corner cases
index 8205af0..3677fcf 100644 (file)
@@ -707,7 +707,7 @@ bool SelectorChecker::checkOneSelector(CSSSelector* sel, Element* e, PseudoId& d
     if (sel->isAttributeSelector()) {
         const QualifiedName& attr = sel->attribute();
 
-        NamedNodeMap* attributes = e->attributes(true);
+        NamedNodeMap* attributes = e->updatedAttributes();
         if (!attributes)
             return false;
 
index e0d875d..dff0ad7 100644 (file)
@@ -142,7 +142,7 @@ void DatasetDOMStringMap::deref()
 
 void DatasetDOMStringMap::getNames(Vector<String>& names)
 {
-    NamedNodeMap* attributeMap = m_element->attributes(true);
+    NamedNodeMap* attributeMap = m_element->updatedAttributes();
     if (attributeMap) {
         unsigned length = attributeMap->length();
         for (unsigned i = 0; i < length; i++) {
@@ -155,7 +155,7 @@ void DatasetDOMStringMap::getNames(Vector<String>& names)
 
 String DatasetDOMStringMap::item(const String& name)
 {
-    NamedNodeMap* attributeMap = m_element->attributes(true);
+    NamedNodeMap* attributeMap = m_element->updatedAttributes();
     if (attributeMap) {
         unsigned length = attributeMap->length();
         for (unsigned i = 0; i < length; i++) {
@@ -170,7 +170,7 @@ String DatasetDOMStringMap::item(const String& name)
 
 bool DatasetDOMStringMap::contains(const String& name)
 {
-    NamedNodeMap* attributeMap = m_element->attributes(true);
+    NamedNodeMap* attributeMap = m_element->updatedAttributes();
     if (attributeMap) {
         unsigned length = attributeMap->length();
         for (unsigned i = 0; i < length; i++) {
index 096a38e..13d4501 100644 (file)
@@ -841,7 +841,7 @@ PassRefPtr<Node> Document::importNode(Node* importedNode, bool deep, ExceptionCo
         if (ec)
             return 0;
 
-        NamedNodeMap* attrs = oldElement->attributes(true);
+        NamedNodeMap* attrs = oldElement->updatedAttributes();
         if (attrs) {
             unsigned length = attrs->length();
             for (unsigned i = 0; i < length; i++) {
index 91d8663..5a22021 100644 (file)
@@ -616,14 +616,14 @@ void Element::setAttribute(const AtomicString& name, const AtomicString& value,
 
     const AtomicString& localName = shouldIgnoreAttributeCase(this) ? name.lower() : name;
 
-    size_t index = attributes(false)->getAttributeItemIndex(localName, false);
+    size_t index = ensureUpdatedAttributes()->getAttributeItemIndex(localName, false);
     const QualifiedName& qName = index != notFound ? m_attributeMap->attributeItem(index)->name() : QualifiedName(nullAtom, localName, nullAtom);
     setAttributeInternal(index, qName, value);
 }
 
 void Element::setAttribute(const QualifiedName& name, const AtomicString& value)
 {
-    setAttributeInternal(attributes(false)->getAttributeItemIndex(name), name, value);
+    setAttributeInternal(ensureUpdatedAttributes()->getAttributeItemIndex(name), name, value);
 }
 
 inline void Element::setAttributeInternal(size_t index, const QualifiedName& name, const AtomicString& value)
@@ -775,14 +775,7 @@ void Element::parserSetAttributeMap(PassOwnPtr<NamedNodeMap> list, FragmentScrip
 
 bool Element::hasAttributes() const
 {
-    if (!isStyleAttributeValid())
-        updateStyleAttribute();
-
-#if ENABLE(SVG)
-    if (!areSVGAttributesValid())
-        updateAnimatedSVGAttribute(anyQName());
-#endif
-
+    updateInvalidAttributes();
     return m_attributeMap && m_attributeMap->length();
 }
 
@@ -1422,7 +1415,7 @@ PassRefPtr<Attr> Element::setAttributeNode(Attr* attr, ExceptionCode& ec)
         ec = TYPE_MISMATCH_ERR;
         return 0;
     }
-    return static_pointer_cast<Attr>(attributes(false)->setNamedItem(attr, ec));
+    return static_pointer_cast<Attr>(ensureUpdatedAttributes()->setNamedItem(attr, ec));
 }
 
 PassRefPtr<Attr> Element::setAttributeNodeNS(Attr* attr, ExceptionCode& ec)
@@ -1431,7 +1424,7 @@ PassRefPtr<Attr> Element::setAttributeNodeNS(Attr* attr, ExceptionCode& ec)
         ec = TYPE_MISMATCH_ERR;
         return 0;
     }
-    return static_pointer_cast<Attr>(attributes(false)->setNamedItem(attr, ec));
+    return static_pointer_cast<Attr>(ensureUpdatedAttributes()->setNamedItem(attr, ec));
 }
 
 PassRefPtr<Attr> Element::removeAttributeNode(Attr* attr, ExceptionCode& ec)
@@ -1447,7 +1440,7 @@ PassRefPtr<Attr> Element::removeAttributeNode(Attr* attr, ExceptionCode& ec)
 
     ASSERT(document() == attr->document());
 
-    NamedNodeMap* attrs = attributes(true);
+    NamedNodeMap* attrs = updatedAttributes();
     if (!attrs)
         return 0;
 
@@ -1493,7 +1486,7 @@ void Element::removeAttributeNS(const String& namespaceURI, const String& localN
 
 PassRefPtr<Attr> Element::getAttributeNode(const String& name)
 {
-    NamedNodeMap* attrs = attributes(true);
+    NamedNodeMap* attrs = updatedAttributes();
     if (!attrs)
         return 0;
     String localName = shouldIgnoreAttributeCase(this) ? name.lower() : name;
@@ -1502,7 +1495,7 @@ PassRefPtr<Attr> Element::getAttributeNode(const String& name)
 
 PassRefPtr<Attr> Element::getAttributeNodeNS(const String& namespaceURI, const String& localName)
 {
-    NamedNodeMap* attrs = attributes(true);
+    NamedNodeMap* attrs = updatedAttributes();
     if (!attrs)
         return 0;
     return static_pointer_cast<Attr>(attrs->getNamedItem(QualifiedName(nullAtom, localName, namespaceURI)));
@@ -1510,7 +1503,7 @@ PassRefPtr<Attr> Element::getAttributeNodeNS(const String& namespaceURI, const S
 
 bool Element::hasAttribute(const String& name) const
 {
-    NamedNodeMap* attrs = attributes(true);
+    NamedNodeMap* attrs = updatedAttributes();
     if (!attrs)
         return false;
 
@@ -1522,7 +1515,7 @@ bool Element::hasAttribute(const String& name) const
 
 bool Element::hasAttributeNS(const String& namespaceURI, const String& localName) const
 {
-    NamedNodeMap* attrs = attributes(true);
+    NamedNodeMap* attrs = updatedAttributes();
     if (!attrs)
         return false;
     return attrs->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI));
@@ -1712,8 +1705,7 @@ void Element::cancelFocusAppearanceUpdate()
 
 void Element::normalizeAttributes()
 {
-    // Normalize attributes.
-    NamedNodeMap* attrs = attributes(true);
+    NamedNodeMap* attrs = updatedAttributes();
     if (!attrs)
         return;
 
index 54e1716..1d37eae 100644 (file)
@@ -214,7 +214,11 @@ public:
 
     void setBooleanAttribute(const QualifiedName& name, bool);
 
-    NamedNodeMap* attributes(bool readonly = false) const;
+    // For exposing to DOM only.
+    NamedNodeMap* attributes() const { return ensureUpdatedAttributes(); }
+
+    NamedNodeMap* ensureUpdatedAttributes() const;
+    NamedNodeMap* updatedAttributes() const;
 
     // This method is called whenever an attribute is added, changed or removed.
     virtual void attributeChanged(Attribute*, bool preserveDecls = false);
@@ -223,10 +227,10 @@ public:
     void parserSetAttributeMap(PassOwnPtr<NamedNodeMap>, FragmentScriptingPermission);
 
     NamedNodeMap* attributeMap() const { return m_attributeMap.get(); }
-    NamedNodeMap* ensureAttributeMap();
+    NamedNodeMap* ensureAttributeMap() const;
 
     ElementAttributeData* attributeData() const { return m_attributeMap ? m_attributeMap->attributeData() : 0; }
-    ElementAttributeData* ensureAttributeData() const { return attributes()->attributeData(); }
+    ElementAttributeData* ensureAttributeData() const { return ensureUpdatedAttributes()->attributeData(); }
 
     void setAttributesFromElement(const Element&);
 
@@ -409,6 +413,8 @@ protected:
     HTMLCollection* ensureCachedHTMLCollection(CollectionType);
 
 private:
+    void updateInvalidAttributes() const;
+
     void scrollByUnits(int units, ScrollGranularity);
 
     virtual void setPrefix(const AtomicString&, ExceptionCode&);
@@ -500,25 +506,22 @@ inline Element* Node::parentElement() const
     return parent && parent->isElementNode() ? toElement(parent) : 0;
 }
 
-inline NamedNodeMap* Element::attributes(bool readonly) const
+inline NamedNodeMap* Element::ensureUpdatedAttributes() const
 {
-    if (!isStyleAttributeValid())
-        updateStyleAttribute();
-
-#if ENABLE(SVG)
-    if (!areSVGAttributesValid())
-        updateAnimatedSVGAttribute(anyQName());
-#endif
+    updateInvalidAttributes();
+    return ensureAttributeMap();
+}
 
-    if (!readonly && !m_attributeMap)
-        createAttributeMap();
+inline NamedNodeMap* Element::updatedAttributes() const
+{
+    updateInvalidAttributes();
     return m_attributeMap.get();
 }
 
 inline void Element::setAttributesFromElement(const Element& other)
 {
-    if (NamedNodeMap* attributeMap = other.attributes(true))
-        attributes(false)->setAttributes(*attributeMap);
+    if (NamedNodeMap* attributeMap = other.updatedAttributes())
+        ensureUpdatedAttributes()->setAttributes(*attributeMap);
 }
 
 inline void Element::updateName(const AtomicString& oldName, const AtomicString& newName)
@@ -598,13 +601,24 @@ inline void Element::setIdAttribute(const AtomicString& value)
     setAttribute(document()->idAttributeName(), value);
 }
 
-inline NamedNodeMap* Element::ensureAttributeMap()
+inline NamedNodeMap* Element::ensureAttributeMap() const
 {
     if (!m_attributeMap)
         createAttributeMap();
     return m_attributeMap.get();
 }
 
+inline void Element::updateInvalidAttributes() const
+{
+    if (!isStyleAttributeValid())
+        updateStyleAttribute();
+
+#if ENABLE(SVG)
+    if (!areSVGAttributesValid())
+        updateAnimatedSVGAttribute(anyQName());
+#endif
+}
+
 inline Element* firstElementChild(const ContainerNode* container)
 {
     ASSERT_ARG(container, container);
index 0134441..7f62297 100644 (file)
@@ -305,7 +305,7 @@ void NamedNodeMap::replaceAttribute(size_t index, PassRefPtr<Attribute> prpAttri
 bool NamedNodeMap::mapsEquivalent(const NamedNodeMap* otherMap) const
 {
     if (!otherMap)
-        return false;
+        return isEmpty();
     
     unsigned len = length();
     if (len != otherMap->length())
index cd77074..738000f 100644 (file)
@@ -1764,14 +1764,16 @@ bool Node::isEqualNode(Node* other) const
     if (nodeValue() != other->nodeValue())
         return false;
     
-    NamedNodeMap* attributes = this->attributes();
-    NamedNodeMap* otherAttributes = other->attributes();
-    
-    if (!attributes && otherAttributes)
-        return false;
-    
-    if (attributes && !attributes->mapsEquivalent(otherAttributes))
-        return false;
+    if (isElementNode()) {
+        NamedNodeMap* attributes = toElement(this)->updatedAttributes();
+        NamedNodeMap* otherAttributes = toElement(other)->updatedAttributes();
+
+        if (attributes && !attributes->mapsEquivalent(otherAttributes))
+            return false;
+
+        if (otherAttributes && !otherAttributes->mapsEquivalent(attributes))
+            return false;
+    }
     
     Node* child = firstChild();
     Node* otherChild = other->firstChild();
@@ -1829,9 +1831,7 @@ bool Node::isDefaultNamespace(const AtomicString& namespaceURIMaybeEmpty) const
             if (elem->prefix().isNull())
                 return elem->namespaceURI() == namespaceURI;
 
-            if (elem->hasAttributes()) {
-                NamedNodeMap* attrs = elem->attributes();
-                
+            if (NamedNodeMap* attrs = elem->updatedAttributes()) {
                 for (unsigned i = 0; i < attrs->length(); i++) {
                     Attribute* attr = attrs->attributeItem(i);
                     
@@ -1917,9 +1917,7 @@ String Node::lookupNamespaceURI(const String &prefix) const
             if (!elem->namespaceURI().isNull() && elem->prefix() == prefix)
                 return elem->namespaceURI();
             
-            if (elem->hasAttributes()) {
-                NamedNodeMap *attrs = elem->attributes();
-                
+            if (NamedNodeMap* attrs = elem->updatedAttributes()) {
                 for (unsigned i = 0; i < attrs->length(); i++) {
                     Attribute *attr = attrs->attributeItem(i);
                     
@@ -1973,15 +1971,12 @@ String Node::lookupNamespacePrefix(const AtomicString &_namespaceURI, const Elem
     if (originalElement->lookupNamespaceURI(prefix()) == _namespaceURI)
         return prefix();
     
-    if (hasAttributes()) {
-        NamedNodeMap *attrs = attributes();
-        
+    if (NamedNodeMap* attrs = toElement(this)->updatedAttributes()) {
         for (unsigned i = 0; i < attrs->length(); i++) {
-            Attribute *attr = attrs->attributeItem(i);
+            Attributeattr = attrs->attributeItem(i);
             
-            if (attr->prefix() == xmlnsAtom &&
-                attr->value() == _namespaceURI &&
-                originalElement->lookupNamespaceURI(attr->localName()) == _namespaceURI)
+            if (attr->prefix() == xmlnsAtom && attr->value() == _namespaceURI
+                    && originalElement->lookupNamespaceURI(attr->localName()) == _namespaceURI)
                 return attr->localName();
         }
     }
@@ -2121,7 +2116,7 @@ unsigned short Node::compareDocumentPosition(Node* otherNode)
     if (attr1 && attr2 && start1 == start2 && start1) {
         // We are comparing two attributes on the same node.  Crawl our attribute map
         // and see which one we hit first.
-        NamedNodeMap* map = attr1->ownerElement()->attributes(true);
+        NamedNodeMap* map = attr1->ownerElement()->updatedAttributes();
         unsigned length = map->length();
         for (unsigned i = 0; i < length; ++i) {
             // If neither of the two determining nodes is a child node and nodeType is the same for both determining nodes, then an 
index 41735c5..43820c4 100644 (file)
@@ -73,8 +73,7 @@ bool isLegacyAppleStyleSpan(const Node *node)
 enum ShouldStyleAttributeBeEmpty { AllowNonEmptyStyleAttribute, StyleAttributeShouldBeEmpty };
 static bool hasNoAttributeOrOnlyStyleAttribute(const StyledElement* element, ShouldStyleAttributeBeEmpty shouldStyleAttributeBeEmpty)
 {
-    const bool readonly = true;
-    NamedNodeMap* map = element->attributes(readonly);
+    NamedNodeMap* map = element->updatedAttributes();
     if (!map || map->isEmpty())
         return true;
 
@@ -109,7 +108,7 @@ static bool isEmptyFontTag(const Node *node)
         return false;
 
     const Element *elem = static_cast<const Element *>(node);
-    NamedNodeMap *map = elem->attributes(true); // true for read-only
+    NamedNodeMap* map = elem->updatedAttributes();
     if (!map)
         return true;
     return map->isEmpty() || (map->length() == 1 && elem->getAttribute(classAttr) == styleSpanClassString());
index be5dbac..2a629c3 100644 (file)
@@ -308,10 +308,9 @@ bool CompositeEditCommand::isRemovableBlock(const Node* node)
     if ((parentNode && parentNode->firstChild() != parentNode->lastChild()) || !node->hasTagName(divTag))
         return false;
 
-    const NamedNodeMap* attributeMap = node->attributes();
-    if (!attributeMap || attributeMap->isEmpty())
+    if (!node->isElementNode() || !toElement(node)->hasAttributes())
         return true;
-    
+
     return false;
 }
 
index f40c15c..e646b51 100644 (file)
@@ -51,7 +51,7 @@ static Element* highestVisuallyEquivalentDivBelowRoot(Element* startBlock)
     // We don't want to return a root node (if it happens to be a div, e.g., in a document fragment) because there are no
     // siblings for us to append to.
     while (!curBlock->nextSibling() && curBlock->parentElement()->hasTagName(divTag) && curBlock->parentElement()->parentElement()) {
-        NamedNodeMap* attributes = curBlock->parentElement()->attributes(true);
+        NamedNodeMap* attributes = curBlock->parentElement()->updatedAttributes();
         if (attributes && !attributes->isEmpty())
             break;
         curBlock = curBlock->parentElement();
index c7d270f..5226b21 100644 (file)
@@ -349,10 +349,12 @@ void MarkupAccumulator::appendElement(StringBuilder& out, Element* element, Name
 {
     appendOpenTag(out, element, namespaces);
 
-    NamedNodeMap* attributes = element->attributes();
-    unsigned length = attributes->length();
-    for (unsigned int i = 0; i < length; i++)
-        appendAttribute(out, element, *attributes->attributeItem(i), namespaces);
+    NamedNodeMap* attributes = element->updatedAttributes();
+    if (attributes) {
+        unsigned length = attributes->length();
+        for (unsigned int i = 0; i < length; i++)
+            appendAttribute(out, element, *attributes->attributeItem(i), namespaces);
+    }
 
     // Give an opportunity to subclasses to add their own attributes.
     appendCustomAttributes(out, element, namespaces);
index 4e6d528..28cc029 100644 (file)
@@ -1157,20 +1157,13 @@ bool areIdenticalElements(const Node* first, const Node* second)
     if (!toElement(first)->tagQName().matches(toElement(second)->tagQName()))
         return false;
 
-    NamedNodeMap* firstMap = toElement(first)->attributes();
-    NamedNodeMap* secondMap = toElement(second)->attributes();
-    unsigned firstLength = firstMap->length();
-
-    if (firstLength != secondMap->length())
-        return false;
-
-    for (unsigned i = 0; i < firstLength; i++) {
-        Attribute* attribute = firstMap->attributeItem(i);
-        Attribute* secondAttribute = secondMap->getAttributeItem(attribute->name());
-        if (!secondAttribute || attribute->value() != secondAttribute->value())
-            return false;
-    }
+    NamedNodeMap* firstMap = toElement(first)->updatedAttributes();
+    NamedNodeMap* secondMap = toElement(second)->updatedAttributes();
 
+    if (firstMap)
+        return firstMap->mapsEquivalent(secondMap);
+    if (secondMap)
+        return secondMap->mapsEquivalent(firstMap);
     return true;
 }
 
index c6441e6..a5482af 100644 (file)
@@ -104,7 +104,9 @@ static void completeURLs(Node* node, const String& baseURL)
     for (Node* n = node; n != end; n = n->traverseNextNode()) {
         if (n->isElementNode()) {
             Element* e = static_cast<Element*>(n);
-            NamedNodeMap* attributes = e->attributes();
+            NamedNodeMap* attributes = e->updatedAttributes();
+            if (!attributes)
+                continue;
             unsigned length = attributes->length();
             for (unsigned i = 0; i < length; i++) {
                 Attribute* attribute = attributes->attributeItem(i);
@@ -287,8 +289,8 @@ void StyledMarkupAccumulator::appendElement(StringBuilder& out, Element* element
     const bool documentIsHTML = element->document()->isHTMLDocument();
     appendOpenTag(out, element, 0);
 
-    NamedNodeMap* attributes = element->attributes();
-    const unsigned length = attributes->length();
+    NamedNodeMap* attributes = element->updatedAttributes();
+    const unsigned length = attributes ? attributes->length() : 0;
     const bool shouldAnnotateOrForceInline = element->isHTMLElement() && (shouldAnnotate() || addDisplayInline);
     const bool shouldOverrideStyleAttr = shouldAnnotateOrForceInline || shouldApplyWrappingStyle(element);
     for (unsigned int i = 0; i < length; i++) {
@@ -822,7 +824,7 @@ static void fillContainerFromString(ContainerNode* paragraph, const String& stri
 
 bool isPlainTextMarkup(Node *node)
 {
-    if (!node->isElementNode() || !node->hasTagName(divTag) || static_cast<Element*>(node)->attributes()->length())
+    if (!node->isElementNode() || !node->hasTagName(divTag) || static_cast<Element*>(node)->hasAttributes())
         return false;
     
     if (node->childNodeCount() == 1 && (node->firstChild()->isTextNode() || (node->firstChild()->firstChild())))
index 08c15e9..309aafe 100644 (file)
@@ -117,7 +117,7 @@ void HTMLEmbedElement::parseMappedAttribute(Attribute* attr)
 
 void HTMLEmbedElement::parametersForPlugin(Vector<String>& paramNames, Vector<String>& paramValues)
 {
-    NamedNodeMap* attributes = this->attributes(true);
+    NamedNodeMap* attributes = updatedAttributes();
     if (!attributes)
         return;
 
index 925275f..590d69b 100644 (file)
@@ -177,7 +177,7 @@ void HTMLObjectElement::parametersForPlugin(Vector<String>& paramNames, Vector<S
     }
     
     // Turn the attributes of the <object> element into arrays, but don't override <param> values.
-    NamedNodeMap* attributes = this->attributes(true);
+    NamedNodeMap* attributes = updatedAttributes();
     if (attributes) {
         for (unsigned i = 0; i < attributes->length(); ++i) {
             Attribute* it = attributes->attributeItem(i);
index d1f53a1..aa228bc 100644 (file)
@@ -65,10 +65,10 @@ void HTMLParamElement::parseMappedAttribute(Attribute* attr)
 
 bool HTMLParamElement::isURLAttribute(Attribute* attr) const
 {
-    if (attr->name() == valueAttr) {
-        Attribute* attr = attributes()->getAttributeItem(nameAttr);
-        if (attr) {
-            const AtomicString& value = attr->value();
+    if (attr->name() == valueAttr && hasAttributes()) {
+        Attribute* nameAttribute = attributeMap()->getAttributeItem(nameAttr);
+        if (nameAttribute) {
+            const AtomicString& value = nameAttribute->value();
             if (isURLParameter(value))
                 return true;
         }
index 4cf798c..0ee89f1 100644 (file)
@@ -208,7 +208,7 @@ void HTMLConstructionSite::mergeAttributesFromTokenIntoElement(AtomicHTMLToken&
     if (!token.attributes())
         return;
 
-    NamedNodeMap* attributes = element->attributes(false);
+    NamedNodeMap* attributes = element->ensureUpdatedAttributes();
     for (unsigned i = 0; i < token.attributes()->length(); ++i) {
         Attribute* attribute = token.attributes()->attributeItem(i);
         if (!attributes->getAttributeItem(attribute->name()))
@@ -425,7 +425,7 @@ namespace {
 // FIXME: Move this function to the top of the file.
 inline PassOwnPtr<NamedNodeMap> cloneAttributes(Element* element)
 {
-    NamedNodeMap* attributes = element->attributes(true);
+    NamedNodeMap* attributes = element->updatedAttributes();
     if (!attributes)
         return nullptr;
 
index b1a59a3..06635f5 100644 (file)
@@ -717,7 +717,10 @@ PassRefPtr<InspectorArray> InspectorCSSAgent::buildArrayForRuleList(CSSRuleList*
 PassRefPtr<InspectorArray> InspectorCSSAgent::buildArrayForAttributeStyles(Element* element)
 {
     RefPtr<InspectorArray> attrStyles = InspectorArray::create();
-    NamedNodeMap* attributes = element->attributes();
+    NamedNodeMap* attributes = element->updatedAttributes();
+    if (!attributes)
+        return attrStyles.release();
+
     for (unsigned i = 0; attributes && i < attributes->length(); ++i) {
         Attribute* attribute = attributes->attributeItem(i);
         if (!attribute->decl())
index 28c0f3b..b50025b 100644 (file)
@@ -530,7 +530,7 @@ void InspectorDOMAgent::setAttributesAsText(ErrorString* errorString, int elemen
         return;
     }
 
-    const NamedNodeMap* attrMap = toHTMLElement(child)->attributes(true);
+    const NamedNodeMap* attrMap = toHTMLElement(child)->updatedAttributes();
     if (!attrMap && name) {
         element->removeAttribute(*name);
         return;
@@ -781,7 +781,7 @@ void InspectorDOMAgent::performSearch(ErrorString*, const String& whitespaceTrim
                     break;
                 }
                 // Go through all attributes and serialize them.
-                const NamedNodeMap* attrMap = static_cast<Element*>(node)->attributes(true);
+                const NamedNodeMap* attrMap = static_cast<Element*>(node)->updatedAttributes();
                 if (!attrMap)
                     break;
 
@@ -1159,7 +1159,7 @@ PassRefPtr<InspectorArray> InspectorDOMAgent::buildArrayForElementAttributes(Ele
 {
     RefPtr<InspectorArray> attributesValue = InspectorArray::create();
     // Go through all attributes and serialize them.
-    const NamedNodeMap* attrMap = element->attributes(true);
+    const NamedNodeMap* attrMap = element->updatedAttributes();
     if (!attrMap)
         return attributesValue.release();
     unsigned numAttrs = attrMap->length();
index 99ab2ef..2c3778b 100644 (file)
@@ -69,11 +69,12 @@ static bool isCharsetSpecifyingNode(Node* node)
     if (!element->hasTagName(HTMLNames::metaTag))
         return false;
     HTMLMetaCharsetParser::AttributeList attributes;
-    const NamedNodeMap* attributesMap = element->attributes(true);
-    for (unsigned i = 0; i < attributesMap->length(); ++i) {
-        Attribute* item = attributesMap->attributeItem(i);
-        // FIXME: We should deal appropriately with the attribute if they have a namespace.
-        attributes.append(make_pair(item->name().toString(), item->value().string()));
+    if (const NamedNodeMap* attributesMap = element->updatedAttributes()) {
+        for (unsigned i = 0; i < attributesMap->length(); ++i) {
+            Attribute* item = attributesMap->attributeItem(i);
+            // FIXME: We should deal appropriately with the attribute if they have a namespace.
+            attributes.append(make_pair(item->name().toString(), item->value().string()));
+        }
     }
     TextEncoding textEncoding = HTMLMetaCharsetParser::encodingFromMetaAttributes(attributes);
     return textEncoding.isValid();
index cb789f3..e6bd013 100644 (file)
@@ -37,7 +37,7 @@ 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->attributes(false);
+        NamedNodeMap* namedAttrMap = ownerElement->ensureUpdatedAttributes();
         Attribute* old = namedAttrMap->getAttributeItem(attrName);
         if (old && value.isNull())
             namedAttrMap->removeAttribute(old->name());
index 7b3f58f..283583d 100644 (file)
@@ -591,9 +591,10 @@ Value FunLang::evaluate() const
     Attribute* languageAttribute = 0;
     Node* node = evaluationContext().node.get();
     while (node) {
-        NamedNodeMap* attrs = node->attributes();
-        if (attrs)
-            languageAttribute = attrs->getAttributeItem(XMLNames::langAttr);
+        if (node->isElementNode()) {
+            if (NamedNodeMap* attributes = toElement(node)->updatedAttributes())
+                languageAttribute = attributes->getAttributeItem(XMLNames::langAttr);
+        }
         if (languageAttribute)
             break;
         node = node->parentNode();
index edb77eb..8eefc57 100644 (file)
@@ -213,7 +213,7 @@ void NodeSet::traversalSort() const
         if (!containsAttributeNodes || !n->isElementNode())
             continue;
 
-        NamedNodeMap* attributes = toElement(n)->attributes(true /* read-only */);
+        NamedNodeMap* attributes = toElement(n)->updatedAttributes();
         if (!attributes)
             continue;
 
index 2866ab6..12f394f 100644 (file)
@@ -328,12 +328,14 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const
             return;
         }
         case AttributeAxis: {
-            if (context->nodeType() != Node::ELEMENT_NODE)
+            if (!context->isElementNode())
                 return;
 
+            Element* contextElement = toElement(context);
+
             // Avoid lazily creating attribute nodes for attributes that we do not need anyway.
             if (m_nodeTest.kind() == NodeTest::NameTest && m_nodeTest.data() != starAtom) {
-                RefPtr<Node> n = static_cast<Element*>(context)->getAttributeNodeNS(m_nodeTest.namespaceURI(), m_nodeTest.data());
+                RefPtr<Node> n = contextElement->getAttributeNodeNS(m_nodeTest.namespaceURI(), m_nodeTest.data());
                 if (n && n->namespaceURI() != XMLNSNames::xmlnsNamespaceURI) { // In XPath land, namespace nodes are not accessible on the attribute axis.
                     if (nodeMatches(n.get(), AttributeAxis, m_nodeTest)) // Still need to check merged predicates.
                         nodes.append(n.release());
@@ -341,7 +343,7 @@ void Step::nodesInAxis(Node* context, NodeSet& nodes) const
                 return;
             }
             
-            NamedNodeMap* attrs = context->attributes();
+            NamedNodeMap* attrs = contextElement->updatedAttributes();
             if (!attrs)
                 return;
 
index 0fdd227..4e1d49e 100644 (file)
@@ -599,7 +599,7 @@ XMLDocumentParser::XMLDocumentParser(DocumentFragment* fragment, Element* parent
 
     for (; !elemStack.isEmpty(); elemStack.removeLast()) {
         Element* element = elemStack.last();
-        if (NamedNodeMap* attrs = element->attributes()) {
+        if (NamedNodeMap* attrs = element->updatedAttributes()) {
             for (unsigned i = 0; i < attrs->length(); i++) {
                 Attribute* attr = attrs->attributeItem(i);
                 if (attr->localName() == xmlnsAtom)
index 0ebfaa9..cd6ea14 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->attributes()) {
+        if (NamedNodeMap* attrs = element->updatedAttributes()) {
             for (unsigned i = 0; i < attrs->length(); i++) {
                 Attribute* attr = attrs->attributeItem(i);
                 if (attr->localName() == "xmlns")
index 6476a1d..8de1a08 100644 (file)
@@ -80,7 +80,7 @@ XMLTreeBuilder::XMLTreeBuilder(NewXMLDocumentParser* parser, DocumentFragment* f
 
     for (Element* element; !nodeStack.isEmpty(); nodeStack.removeLast()) {
         element = nodeStack.last();
-        if (NamedNodeMap* attrs = element->attributes()) {
+        if (NamedNodeMap* attrs = element->updatedAttributes()) {
             for (size_t i = 0; i < attrs->length(); ++i) {
                 Attribute* attr = attrs->attributeItem(i);
                 if (attr->localName() == xmlnsAtom)
index 3e30cf3..32e9361 100644 (file)
@@ -1,3 +1,13 @@
+2012-02-01  Caio Marcelo de Oliveira Filho  <caio.oliveira@openbossa.org>
+
+        Avoid creating NamedNodeMap unnecessarily
+        https://bugs.webkit.org/show_bug.cgi?id=77574
+
+        Reviewed by Ryosuke Niwa.
+
+        * src/WebPageSerializerImpl.cpp:
+        (WebKit::WebPageSerializerImpl::openTagToString): use updatedAttributes().
+
 2012-02-01  Justin Novosad  <junov@chromium.org>
 
         [Chromium] Enable deferred canvas rendering in the skia port
index 37489c6..da0e325 100644 (file)
@@ -307,7 +307,7 @@ void WebPageSerializerImpl::openTagToString(Element* element,
     // Add open tag
     result += "<" + element->nodeName().lower();
     // Go through all attributes and serialize them.
-    const NamedNodeMap *attrMap = element->attributes(true);
+    const NamedNodeMap *attrMap = element->updatedAttributes();
     if (attrMap) {
         unsigned numAttrs = attrMap->length();
         for (unsigned i = 0; i < numAttrs; i++) {
index d527ff9..771ad56 100644 (file)
@@ -503,7 +503,7 @@ QStringList QWebElement::attributeNames(const QString& namespaceUri) const
         return QStringList();
 
     QStringList attributeNameList;
-    const NamedNodeMap* const attrs = m_element->attributes(/* read only = */ true);
+    const NamedNodeMap* const attrs = m_element->updatedAttributes();
     if (attrs) {
         const String namespaceUriString(namespaceUri); // convert QString -> String once
         const unsigned attrsCount = attrs->length();
index 5543e60..0b854f9 100644 (file)
@@ -1,3 +1,13 @@
+2012-02-01  Caio Marcelo de Oliveira Filho  <caio.oliveira@openbossa.org>
+
+        Avoid creating NamedNodeMap unnecessarily
+        https://bugs.webkit.org/show_bug.cgi?id=77574
+
+        Reviewed by Ryosuke Niwa.
+
+        * Api/qwebelement.cpp:
+        (QWebElement::attributeNames): use updateAttributes().
+
 2012-02-01  Alexis Menard  <alexis.menard@openbossa.org>
 
         CSSStyleDeclaration.getPropertyPriority() fails for CSS shorthand properties with 'important' priority