Inserting nodes is slow due to Node::notifyNodeListsAttributeChanged (20%+)
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Jan 2012 21:48:50 +0000 (21:48 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 5 Jan 2012 21:48:50 +0000 (21:48 +0000)
https://bugs.webkit.org/show_bug.cgi?id=73853

Reviewed by Antti Koivisto.

Lazily invalidate the node list caches instead of invaliding them at the time of modification. We use
the DOM tree version to detect whether caches need to be invalidated or not. We now invalidate caches more
frequently after this patch (in particular, invalidates caches that are stored on nodes not present in
the ancestry of the modified nodes); however, our study on major Web sites such as Gmail, Facebook, Twitter,
etc... indicate that about 1% of real-world usage benefits from keeping the caches alive across different
DOM tree versions.

In order to invalidate caches lazily, this patch adds replaces the type of m_caches in DynamicSubtreeNodeList
by DynamicSubtreeNodeList::SubtreeCaches which encapsulates member variables in DynamicNodeList::Caches and
invalidates values as needed. Also this change allows m_caches to be allocated as a part of
DynamicSubtreeNodeList instead of a separate ref-counted object.

* dom/Attr.cpp:
(WebCore::Attr::setValue):
(WebCore::Attr::childrenChanged):
* dom/DynamicNodeList.cpp:
(WebCore::DynamicSubtreeNodeList::DynamicSubtreeNodeList):
(WebCore::DynamicSubtreeNodeList::length):
(WebCore::DynamicSubtreeNodeList::itemForwardsFromCurrent):
(WebCore::DynamicSubtreeNodeList::itemBackwardsFromCurrent):
(WebCore::DynamicSubtreeNodeList::item):
(WebCore::DynamicSubtreeNodeList::invalidateCache):
(WebCore::DynamicNodeList::Caches::create):
(WebCore::DynamicNodeList::Caches::reset):
* dom/DynamicNodeList.h:
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::SubtreeCaches): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::isLengthCacheValid): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::isItemCacheValid): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedLength): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedItem): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedItemOffset): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::setLengthCache): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::setItemCache): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::reset): Added.
(WebCore::DynamicSubtreeNodeList::SubtreeCaches::domVersionIsConsistent): Added.
* dom/Element.cpp:
(WebCore::Element::updateAfterAttributeChanged):
* dom/Node.cpp:
(WebCore::Node::setTreeScopeRecursively): Clear caches when a node moves from one document to another.
(WebCore::Node::invalidateNodeListsCacheAfterAttributeChanged): Only clears child node list of Attr.
(WebCore::Node::invalidateNodeListsCacheAfterChildrenChanged): Only clears child node list.
(WebCore::NodeListsNodeData::invalidateCaches): Merged with invalidateCachesThatDependOnAttributes.
* dom/Node.h:
* dom/NodeRareData.h:
* html/HTMLElement.cpp:
(WebCore::HTMLElement::parseMappedAttribute):
* html/HTMLLabelElement.cpp:
* html/HTMLLabelElement.h:

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

Source/WebCore/ChangeLog
Source/WebCore/dom/Attr.cpp
Source/WebCore/dom/DynamicNodeList.cpp
Source/WebCore/dom/DynamicNodeList.h
Source/WebCore/dom/Element.cpp
Source/WebCore/dom/Node.cpp
Source/WebCore/dom/Node.h
Source/WebCore/dom/NodeRareData.h
Source/WebCore/html/HTMLElement.cpp
Source/WebCore/html/HTMLLabelElement.cpp
Source/WebCore/html/HTMLLabelElement.h

index 55c8505abd8bd36d7cbf6d63c544de3c5a691ed8..a6275c1a3e050613d86a0400c3cc46425d45f30d 100755 (executable)
@@ -1,3 +1,59 @@
+2012-01-05  Ryosuke Niwa  <rniwa@webkit.org>
+
+        Inserting nodes is slow due to Node::notifyNodeListsAttributeChanged (20%+)
+        https://bugs.webkit.org/show_bug.cgi?id=73853
+
+        Reviewed by Antti Koivisto.
+
+        Lazily invalidate the node list caches instead of invaliding them at the time of modification. We use
+        the DOM tree version to detect whether caches need to be invalidated or not. We now invalidate caches more
+        frequently after this patch (in particular, invalidates caches that are stored on nodes not present in
+        the ancestry of the modified nodes); however, our study on major Web sites such as Gmail, Facebook, Twitter,
+        etc... indicate that about 1% of real-world usage benefits from keeping the caches alive across different
+        DOM tree versions.
+
+        In order to invalidate caches lazily, this patch adds replaces the type of m_caches in DynamicSubtreeNodeList
+        by DynamicSubtreeNodeList::SubtreeCaches which encapsulates member variables in DynamicNodeList::Caches and
+        invalidates values as needed. Also this change allows m_caches to be allocated as a part of
+        DynamicSubtreeNodeList instead of a separate ref-counted object.
+
+        * dom/Attr.cpp:
+        (WebCore::Attr::setValue):
+        (WebCore::Attr::childrenChanged):
+        * dom/DynamicNodeList.cpp:
+        (WebCore::DynamicSubtreeNodeList::DynamicSubtreeNodeList):
+        (WebCore::DynamicSubtreeNodeList::length):
+        (WebCore::DynamicSubtreeNodeList::itemForwardsFromCurrent):
+        (WebCore::DynamicSubtreeNodeList::itemBackwardsFromCurrent):
+        (WebCore::DynamicSubtreeNodeList::item):
+        (WebCore::DynamicSubtreeNodeList::invalidateCache):
+        (WebCore::DynamicNodeList::Caches::create):
+        (WebCore::DynamicNodeList::Caches::reset):
+        * dom/DynamicNodeList.h:
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::SubtreeCaches): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::isLengthCacheValid): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::isItemCacheValid): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedLength): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedItem): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::cachedItemOffset): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::setLengthCache): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::setItemCache): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::reset): Added.
+        (WebCore::DynamicSubtreeNodeList::SubtreeCaches::domVersionIsConsistent): Added.
+        * dom/Element.cpp:
+        (WebCore::Element::updateAfterAttributeChanged):
+        * dom/Node.cpp:
+        (WebCore::Node::setTreeScopeRecursively): Clear caches when a node moves from one document to another.
+        (WebCore::Node::invalidateNodeListsCacheAfterAttributeChanged): Only clears child node list of Attr.
+        (WebCore::Node::invalidateNodeListsCacheAfterChildrenChanged): Only clears child node list.
+        (WebCore::NodeListsNodeData::invalidateCaches): Merged with invalidateCachesThatDependOnAttributes.
+        * dom/Node.h:
+        * dom/NodeRareData.h:
+        * html/HTMLElement.cpp:
+        (WebCore::HTMLElement::parseMappedAttribute):
+        * html/HTMLLabelElement.cpp:
+        * html/HTMLLabelElement.h:
+
 2012-01-05  Ojan Vafai  <ojan@chromium.org>
 
         IE quirk for percentage size on a table element doesn't work with orthogonal writing modes
index 6930fa1dc0605068ce554fc12503e2fb775ead5b..cf2111ff2f0434f8bccf18d58ff99cd04e29b5a5 100644 (file)
@@ -129,7 +129,7 @@ void Attr::setValue(const AtomicString& value)
     createTextChild();
     m_ignoreChildrenChanged--;
 
-    invalidateNodeListsCacheAfterAttributeChanged(m_attribute->name());
+    invalidateNodeListsCacheAfterAttributeChanged();
 }
 
 void Attr::setValue(const AtomicString& value, ExceptionCode&)
@@ -174,7 +174,7 @@ void Attr::childrenChanged(bool changedByParser, Node* beforeChange, Node* after
 
     Node::childrenChanged(changedByParser, beforeChange, afterChange, childCountDelta);
 
-    invalidateNodeListsCacheAfterAttributeChanged(m_attribute->name());
+    invalidateNodeListsCacheAfterAttributeChanged();
 
     // FIXME: We should include entity references in the value
 
index 788ab6ef6809e6b40cd4d357b71e50880e342790..c234512874fcd09325f7c2a98754fd5b215cd35b 100644 (file)
 
 namespace WebCore {
 
+DynamicSubtreeNodeList::SubtreeCaches::SubtreeCaches()
+    : m_cachedItem(0)
+    , m_isLengthCacheValid(false)
+    , m_isItemCacheValid(false)
+    , m_domTreeVersionAtTimeOfCaching(0)
+{
+}
+
+void DynamicSubtreeNodeList::SubtreeCaches::setLengthCache(Node* node, unsigned length)
+{
+    if (m_isItemCacheValid && !domVersionIsConsistent()) {
+        m_cachedItem = node;
+        m_isItemCacheValid = false;
+    }
+    m_cachedLength = length;
+    m_isLengthCacheValid = true;
+    m_domTreeVersionAtTimeOfCaching = node->document()->domTreeVersion();
+}
+
+void DynamicSubtreeNodeList::SubtreeCaches::setItemCache(Node* item, unsigned offset)
+{
+    if (m_isLengthCacheValid && !domVersionIsConsistent())
+        m_isLengthCacheValid = false;
+    m_cachedItem = item;
+    m_cachedItemOffset = offset;
+    m_isItemCacheValid = true;
+    m_domTreeVersionAtTimeOfCaching = item->document()->domTreeVersion();
+}
+
+void DynamicSubtreeNodeList::SubtreeCaches::reset()
+{
+    m_cachedItem = 0;
+    m_isLengthCacheValid = false;
+    m_isItemCacheValid = false;
+}
+
 DynamicSubtreeNodeList::DynamicSubtreeNodeList(PassRefPtr<Node> node)
     : DynamicNodeList(node)
-    , m_caches(Caches::create())
 {
     rootNode()->registerDynamicSubtreeNodeList(this);
 }
@@ -42,16 +77,15 @@ DynamicSubtreeNodeList::~DynamicSubtreeNodeList()
 
 unsigned DynamicSubtreeNodeList::length() const
 {
-    if (m_caches->isLengthCacheValid)
-        return m_caches->cachedLength;
+    if (m_caches.isLengthCacheValid())
+        return m_caches.cachedLength();
 
     unsigned length = 0;
 
     for (Node* n = node()->firstChild(); n; n = n->traverseNextNode(rootNode()))
         length += n->isElementNode() && nodeMatches(static_cast<Element*>(n));
 
-    m_caches->cachedLength = length;
-    m_caches->isLengthCacheValid = true;
+    m_caches.setLengthCache(node(), length);
 
     return length;
 }
@@ -62,9 +96,7 @@ Node* DynamicSubtreeNodeList::itemForwardsFromCurrent(Node* start, unsigned offs
     for (Node* n = start; n; n = n->traverseNextNode(rootNode())) {
         if (n->isElementNode() && nodeMatches(static_cast<Element*>(n))) {
             if (!remainingOffset) {
-                m_caches->lastItem = n;
-                m_caches->lastItemOffset = offset;
-                m_caches->isItemCacheValid = true;
+                m_caches.setItemCache(n, offset);
                 return n;
             }
             --remainingOffset;
@@ -80,9 +112,7 @@ Node* DynamicSubtreeNodeList::itemBackwardsFromCurrent(Node* start, unsigned off
     for (Node* n = start; n; n = n->traversePreviousNode(rootNode())) {
         if (n->isElementNode() && nodeMatches(static_cast<Element*>(n))) {
             if (!remainingOffset) {
-                m_caches->lastItem = n;
-                m_caches->lastItemOffset = offset;
-                m_caches->isItemCacheValid = true;
+                m_caches.setItemCache(n, offset);
                 return n;
             }
             ++remainingOffset;
@@ -96,12 +126,12 @@ Node* DynamicSubtreeNodeList::item(unsigned offset) const
 {
     int remainingOffset = offset;
     Node* start = node()->firstChild();
-    if (m_caches->isItemCacheValid) {
-        if (offset == m_caches->lastItemOffset)
-            return m_caches->lastItem;
-        else if (offset > m_caches->lastItemOffset || m_caches->lastItemOffset - offset < offset) {
-            start = m_caches->lastItem;
-            remainingOffset -= m_caches->lastItemOffset;
+    if (m_caches.isItemCacheValid()) {
+        if (offset == m_caches.cachedItemOffset())
+            return m_caches.cachedItem();
+        if (offset > m_caches.cachedItemOffset() || m_caches.cachedItemOffset() - offset < offset) {
+            start = m_caches.cachedItem();
+            remainingOffset -= m_caches.cachedItemOffset();
         }
     }
 
@@ -139,7 +169,7 @@ bool DynamicSubtreeNodeList::isDynamicNodeList() const
 
 void DynamicSubtreeNodeList::invalidateCache()
 {
-    m_caches->reset();
+    m_caches.reset();
 }
 
 DynamicSubtreeNodeList::Caches::Caches()
@@ -149,12 +179,12 @@ DynamicSubtreeNodeList::Caches::Caches()
 {
 }
 
-PassRefPtr<DynamicSubtreeNodeList::Caches> DynamicSubtreeNodeList::Caches::create()
+PassRefPtr<DynamicNodeList::Caches> DynamicNodeList::Caches::create()
 {
     return adoptRef(new Caches());
 }
 
-void DynamicSubtreeNodeList::Caches::reset()
+void DynamicNodeList::Caches::reset()
 {
     lastItem = 0;
     isLengthCacheValid = false;
index 606fc64bf99a96e375b9d4d538b97e4fe21d4a3a..7f36e8190ad061016a5751a8e54a600db526eaa4 100644 (file)
@@ -24,6 +24,7 @@
 #ifndef DynamicNodeList_h
 #define DynamicNodeList_h
 
+#include "Document.h"
 #include "NodeList.h"
 #include <wtf/RefCounted.h>
 #include <wtf/Forward.h>
@@ -45,9 +46,11 @@ public:
         unsigned lastItemOffset;
         bool isLengthCacheValid : 1;
         bool isItemCacheValid : 1;
+
     protected:
         Caches();
     };
+
     DynamicNodeList(PassRefPtr<Node> node)
         : m_node(node)
     { }
@@ -76,9 +79,40 @@ public:
 
 protected:
     DynamicSubtreeNodeList(PassRefPtr<Node> rootNode);
-    mutable RefPtr<Caches> m_caches;
 
 private:
+
+    class SubtreeCaches {
+    public:
+        SubtreeCaches();
+
+        bool isLengthCacheValid() const { return m_isLengthCacheValid && domVersionIsConsistent(); }
+        bool isItemCacheValid() const { return m_isItemCacheValid && domVersionIsConsistent(); }
+
+        unsigned cachedLength() const { return m_cachedLength; }
+        Node* cachedItem() const { return m_cachedItem; }
+        unsigned cachedItemOffset() const { return m_cachedItemOffset; }
+
+        void setLengthCache(Node* nodeForDocumentVersion, unsigned length);
+        void setItemCache(Node*, unsigned offset);
+        void reset();
+
+    private:
+        Node* m_cachedItem;
+        unsigned m_cachedItemOffset;
+        unsigned m_cachedLength;
+        bool m_isLengthCacheValid : 1;
+        bool m_isItemCacheValid : 1;
+
+        bool domVersionIsConsistent() const
+        {
+            return m_cachedItem && m_cachedItem->document()->domTreeVersion() == m_domTreeVersionAtTimeOfCaching;
+        }
+        uint64_t m_domTreeVersionAtTimeOfCaching;
+    };
+
+    mutable SubtreeCaches m_caches;
+
     virtual bool isDynamicNodeList() const;
     Node* itemForwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const;
     Node* itemBackwardsFromCurrent(Node* start, unsigned offset, int remainingOffset) const;
index 0999596917e74d048d15cc1340569397e209ca53..10931165169b98cf712d04be38613f56146f3f24 100644 (file)
@@ -674,7 +674,7 @@ void Element::attributeChanged(Attribute* attr, bool)
 
 void Element::updateAfterAttributeChanged(Attribute* attr)
 {
-    invalidateNodeListsCacheAfterAttributeChanged(attr->name());
+    invalidateNodeListsCacheAfterAttributeChanged();
 
     if (!AXObjectCache::accessibilityEnabled())
         return;
index 9e6dd5fac6fdee0fe0c6cc3a7d249a2a5fa96c49..1a9dcf70da5e76bb536fd856ee248bf69cda5205 100644 (file)
@@ -489,6 +489,7 @@ void Node::setTreeScopeRecursively(TreeScope* newTreeScope)
             node->ensureRareData()->setTreeScope(newTreeScope);
 
         if (node->hasRareData() && node->rareData()->nodeLists()) {
+            node->rareData()->nodeLists()->invalidateCaches();
             if (currentTreeScope)
                 currentTreeScope->removeNodeListCache();
             newTreeScope->addNodeListCache();
@@ -1002,73 +1003,19 @@ void Node::unregisterDynamicSubtreeNodeList(DynamicSubtreeNodeList* list)
     removeNodeListCacheIfPossible(this, data);
 }
 
-void Node::invalidateNodeListsCacheAfterAttributeChanged(const QualifiedName& attrName)
+void Node::invalidateNodeListsCacheAfterAttributeChanged()
 {
     if (hasRareData() && isAttributeNode()) {
         NodeRareData* data = rareData();
         ASSERT(!data->nodeLists());
         data->clearChildNodeListCache();
     }
-
-    // This list should be sync'ed with NodeListsNodeData.
-    if (attrName != classAttr
-#if ENABLE(MICRODATA)
-        && attrName != itemscopeAttr
-        && attrName != itempropAttr
-#endif
-        && attrName != nameAttr)
-        return;
-
-    if (!treeScope()->hasNodeListCaches())
-        return;
-
-    for (Node* node = this; node; node = node->parentNode()) {
-        ASSERT(this == node || !node->isAttributeNode());
-        if (!node->hasRareData())
-            continue;
-        NodeRareData* data = node->rareData();
-        if (!data->nodeLists())
-            continue;
-
-        data->nodeLists()->invalidateCachesThatDependOnAttributes();
-        removeNodeListCacheIfPossible(node, data);
-    }
 }
 
 void Node::invalidateNodeListsCacheAfterChildrenChanged()
 {
     if (hasRareData())
         rareData()->clearChildNodeListCache();
-
-    if (!treeScope()->hasNodeListCaches())
-        return;
-    for (Node* node = this; node; node = node->parentNode()) {
-        if (!node->hasRareData())
-            continue;
-        NodeRareData* data = node->rareData();
-        if (!data->nodeLists())
-            continue;
-
-        data->nodeLists()->invalidateCaches();
-
-        NodeListsNodeData::NodeListSet::iterator end = data->nodeLists()->m_listsWithCaches.end();
-        for (NodeListsNodeData::NodeListSet::iterator it = data->nodeLists()->m_listsWithCaches.begin(); it != end; ++it)
-            (*it)->invalidateCache();
-
-        removeNodeListCacheIfPossible(node, data);
-    }
-}
-
-void Node::notifyLocalNodeListsLabelChanged()
-{
-    if (!hasRareData())
-        return;
-    NodeRareData* data = rareData();
-    if (!data->nodeLists())
-        return;
-
-    if (data->nodeLists()->m_labelsNodeListCache)
-        data->nodeLists()->m_labelsNodeListCache->invalidateCache();
 }
 
 void Node::removeCachedClassNodeList(ClassNodeList* list, const String& className)
@@ -2243,23 +2190,6 @@ FloatPoint Node::convertFromPage(const FloatPoint& p) const
     return p;
 }
 
-#if ENABLE(MICRODATA)
-void Node::itemTypeAttributeChanged()
-{
-    Node * rootNode = document();
-
-    if (!rootNode->hasRareData())
-        return;
-
-    NodeRareData* data = rootNode->rareData();
-
-    if (!data->nodeLists())
-        return;
-
-    data->nodeLists()->invalidateMicrodataItemListCaches();
-}
-#endif
-
 #ifndef NDEBUG
 
 static void appendAttributeDesc(const Node* node, String& string, const QualifiedName& name, const char* attrDesc)
@@ -2388,11 +2318,7 @@ void NodeListsNodeData::invalidateCaches()
     TagNodeListCacheNS::const_iterator tagCacheNSEnd = m_tagNodeListCacheNS.end();
     for (TagNodeListCacheNS::const_iterator it = m_tagNodeListCacheNS.begin(); it != tagCacheNSEnd; ++it)
         it->second->invalidateCache();
-    invalidateCachesThatDependOnAttributes();
-}
 
-void NodeListsNodeData::invalidateCachesThatDependOnAttributes()
-{
     ClassNodeListCache::iterator classCacheEnd = m_classNodeListCache.end();
     for (ClassNodeListCache::iterator it = m_classNodeListCache.begin(); it != classCacheEnd; ++it)
         it->second->invalidateCache();
@@ -2406,6 +2332,7 @@ void NodeListsNodeData::invalidateCachesThatDependOnAttributes()
 #if ENABLE(MICRODATA)
     invalidateMicrodataItemListCaches();
 #endif
+    
 }
 
 #if ENABLE(MICRODATA)
index fa69b75940b3d4187bc97453563da9cabd1b596f..ea49f60f55e60b83dddcc3dfb90b3ed3ab370e37 100644 (file)
@@ -521,9 +521,8 @@ public:
 
     void registerDynamicSubtreeNodeList(DynamicSubtreeNodeList*);
     void unregisterDynamicSubtreeNodeList(DynamicSubtreeNodeList*);
-    void invalidateNodeListsCacheAfterAttributeChanged(const QualifiedName&);
+    void invalidateNodeListsCacheAfterAttributeChanged();
     void invalidateNodeListsCacheAfterChildrenChanged();
-    void notifyLocalNodeListsLabelChanged();
     void removeCachedClassNodeList(ClassNodeList*, const String&);
 
     void removeCachedNameNodeList(NameNodeList*, const String&);
@@ -591,8 +590,6 @@ public:
     virtual EventTargetData* ensureEventTargetData();
 
 #if ENABLE(MICRODATA)
-    void itemTypeAttributeChanged();
-
     DOMSettableTokenList* itemProp();
     DOMSettableTokenList* itemRef();
     DOMSettableTokenList* itemType();
index fb0f76f85c8a6905f8d5a6989e4d4d97a2bce3e3..2249e526b96d0596f6276b067376a392093a6ce2 100644 (file)
@@ -78,7 +78,6 @@ public:
     }
     
     void invalidateCaches();
-    void invalidateCachesThatDependOnAttributes();
 
 #if ENABLE(MICRODATA)
     void invalidateMicrodataItemListCaches();
index bacd44f7e8492e5ee77636ea182381914ee06bf6..573d581d42d8f91e17caa198854702b9b54a4fce 100644 (file)
@@ -237,7 +237,6 @@ void HTMLElement::parseMappedAttribute(Attribute* attr)
         setItemRef(attr->value());
     } else if (attr->name() == itemtypeAttr) {
         setItemType(attr->value());
-        itemTypeAttributeChanged();
 #endif
     }
 // standard events
index 318fe88da2cb3ac1905c9b83fa51e1c9c7cad9aa..6cbd21900b03e79c6f003f0d14593468b4f39a03 100644 (file)
@@ -154,15 +154,4 @@ void HTMLLabelElement::accessKeyAction(bool sendMouseEvents)
         HTMLElement::accessKeyAction(sendMouseEvents);
 }
 
-void HTMLLabelElement::parseMappedAttribute(Attribute* attribute)
-{
-    if (attribute->name() == forAttr) {
-        // htmlFor attribute change affects other nodes than this.
-        // Clear the caches to ensure that the labels caches are cleared.
-        if (document())
-            document()->notifyLocalNodeListsLabelChanged();
-    } else
-        HTMLElement::parseMappedAttribute(attribute);
-}
-                
 } // namespace
index 2f8497839278c45eff34be383c97a09cbd444201..32a55e449668fedd4d55d4b74ab60196df8b7428 100644 (file)
@@ -50,8 +50,6 @@ private:
     virtual void defaultEventHandler(Event*);
 
     void focus(bool restorePreviousSelection = true);
-
-    virtual void parseMappedAttribute(Attribute*);
 };
 
 } //namespace