2011-02-24 Dirk Schulze <krit@webkit.org>
authorkrit@webkit.org <krit@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 24 Feb 2011 16:22:46 +0000 (16:22 +0000)
committerkrit@webkit.org <krit@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 24 Feb 2011 16:22:46 +0000 (16:22 +0000)
        Reviewed by Darin Adler.

        Removing a SVG animation target during animation crashes WebKit
        https://bugs.webkit.org/show_bug.cgi?id=12065

        SVGAnimations with IRI references via 'xlink:href' are slow
        https://bugs.webkit.org/show_bug.cgi?id=49437

        Test that WebKit does not crash, if the target element of a SVG animation gets removed, or if
        its id changed.

        * platform/mac/svg/custom/animate-target-id-changed-expected.checksum: Added.
        * platform/mac/svg/custom/animate-target-id-changed-expected.png: Added.
        * platform/mac/svg/custom/animate-target-id-changed-expected.txt: Added.
        * platform/mac/svg/custom/animate-target-removed-from-document-expected.checksum: Added.
        * platform/mac/svg/custom/animate-target-removed-from-document-expected.png: Added.
        * platform/mac/svg/custom/animate-target-removed-from-document-expected.txt: Added.
        * svg/custom/animate-target-id-changed.svg: Added.
        * svg/custom/animate-target-removed-from-document.svg: Added.
2011-02-24  Dirk Schulze  <krit@webkit.org>

        Reviewed by Darin Adler.

        Removing a SVG animation target during animation crashes WebKit
        https://bugs.webkit.org/show_bug.cgi?id=12065

        SVGAnimations with IRI references via 'xlink:href' are slow
        https://bugs.webkit.org/show_bug.cgi?id=49437

        Store reference to target element for SVG animation elements. This is important if the
        target gets referenced via 'xlink:href'. At the moment we would call getElementById() multiple
        times on every animation step. A very expensive operation. This will be avoided with this patch.
        On the other hand, we need to be sure that the target element is always valid. The reference is
        reset, if the target was removed from document or its destructor was called. A HashMap in
        SVGDocumentExtensions stores all mappings from target element to all current animation elements.

        Tests: svg/custom/animate-target-id-changed.svg
               svg/custom/animate-target-removed-from-document.svg

        * svg/SVGDocumentExtensions.cpp:
        (WebCore::SVGDocumentExtensions::~SVGDocumentExtensions):
        (WebCore::SVGDocumentExtensions::addAnimationElementToTarget): New animation gets applied to target.
        (WebCore::SVGDocumentExtensions::removeAnimationElementFromTarget): Animation stoped, remove it from HashMap.
        (WebCore::SVGDocumentExtensions::removeAllAnimationElementsFromTarget): Target no longer in document, reset all
        references in SVG animation elements.
        * svg/SVGDocumentExtensions.h:
        * svg/SVGElement.cpp:
        (WebCore::SVGElement::~SVGElement):
        (WebCore::SVGElement::removedFromDocument):
        (WebCore::SVGElement::attributeChanged):
        * svg/SVGElement.h:
        * svg/SVGHKernElement.cpp:
        (WebCore::SVGHKernElement::removedFromDocument):
        * svg/SVGVKernElement.cpp:
        (WebCore::SVGVKernElement::removedFromDocument):
        * svg/animation/SVGSMILElement.cpp:
        (WebCore::SVGSMILElement::SVGSMILElement):
        (WebCore::SVGSMILElement::removedFromDocument):
        (WebCore::SVGSMILElement::eventBaseFor):
        (WebCore::SVGSMILElement::targetElement):
        * svg/animation/SVGSMILElement.h:
        (WebCore::SVGSMILElement::resetTargetElement):

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

18 files changed:
LayoutTests/ChangeLog
LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.checksum [new file with mode: 0644]
LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.png [new file with mode: 0644]
LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.txt [new file with mode: 0644]
LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.checksum [new file with mode: 0644]
LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.png [new file with mode: 0644]
LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.txt [new file with mode: 0644]
LayoutTests/svg/custom/animate-target-id-changed.svg [new file with mode: 0644]
LayoutTests/svg/custom/animate-target-removed-from-document.svg [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/svg/SVGDocumentExtensions.cpp
Source/WebCore/svg/SVGDocumentExtensions.h
Source/WebCore/svg/SVGElement.cpp
Source/WebCore/svg/SVGElement.h
Source/WebCore/svg/SVGHKernElement.cpp
Source/WebCore/svg/SVGVKernElement.cpp
Source/WebCore/svg/animation/SVGSMILElement.cpp
Source/WebCore/svg/animation/SVGSMILElement.h

index 2b694205f5a8191b0d2d9084df186f1c3097cf7c..5f0e173b6a4ef074b26e29097934679302b693fd 100644 (file)
@@ -1,3 +1,25 @@
+2011-02-24  Dirk Schulze  <krit@webkit.org>
+
+        Reviewed by Darin Adler.
+
+        Removing a SVG animation target during animation crashes WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=12065
+
+        SVGAnimations with IRI references via 'xlink:href' are slow
+        https://bugs.webkit.org/show_bug.cgi?id=49437
+
+        Test that WebKit does not crash, if the target element of a SVG animation gets removed, or if
+        its id changed.
+
+        * platform/mac/svg/custom/animate-target-id-changed-expected.checksum: Added.
+        * platform/mac/svg/custom/animate-target-id-changed-expected.png: Added.
+        * platform/mac/svg/custom/animate-target-id-changed-expected.txt: Added.
+        * platform/mac/svg/custom/animate-target-removed-from-document-expected.checksum: Added.
+        * platform/mac/svg/custom/animate-target-removed-from-document-expected.png: Added.
+        * platform/mac/svg/custom/animate-target-removed-from-document-expected.txt: Added.
+        * svg/custom/animate-target-id-changed.svg: Added.
+        * svg/custom/animate-target-removed-from-document.svg: Added.
+
 2011-02-24  Simon Fraser  <simon.fraser@apple.com>
 
         Reviewed by Eric Seidel.
diff --git a/LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.checksum b/LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.checksum
new file mode 100644 (file)
index 0000000..7363841
--- /dev/null
@@ -0,0 +1 @@
+778803df0a824ed8f2c7dfa07c56832e
\ No newline at end of file
diff --git a/LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.png b/LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.png
new file mode 100644 (file)
index 0000000..987150b
Binary files /dev/null and b/LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.png differ
diff --git a/LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.txt b/LayoutTests/platform/mac/svg/custom/animate-target-id-changed-expected.txt
new file mode 100644 (file)
index 0000000..2cafd1f
--- /dev/null
@@ -0,0 +1,6 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderSVGRoot {svg} at (0,0) size 100x100
+    RenderSVGPath {rect} at (0,0) size 100x100 [fill={[type=SOLID] [color=#008000]}] [x=0.00] [y=0.00] [width=100.00] [height=100.00]
+    RenderSVGPath {rect} at (0,0) size 100x100 [fill={[type=SOLID] [color=#008000]}] [x=0.00] [y=0.00] [width=100.00] [height=100.00]
diff --git a/LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.checksum b/LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.checksum
new file mode 100644 (file)
index 0000000..7363841
--- /dev/null
@@ -0,0 +1 @@
+778803df0a824ed8f2c7dfa07c56832e
\ No newline at end of file
diff --git a/LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.png b/LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.png
new file mode 100644 (file)
index 0000000..987150b
Binary files /dev/null and b/LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.png differ
diff --git a/LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.txt b/LayoutTests/platform/mac/svg/custom/animate-target-removed-from-document-expected.txt
new file mode 100644 (file)
index 0000000..e2152e7
--- /dev/null
@@ -0,0 +1,5 @@
+layer at (0,0) size 800x600
+  RenderView at (0,0) size 800x600
+layer at (0,0) size 800x600
+  RenderSVGRoot {svg} at (0,0) size 100x100
+    RenderSVGPath {rect} at (0,0) size 100x100 [fill={[type=SOLID] [color=#008000]}] [x=0.00] [y=0.00] [width=100.00] [height=100.00]
diff --git a/LayoutTests/svg/custom/animate-target-id-changed.svg b/LayoutTests/svg/custom/animate-target-id-changed.svg
new file mode 100644 (file)
index 0000000..f019f9c
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
+"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<rect width="100" height="100" fill="green"/>
+<rect id="target" width="100" height="100" fill="red"/>
+<animate attributeName="fill" xlink:href="#target" from="green" to="green" dur="0.5" fill="freeze"/>
+<script type="text/ecmascript">
+<![CDATA[
+if (window.layoutTestController) {
+    layoutTestController.waitUntilDone();
+    setTimeout(function() {
+        document.getElementById('target').setAttribute("id", "newId");
+        setTimeout(function() { layoutTestController.notifyDone(); }, 0);
+   }, 0);
+}
+]]>
+</script>
+</svg>
diff --git a/LayoutTests/svg/custom/animate-target-removed-from-document.svg b/LayoutTests/svg/custom/animate-target-removed-from-document.svg
new file mode 100644 (file)
index 0000000..fd7a6e2
--- /dev/null
@@ -0,0 +1,20 @@
+<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
+"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+<rect width="100" height="100" fill="green"/>
+<rect id="target" width="100" height="100" fill="red"/>
+<animate attributeName="fill" xlink:href="#target" from="green" to="green" dur="0.5" fill="freeze"/>
+<script type="text/ecmascript">
+<![CDATA[
+if (window.layoutTestController) {
+    layoutTestController.waitUntilDone();
+    setTimeout(function() {
+        document.getElementsByTagName("svg")[0].removeChild(document.getElementById('target'));
+        setTimeout(function() { layoutTestController.notifyDone(); }, 0);
+   }, 0);
+}
+]]>
+</script>
+</svg>
index a01005f5006198dd8c5527378c776d9a1a5b50d6..14f56592bb5c3f42700b64e5520cf354202e2b6d 100644 (file)
@@ -1,3 +1,47 @@
+2011-02-24  Dirk Schulze  <krit@webkit.org>
+
+        Reviewed by Darin Adler.
+
+        Removing a SVG animation target during animation crashes WebKit
+        https://bugs.webkit.org/show_bug.cgi?id=12065
+
+        SVGAnimations with IRI references via 'xlink:href' are slow
+        https://bugs.webkit.org/show_bug.cgi?id=49437
+
+        Store reference to target element for SVG animation elements. This is important if the
+        target gets referenced via 'xlink:href'. At the moment we would call getElementById() multiple
+        times on every animation step. A very expensive operation. This will be avoided with this patch.
+        On the other hand, we need to be sure that the target element is always valid. The reference is
+        reset, if the target was removed from document or its destructor was called. A HashMap in
+        SVGDocumentExtensions stores all mappings from target element to all current animation elements.
+
+        Tests: svg/custom/animate-target-id-changed.svg
+               svg/custom/animate-target-removed-from-document.svg
+
+        * svg/SVGDocumentExtensions.cpp:
+        (WebCore::SVGDocumentExtensions::~SVGDocumentExtensions):
+        (WebCore::SVGDocumentExtensions::addAnimationElementToTarget): New animation gets applied to target.
+        (WebCore::SVGDocumentExtensions::removeAnimationElementFromTarget): Animation stoped, remove it from HashMap.
+        (WebCore::SVGDocumentExtensions::removeAllAnimationElementsFromTarget): Target no longer in document, reset all
+        references in SVG animation elements. 
+        * svg/SVGDocumentExtensions.h:
+        * svg/SVGElement.cpp:
+        (WebCore::SVGElement::~SVGElement):
+        (WebCore::SVGElement::removedFromDocument):
+        (WebCore::SVGElement::attributeChanged):
+        * svg/SVGElement.h:
+        * svg/SVGHKernElement.cpp:
+        (WebCore::SVGHKernElement::removedFromDocument):
+        * svg/SVGVKernElement.cpp:
+        (WebCore::SVGVKernElement::removedFromDocument):
+        * svg/animation/SVGSMILElement.cpp:
+        (WebCore::SVGSMILElement::SVGSMILElement):
+        (WebCore::SVGSMILElement::removedFromDocument):
+        (WebCore::SVGSMILElement::eventBaseFor):
+        (WebCore::SVGSMILElement::targetElement):
+        * svg/animation/SVGSMILElement.h:
+        (WebCore::SVGSMILElement::resetTargetElement):
+
 2011-02-24  Simon Fraser  <simon.fraser@apple.com>
 
         Reviewed by Eric Seidel.
index 7f7ba67133568f98d1e27965e6827cb8788e3d5d..7fb9c26bde1bed6c174894bcb7d691b583ae3356 100644 (file)
@@ -32,6 +32,7 @@
 #include "FrameLoader.h"
 #include "Page.h"
 #include "SMILTimeContainer.h"
+#include "SVGElement.h"
 #include "SVGSMILElement.h"
 #include "SVGSVGElement.h"
 #include "ScriptController.h"
@@ -48,6 +49,7 @@ SVGDocumentExtensions::SVGDocumentExtensions(Document* document)
 
 SVGDocumentExtensions::~SVGDocumentExtensions()
 {
+    deleteAllValues(m_animatedElements);
     deleteAllValues(m_pendingResources);
 }
 
@@ -135,6 +137,52 @@ bool SVGDocumentExtensions::sampleAnimationAtTime(const String& elementId, SVGSM
 #endif
 }
 
+void SVGDocumentExtensions::addAnimationElementToTarget(SVGSMILElement* animationElement, SVGElement* targetElement)
+{
+    ASSERT(targetElement);
+    ASSERT(animationElement);
+
+    if (HashSet<SVGSMILElement*>* animationElementsForTarget = m_animatedElements.get(targetElement)) {
+        animationElementsForTarget->add(animationElement);
+        return;
+    }
+
+    HashSet<SVGSMILElement*>* animationElementsForTarget = new HashSet<SVGSMILElement*>;
+    animationElementsForTarget->add(animationElement);
+    m_animatedElements.set(targetElement, animationElementsForTarget);
+}
+
+void SVGDocumentExtensions::removeAnimationElementFromTarget(SVGSMILElement* animationElement, SVGElement* targetElement)
+{
+    ASSERT(targetElement);
+    ASSERT(animationElement);
+
+    HashMap<SVGElement*, HashSet<SVGSMILElement*>* >::iterator it = m_animatedElements.find(targetElement);
+    ASSERT(it != m_animatedElements.end());
+    
+    HashSet<SVGSMILElement*>* animationElementsForTarget = it->second;
+    ASSERT(!animationElementsForTarget->isEmpty());
+
+    animationElementsForTarget->remove(animationElement);
+    if (animationElementsForTarget->isEmpty()) {
+        m_animatedElements.remove(it);
+        delete animationElementsForTarget;
+    }
+}
+
+void SVGDocumentExtensions::removeAllAnimationElementsFromTarget(SVGElement* targetElement)
+{
+    ASSERT(targetElement);
+    HashSet<SVGSMILElement*>* animationElementsForTarget = m_animatedElements.take(targetElement);
+    if (!animationElementsForTarget)
+        return;
+    HashSet<SVGSMILElement*>::iterator it = animationElementsForTarget->begin();
+    HashSet<SVGSMILElement*>::iterator end = animationElementsForTarget->end();
+    for (; it != end; ++it)
+        (*it)->resetTargetElement();
+    delete animationElementsForTarget;
+}
+
 // FIXME: Callers should probably use ScriptController::eventHandlerLineNumber()
 static int parserLineNumber(Document* document)
 {
index 0ed62a98461a82f8489ffc6282a31c6b4ed8bf03..4e28b0da8607ba72bebabf59ea0e26453eff82ea 100644 (file)
@@ -34,6 +34,7 @@ namespace WebCore {
 
 class Document;
 class RenderSVGResourceContainer;
+class SVGElement;
 class SVGStyledElement;
 class SVGSMILElement;
 class SVGSVGElement;
@@ -56,6 +57,10 @@ public:
     void pauseAnimations();
     void unpauseAnimations();
     bool sampleAnimationAtTime(const String& elementId, SVGSMILElement*, double time);
+    
+    void addAnimationElementToTarget(SVGSMILElement*, SVGElement*);
+    void removeAnimationElementFromTarget(SVGSMILElement*, SVGElement*);
+    void removeAllAnimationElementsFromTarget(SVGElement*);
 
     void reportWarning(const String&);
     void reportError(const String&);
@@ -65,6 +70,7 @@ public:
 private:
     Document* m_document; // weak reference
     HashSet<SVGSVGElement*> m_timeContainers; // For SVG 1.2 support this will need to be made more general.
+    HashMap<SVGElement*, HashSet<SVGSMILElement*>* > m_animatedElements;
     HashMap<AtomicString, RenderSVGResourceContainer*> m_resources;
     HashMap<AtomicString, SVGPendingElements*> m_pendingResources;
     OwnPtr<SVGResourcesCache> m_resourcesCache;
index 99134b2109229480728574d9bc10b4f1739a4624..4630ccba70d27af107cb4afc82a9a8c8ba59799c 100644 (file)
@@ -82,6 +82,7 @@ SVGElement::~SVGElement()
         delete rareData;
         rareDataMap.remove(it);
     }
+    document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
 }
 
 SVGElementRareData* SVGElement::rareSVGData() const
@@ -117,6 +118,12 @@ void SVGElement::setXmlbase(const String& value, ExceptionCode&)
     setAttribute(XMLNames::baseAttr, value);
 }
 
+void SVGElement::removedFromDocument()
+{
+    document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
+    StyledElement::removedFromDocument();
+}
+
 SVGSVGElement* SVGElement::ownerSVGElement() const
 {
     ContainerNode* n = parentOrHostNode();
@@ -371,6 +378,9 @@ void SVGElement::attributeChanged(Attribute* attr, bool preserveDecls)
     if (isSynchronizingSVGAttributes())
         return;
 
+    if (isIdAttributeName(attr->name()))
+        document()->accessSVGExtensions()->removeAllAnimationElementsFromTarget(this);
+
     // Changes to the style attribute are processed lazily (see Element::getAttribute() and related methods),
     // so we don't want changes to the style attribute to result in extra work here.
     if (attr->name() != HTMLNames::styleAttr)
index ceb0973e8b912b5a1e80eb0ae66ad6eaab15917e..380fa0e8a9291b94a94b94cbbfcf3571e3bde079 100644 (file)
@@ -118,6 +118,8 @@ protected:
     virtual void insertedIntoDocument();
     virtual void attributeChanged(Attribute*, bool preserveDecls = false);
     virtual bool childShouldCreateRenderer(Node*) const;
+    
+    virtual void removedFromDocument();
 
     SVGElementRareData* rareSVGData() const;
     SVGElementRareData* ensureRareSVGData();
index 463237845ef290816f1c74ba510f34e710c15ea3..28a1119d0ce1b6cdeb244c0bab9f99b79ebf6873 100644 (file)
@@ -61,6 +61,7 @@ void SVGHKernElement::removedFromDocument()
         if (SVGFontElement* element = static_cast<SVGFontElement*>(fontNode))
             element->invalidateGlyphCache();
     }
+    SVGElement::removedFromDocument();
 }
 
 void SVGHKernElement::buildHorizontalKerningPair(KerningPairVector& kerningPairs)
index eed13d20d55d2204f7a3c6705a4d80a78554bc45..b39cc13ac5d3d68c72f6d9646af60adab7260f6c 100644 (file)
@@ -59,6 +59,7 @@ void SVGVKernElement::removedFromDocument()
         if (SVGFontElement* element = static_cast<SVGFontElement*>(fontNode))
             element->invalidateGlyphCache();
     }
+    SVGElement::removedFromDocument();
 }
 
 void SVGVKernElement::buildVerticalKerningPair(KerningPairVector& kerningPairs)
index bcdcb7832dffddc450c04f3f65f9a8d316c50729..3bb5880564f4ed7957df15d58b7ee43f7ffb8c9f 100644 (file)
@@ -37,6 +37,7 @@
 #include "FrameView.h"
 #include "HTMLNames.h"
 #include "SMILTimeContainer.h"
+#include "SVGDocumentExtensions.h"
 #include "SVGNames.h"
 #include "SVGParserUtilities.h"
 #include "SVGSVGElement.h"
@@ -115,6 +116,7 @@ SVGSMILElement::Condition::Condition(Type type, BeginOrEnd beginOrEnd, const Str
 SVGSMILElement::SVGSMILElement(const QualifiedName& tagName, Document* doc)
     : SVGElement(tagName, doc)
     , m_attributeName(anyQName())
+    , m_targetElement(0)
     , m_conditionsConnected(false)
     , m_hasEndEventConditions(false)
     , m_intervalBegin(SMILTime::unresolved())
@@ -188,6 +190,10 @@ void SVGSMILElement::removedFromDocument()
         m_timeContainer->unschedule(this);
         m_timeContainer = 0;
     }
+    if (m_targetElement) {
+        document()->accessSVGExtensions()->removeAnimationElementFromTarget(this, m_targetElement);
+        m_targetElement = 0;
+    }
     // Calling disconnectConditions() may kill us if there are syncbase conditions.
     // OK, but we don't want to die inside the call.
     RefPtr<SVGSMILElement> keepAlive(this);
@@ -489,11 +495,17 @@ void SVGSMILElement::reschedule()
 
 SVGElement* SVGSMILElement::targetElement() const
 {
+    if (m_targetElement)
+        return m_targetElement;
+
     String href = xlinkHref();
     ContainerNode* target = href.isEmpty() ? parentNode() : document()->getElementById(SVGURIReference::getTarget(href));
-    if (target && target->isSVGElement())
-        return static_cast<SVGElement*>(target);
-    return 0;
+    if (!target || !target->isSVGElement())
+        return 0;
+    
+    m_targetElement = static_cast<SVGElement*>(target);
+    document()->accessSVGExtensions()->addAnimationElementToTarget(const_cast<SVGSMILElement*>(this), m_targetElement);
+    return m_targetElement;
 }
 
 SMILTime SVGSMILElement::elapsed() const 
index 6d7fd01fbc20ed2e47306a87a5871c12c29e4332..0f26f26db135b78b66141364d0670eac9becef11 100644 (file)
@@ -55,6 +55,7 @@ public:
     SMILTimeContainer* timeContainer() const { return m_timeContainer.get(); }
 
     SVGElement* targetElement() const;
+    void resetTargetElement() { m_targetElement = 0; }
     const QualifiedName& attributeName() const { return m_attributeName; }
 
     void beginByLinkActivation();
@@ -183,6 +184,8 @@ private:
     float calculateAnimationPercentAndRepeat(SMILTime elapsed, unsigned& repeat) const;
     SMILTime calculateNextProgressTime(SMILTime elapsed) const;
 
+    mutable SVGElement* m_targetElement;
+
     Vector<Condition> m_conditions;
     bool m_conditionsConnected;
     bool m_hasEndEventConditions;