Inserting nodes is slow due to Node::notifyNodeListsAttributeChanged (20%+)
authorrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Jan 2012 23:08:50 +0000 (23:08 +0000)
committerrniwa@webkit.org <rniwa@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Jan 2012 23:08:50 +0000 (23:08 +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@104084 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 37831bd2f7dfe1e90d9ca53072186fee7eeebb3f..9daddfb3241d5066bead5ad1459ebcb6c0245f9f 100644 (file)
@@ -1,3 +1,59 @@
+2011-12-30  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-04  Ryosuke Niwa  <rniwa@webkit.org>
 
         Revert unintentional changes to Element.cpp in r104068.
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..cda89ba0da604f08d68c65c5e7520bc10de68a24 100644 (file)
 
 namespace WebCore {
 
+DynamicSubtreeNodeList::SubtreeCaches::SubtreeCaches()
+    : m_cachedItem(0)
+    , m_isLengthCacheValid(false)
+    , m_isItemCacheValid(false)
+    , m_DOMVersionAtTimeOfCaching(0)
+{
+}
+
+inline void DynamicSubtreeNodeList::SubtreeCaches::setLengthCache(Node* node, unsigned length)
+{
+    if (m_isItemCacheValid && !domVersionIsConsistent()) {
+        m_cachedItem = node;
+        m_isItemCacheValid = false;
+    }
+    m_cachedLength = length;
+    m_isLengthCacheValid = true;
+}
+
+inline void DynamicSubtreeNodeList::SubtreeCaches::setItemCache(Node* item, unsigned offset)
+{
+    if (m_isLengthCacheValid && !domVersionIsConsistent())
+        m_isLengthCacheValid = false;
+    m_cachedItem = item;
+    m_cachedItemOffset = offset;
+    m_isLengthCacheValid = true;
+}
+
+inline 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 +75,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 +94,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 +110,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 +124,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 +167,7 @@ bool DynamicSubtreeNodeList::isDynamicNodeList() const
 
 void DynamicSubtreeNodeList::invalidateCache()
 {
-    m_caches->reset();
+    m_caches.reset();
 }
 
 DynamicSubtreeNodeList::Caches::Caches()
@@ -149,12 +177,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..13ef6bbe4c19d5324a12c3870b06b8c57519fe0a 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* node, unsigned length);
+        void setItemCache(Node* item, unsigned offset);
+        void reset();
+
+    private:
+        Node* m_cachedItem;
+        unsigned m_cachedItemOffset;
+        unsigned m_cachedLength : 30;
+        unsigned m_isLengthCacheValid : 1;
+        unsigned m_isItemCacheValid : 1;
+
+        bool domVersionIsConsistent() const
+        {
+            return m_cachedItem && m_cachedItem->document()->domTreeVersion() == m_DOMVersionAtTimeOfCaching;
+        }
+        uint64_t m_DOMVersionAtTimeOfCaching;
+    };
+
+    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 9679b1229fd12a57a8d4f6e2f0268fc637d7bef1..365243c8bbae463539910b3f4707a4ef80cc622a 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