Title: [143112] trunk/Source/WebCore
Revision
143112
Author
[email protected]
Date
2013-02-16 14:57:02 -0800 (Sat, 16 Feb 2013)

Log Message

Element: Avoid unrelated attribute synchronization on other attribute access.
<http://webkit.org/b/110025>

Reviewed by Darin Adler.

We've been extremely trigger happy with re-serializing the style attribute (and SVG animatables)
whenever any Element attribute API was used. This patch narrows this down to (almost always)
only synchronizing an attribute when someone specifically wants to read/update it.

Also removed two more confusing ElementData accessors:

    - Element::elementDataWithSynchronizedAttributes()
    - Element::ensureElementDataWithSynchronizedAttributes()

* dom/Element.h:
* dom/Element.cpp:
(WebCore::Element::hasAttributes):
(WebCore::Element::hasEquivalentAttributes):
(WebCore::Element::cloneAttributesFromElement):
(WebCore::Element::synchronizeAllAttributes):

    Renamed updateInvalidAttributes() to synchronizeAllAttributes().
    This function should only be used when we need every single attribute to be up-to-date.

(WebCore::Element::synchronizeAttribute):

    Broke out logic for synchronizing a specific attribute, given either a full QualifiedName
    or a localName.

(WebCore::Element::setSynchronizedLazyAttribute):

    Don't call ensureUniqueElementData() indisciminately here. This avoids converting the attribute
    storage when re-serializing the inline style yields the same CSS text that was already in the
    style attribute.

(WebCore::Element::hasAttribute):
(WebCore::Element::hasAttributeNS):
(WebCore::Element::getAttribute):
(WebCore::Element::getAttributeNode):
(WebCore::Element::getAttributeNodeNS):
(WebCore::Element::setAttribute):
(WebCore::Element::setAttributeNode):
(WebCore::Element::removeAttributeNode):

    Only synchronize the attribute in question.

* dom/Node.cpp:
(WebCore::Node::compareDocumentPosition):

    Call synchronizeAllAttributes() when comparing two Attr nodes on the same Element instead
    of relying on the side-effects of another function doing this.

Modified Paths

Diff

Modified: trunk/Source/WebCore/ChangeLog (143111 => 143112)


--- trunk/Source/WebCore/ChangeLog	2013-02-16 22:27:12 UTC (rev 143111)
+++ trunk/Source/WebCore/ChangeLog	2013-02-16 22:57:02 UTC (rev 143112)
@@ -1,3 +1,57 @@
+2013-02-16  Andreas Kling  <[email protected]>
+
+        Element: Avoid unrelated attribute synchronization on other attribute access.
+        <http://webkit.org/b/110025>
+
+        Reviewed by Darin Adler.
+
+        We've been extremely trigger happy with re-serializing the style attribute (and SVG animatables)
+        whenever any Element attribute API was used. This patch narrows this down to (almost always)
+        only synchronizing an attribute when someone specifically wants to read/update it.
+
+        Also removed two more confusing ElementData accessors:
+
+            - Element::elementDataWithSynchronizedAttributes()
+            - Element::ensureElementDataWithSynchronizedAttributes()
+
+        * dom/Element.h:
+        * dom/Element.cpp:
+        (WebCore::Element::hasAttributes):
+        (WebCore::Element::hasEquivalentAttributes):
+        (WebCore::Element::cloneAttributesFromElement):
+        (WebCore::Element::synchronizeAllAttributes):
+
+            Renamed updateInvalidAttributes() to synchronizeAllAttributes().
+            This function should only be used when we need every single attribute to be up-to-date.
+
+        (WebCore::Element::synchronizeAttribute):
+
+            Broke out logic for synchronizing a specific attribute, given either a full QualifiedName
+            or a localName.
+
+        (WebCore::Element::setSynchronizedLazyAttribute):
+
+            Don't call ensureUniqueElementData() indisciminately here. This avoids converting the attribute
+            storage when re-serializing the inline style yields the same CSS text that was already in the
+            style attribute.
+
+        (WebCore::Element::hasAttribute):
+        (WebCore::Element::hasAttributeNS):
+        (WebCore::Element::getAttribute):
+        (WebCore::Element::getAttributeNode):
+        (WebCore::Element::getAttributeNodeNS):
+        (WebCore::Element::setAttribute):
+        (WebCore::Element::setAttributeNode):
+        (WebCore::Element::removeAttributeNode):
+
+            Only synchronize the attribute in question.
+
+        * dom/Node.cpp:
+        (WebCore::Node::compareDocumentPosition):
+
+            Call synchronizeAllAttributes() when comparing two Attr nodes on the same Element instead
+            of relying on the side-effects of another function doing this.
+
 2013-02-16  Seokju Kwon  <[email protected]>
 
         Fix build warnings after r139853

Modified: trunk/Source/WebCore/dom/Element.cpp (143111 => 143112)


--- trunk/Source/WebCore/dom/Element.cpp	2013-02-16 22:27:12 UTC (rev 143111)
+++ trunk/Source/WebCore/dom/Element.cpp	2013-02-16 22:57:02 UTC (rev 143112)
@@ -94,6 +94,11 @@
 
 using namespace HTMLNames;
 using namespace XMLNames;
+
+static inline bool shouldIgnoreAttributeCase(const Element* e)
+{
+    return e && e->document()->isHTMLDocument() && e->isHTMLElement();
+}
     
 class StyleResolverParentPusher {
 public:
@@ -331,19 +336,55 @@
     return hasAttributeNS(name.namespaceURI(), name.localName());
 }
 
-const AtomicString& Element::getAttribute(const QualifiedName& name) const
+void Element::synchronizeAllAttributes() const
 {
     if (!elementData())
-        return nullAtom;
+        return;
+    if (elementData()->m_styleAttributeIsDirty)
+        updateStyleAttribute();
+#if ENABLE(SVG)
+    if (elementData()->m_animatedSVGAttributesAreDirty)
+        updateAnimatedSVGAttribute(anyQName());
+#endif
+}
 
-    if (UNLIKELY(name == styleAttr && elementData()->m_styleAttributeIsDirty))
+inline void Element::synchronizeAttribute(const QualifiedName& name) const
+{
+    if (!elementData())
+        return;
+    if (UNLIKELY(name == styleAttr && elementData()->m_styleAttributeIsDirty)) {
         updateStyleAttribute();
-
+        return;
+    }
 #if ENABLE(SVG)
     if (UNLIKELY(elementData()->m_animatedSVGAttributesAreDirty))
         updateAnimatedSVGAttribute(name);
 #endif
+}
 
+inline void Element::synchronizeAttribute(const AtomicString& localName) const
+{
+    // This version of synchronizeAttribute() is streamlined for the case where you don't have a full QualifiedName,
+    // e.g when called from DOM API.
+    if (!elementData())
+        return;
+    if (elementData()->m_styleAttributeIsDirty && equalPossiblyIgnoringCase(localName, styleAttr.localName(), shouldIgnoreAttributeCase(this))) {
+        updateStyleAttribute();
+        return;
+    }
+#if ENABLE(SVG)
+    if (elementData()->m_animatedSVGAttributesAreDirty) {
+        // We're not passing a namespace argument on purpose. SVGNames::*Attr are defined w/o namespaces as well.
+        updateAnimatedSVGAttribute(QualifiedName(nullAtom, localName, nullAtom));
+    }
+#endif
+}
+
+const AtomicString& Element::getAttribute(const QualifiedName& name) const
+{
+    if (!elementData())
+        return nullAtom;
+    synchronizeAttribute(name);
     if (const Attribute* attribute = getAttributeItem(name))
         return attribute->value();
     return nullAtom;
@@ -698,30 +739,12 @@
     return document()->view()->contentsToScreen(renderer()->absoluteBoundingBoxRectIgnoringTransforms());
 }
 
-static inline bool shouldIgnoreAttributeCase(const Element* e)
+const AtomicString& Element::getAttribute(const AtomicString& localName) const
 {
-    return e && e->document()->isHTMLDocument() && e->isHTMLElement();
-}
-
-const AtomicString& Element::getAttribute(const AtomicString& name) const
-{
     if (!elementData())
         return nullAtom;
-
-    bool ignoreCase = shouldIgnoreAttributeCase(this);
-
-    // Update the 'style' attribute if it's invalid and being requested:
-    if (elementData()->m_styleAttributeIsDirty && equalPossiblyIgnoringCase(name, styleAttr.localName(), ignoreCase))
-        updateStyleAttribute();
-
-#if ENABLE(SVG)
-    if (elementData()->m_animatedSVGAttributesAreDirty) {
-        // We're not passing a namespace argument on purpose. SVGNames::*Attr are defined w/o namespaces as well.
-        updateAnimatedSVGAttribute(QualifiedName(nullAtom, name, nullAtom));
-    }
-#endif
-
-    if (const Attribute* attribute = elementData()->getAttributeItem(name, ignoreCase))
+    synchronizeAttribute(localName);
+    if (const Attribute* attribute = elementData()->getAttributeItem(localName, shouldIgnoreAttributeCase(this)))
         return attribute->value();
     return nullAtom;
 }
@@ -731,28 +754,32 @@
     return getAttribute(QualifiedName(nullAtom, localName, namespaceURI));
 }
 
-void Element::setAttribute(const AtomicString& name, const AtomicString& value, ExceptionCode& ec)
+void Element::setAttribute(const AtomicString& localName, const AtomicString& value, ExceptionCode& ec)
 {
-    if (!Document::isValidName(name)) {
+    if (!Document::isValidName(localName)) {
         ec = INVALID_CHARACTER_ERR;
         return;
     }
 
-    const AtomicString& localName = shouldIgnoreAttributeCase(this) ? name.lower() : name;
+    synchronizeAttribute(localName);
+    const AtomicString& caseAdjustedLocalName = shouldIgnoreAttributeCase(this) ? localName.lower() : localName;
 
-    size_t index = ensureElementDataWithSynchronizedAttributes()->getAttributeItemIndex(localName, false);
-    const QualifiedName& qName = index != notFound ? attributeItem(index)->name() : QualifiedName(nullAtom, localName, nullAtom);
+    size_t index = elementData() ? elementData()->getAttributeItemIndex(caseAdjustedLocalName, false) : notFound;
+    const QualifiedName& qName = index != notFound ? attributeItem(index)->name() : QualifiedName(nullAtom, caseAdjustedLocalName, nullAtom);
     setAttributeInternal(index, qName, value, NotInSynchronizationOfLazyAttribute);
 }
 
 void Element::setAttribute(const QualifiedName& name, const AtomicString& value)
 {
-    setAttributeInternal(ensureElementDataWithSynchronizedAttributes()->getAttributeItemIndex(name), name, value, NotInSynchronizationOfLazyAttribute);
+    synchronizeAttribute(name);
+    size_t index = elementData() ? elementData()->getAttributeItemIndex(name) : notFound;
+    setAttributeInternal(index, name, value, NotInSynchronizationOfLazyAttribute);
 }
 
 void Element::setSynchronizedLazyAttribute(const QualifiedName& name, const AtomicString& value)
 {
-    setAttributeInternal(ensureUniqueElementData()->getAttributeItemIndex(name), name, value, InSynchronizationOfLazyAttribute);
+    size_t index = elementData() ? elementData()->getAttributeItemIndex(name) : notFound;
+    setAttributeInternal(index, name, value, InSynchronizationOfLazyAttribute);
 }
 
 inline void Element::setAttributeInternal(size_t index, const QualifiedName& name, const AtomicString& newValue, SynchronizationOfLazyAttribute inSynchronizationOfLazyAttribute)
@@ -1032,20 +1059,20 @@
 
 bool Element::hasAttributes() const
 {
-    updateInvalidAttributes();
+    synchronizeAllAttributes();
     return elementData() && elementData()->length();
 }
 
 bool Element::hasEquivalentAttributes(const Element* other) const
 {
-    const ElementData* elementData = elementDataWithSynchronizedAttributes();
-    const ElementData* otherElementData = other->elementDataWithSynchronizedAttributes();
-    if (elementData == otherElementData)
+    synchronizeAllAttributes();
+    other->synchronizeAllAttributes();
+    if (elementData() == other->elementData())
         return true;
-    if (elementData)
-        return elementData->isEquivalent(otherElementData);
-    if (otherElementData)
-        return otherElementData->isEquivalent(elementData);
+    if (elementData())
+        return elementData()->isEquivalent(other->elementData());
+    if (other->elementData())
+        return other->elementData()->isEquivalent(elementData());
     return true;
 }
 
@@ -1685,7 +1712,7 @@
         return 0;
     }
 
-    updateInvalidAttributes();
+    synchronizeAllAttributes();
     UniqueElementData* elementData = ensureUniqueElementData();
 
     size_t index = elementData->getAttributeItemIndex(attrNode->qualifiedName());
@@ -1722,10 +1749,9 @@
 
     ASSERT(document() == attr->document());
 
-    const ElementData* elementData = elementDataWithSynchronizedAttributes();
-    ASSERT(elementData);
+    synchronizeAttribute(attr->qualifiedName());
 
-    size_t index = elementData->getAttributeItemIndex(attr->qualifiedName());
+    size_t index = elementData()->getAttributeItemIndex(attr->qualifiedName());
     if (index == notFound) {
         ec = NOT_FOUND_ERR;
         return 0;
@@ -1813,12 +1839,12 @@
     removeAttribute(QualifiedName(nullAtom, localName, namespaceURI));
 }
 
-PassRefPtr<Attr> Element::getAttributeNode(const AtomicString& name)
+PassRefPtr<Attr> Element::getAttributeNode(const AtomicString& localName)
 {
-    const ElementData* elementData = elementDataWithSynchronizedAttributes();
-    if (!elementData)
+    if (!elementData())
         return 0;
-    const Attribute* attribute = elementData->getAttributeItem(name, shouldIgnoreAttributeCase(this));
+    synchronizeAttribute(localName);
+    const Attribute* attribute = elementData()->getAttributeItem(localName, shouldIgnoreAttributeCase(this));
     if (!attribute)
         return 0;
     return ensureAttr(attribute->name());
@@ -1826,32 +1852,31 @@
 
 PassRefPtr<Attr> Element::getAttributeNodeNS(const AtomicString& namespaceURI, const AtomicString& localName)
 {
-    const ElementData* elementData = elementDataWithSynchronizedAttributes();
-    if (!elementData)
+    if (!elementData())
         return 0;
-    const Attribute* attribute = elementData->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI));
+    QualifiedName qName(nullAtom, localName, namespaceURI);
+    synchronizeAttribute(qName);
+    const Attribute* attribute = elementData()->getAttributeItem(qName);
     if (!attribute)
         return 0;
     return ensureAttr(attribute->name());
 }
 
-bool Element::hasAttribute(const AtomicString& name) const
+bool Element::hasAttribute(const AtomicString& localName) const
 {
     if (!elementData())
         return false;
-
-    // This call to String::lower() seems to be required but
-    // there may be a way to remove it.
-    AtomicString localName = shouldIgnoreAttributeCase(this) ? name.lower() : name;
-    return elementDataWithSynchronizedAttributes()->getAttributeItem(localName, false);
+    synchronizeAttribute(localName);
+    return elementData()->getAttributeItem(shouldIgnoreAttributeCase(this) ? localName.lower() : localName, false);
 }
 
 bool Element::hasAttributeNS(const AtomicString& namespaceURI, const AtomicString& localName) const
 {
-    const ElementData* elementData = elementDataWithSynchronizedAttributes();
-    if (!elementData)
+    if (!elementData())
         return false;
-    return elementData->getAttributeItem(QualifiedName(nullAtom, localName, namespaceURI));
+    QualifiedName qName(nullAtom, localName, namespaceURI);
+    synchronizeAttribute(qName);
+    return elementData()->getAttributeItem(qName);
 }
 
 CSSStyleDeclaration *Element::style()
@@ -2743,7 +2768,7 @@
     if (hasSyntheticAttrChildNodes())
         detachAllAttrNodesFromElement();
 
-    other.updateInvalidAttributes();
+    other.synchronizeAllAttributes();
     if (!other.m_elementData) {
         m_elementData.clear();
         return;

Modified: trunk/Source/WebCore/dom/Element.h (143111 => 143112)


--- trunk/Source/WebCore/dom/Element.h	2013-02-16 22:27:12 UTC (rev 143111)
+++ trunk/Source/WebCore/dom/Element.h	2013-02-16 22:57:02 UTC (rev 143112)
@@ -369,10 +369,10 @@
     void parserSetAttributes(const Vector<Attribute>&, FragmentScriptingPermission);
 
     const ElementData* elementData() const { return m_elementData.get(); }
-    const ElementData* elementDataWithSynchronizedAttributes() const;
-    const ElementData* ensureElementDataWithSynchronizedAttributes() const;
     UniqueElementData* ensureUniqueElementData();
 
+    void synchronizeAllAttributes() const;
+
     // Clones attributes only.
     void cloneAttributesFromElement(const Element&);
 
@@ -647,7 +647,8 @@
     void didModifyAttribute(const QualifiedName&, const AtomicString&);
     void didRemoveAttribute(const QualifiedName&);
 
-    void updateInvalidAttributes() const;
+    void synchronizeAttribute(const QualifiedName&) const;
+    void synchronizeAttribute(const AtomicString& localName) const;
 
     void scrollByUnits(int units, ScrollGranularity);
 
@@ -774,20 +775,6 @@
     return static_cast<Element*>(n);
 }
 
-inline const ElementData* Element::elementDataWithSynchronizedAttributes() const
-{
-    updateInvalidAttributes();
-    return elementData();
-}
-
-inline const ElementData* Element::ensureElementDataWithSynchronizedAttributes() const
-{
-    updateInvalidAttributes();
-    if (elementData())
-        return elementData();
-    return const_cast<Element*>(this)->ensureUniqueElementData();
-}
-
 inline void Element::updateName(const AtomicString& oldName, const AtomicString& newName)
 {
     if (!inDocument() || isInShadowTree())
@@ -901,20 +888,6 @@
     return elementData()->getAttributeItem(name);
 }
 
-inline void Element::updateInvalidAttributes() const
-{
-    if (!elementData())
-        return;
-
-    if (elementData()->m_styleAttributeIsDirty)
-        updateStyleAttribute();
-
-#if ENABLE(SVG)
-    if (elementData()->m_animatedSVGAttributesAreDirty)
-        updateAnimatedSVGAttribute(anyQName());
-#endif
-}
-
 inline bool Element::hasID() const
 {
     return elementData() && elementData()->hasID();

Modified: trunk/Source/WebCore/dom/Node.cpp (143111 => 143112)


--- trunk/Source/WebCore/dom/Node.cpp	2013-02-16 22:27:12 UTC (rev 143111)
+++ trunk/Source/WebCore/dom/Node.cpp	2013-02-16 22:57:02 UTC (rev 143112)
@@ -1753,7 +1753,7 @@
     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.
         Element* owner1 = attr1->ownerElement();
-        owner1->elementDataWithSynchronizedAttributes(); // Force update invalid attributes.
+        owner1->synchronizeAllAttributes();
         unsigned length = owner1->attributeCount();
         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 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to