Bug 485157: SMIL event timing, part 5 make default event base the animation target, r=dholbert, sr=roc, a=roc
authorBrian Birtles <birtles@gmail.com>
Wed, 18 Aug 2010 19:20:24 +0900
changeset 50806 fd27d7619098
parent 50805 31a2747a096b
child 50807 32b5f913f72c
push id15162
push userbbirtles@mozilla.com
push date2010-08-18 10:24 +0000
treeherdermozilla-central@ca457b5758e0 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersdholbert, roc, roc
bugs485157
milestone2.0b5pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 485157: SMIL event timing, part 5 make default event base the animation target, r=dholbert, sr=roc, a=roc
content/smil/nsSMILTimeValueSpec.cpp
content/smil/nsSMILTimeValueSpec.h
content/smil/nsSMILTimedElement.cpp
content/smil/nsSMILTimedElement.h
content/svg/content/src/nsSVGAnimationElement.cpp
content/svg/content/src/nsSVGAnimationElement.h
layout/reftests/svg/smil/event/event-target-default-1.svg
layout/reftests/svg/smil/event/event-target-default-2.svg
layout/reftests/svg/smil/event/event-target-id-change-1.svg
layout/reftests/svg/smil/event/event-target-id-change-2.svg
layout/reftests/svg/smil/event/event-target-id-change-3.svg
layout/reftests/svg/smil/event/event-target-non-svg-1.xhtml
layout/reftests/svg/smil/event/event-target-surgery-1.svg
layout/reftests/svg/smil/event/event-target-surgery-2.svg
layout/reftests/svg/smil/event/event-target-surgery-3.svg
layout/reftests/svg/smil/event/event-target-xlink-change-1.svg
layout/reftests/svg/smil/event/event-target-xlink-change-2.svg
layout/reftests/svg/smil/event/event-target-xlink-change-3.svg
layout/reftests/svg/smil/event/event-target-xlink-change-4.svg
layout/reftests/svg/smil/event/green-box-ref.xhtml
layout/reftests/svg/smil/event/reftest.list
--- a/content/smil/nsSMILTimeValueSpec.cpp
+++ b/content/smil/nsSMILTimeValueSpec.cpp
@@ -121,37 +121,41 @@ nsSMILTimeValueSpec::SetSpec(const nsASt
 
   return rv;
 }
 
 void
 nsSMILTimeValueSpec::ResolveReferences(nsIContent* aContextNode)
 {
   if (mParams.mType != nsSMILTimeValueSpecParams::SYNCBASE &&
-      mParams.mType != nsSMILTimeValueSpecParams::EVENT)
+      !IsEventBased())
     return;
 
   NS_ABORT_IF_FALSE(aContextNode,
       "null context node for resolving timing references against");
 
   // If we're not bound to the document yet, don't worry, we'll get called again
   // when that happens
   if (!aContextNode->IsInDoc())
     return;
 
   // Hold ref to the old element so that it isn't destroyed in between resetting
   // the referenced element and using the pointer to update the referenced
   // element.
   nsRefPtr<Element> oldReferencedElement = mReferencedElement.get();
 
-  // XXX Support default event targets
-  NS_ABORT_IF_FALSE(mParams.mDependentElemID, "NULL dependent element id");
-  nsString idStr;
-  mParams.mDependentElemID->ToString(idStr);
-  mReferencedElement.ResetWithID(aContextNode, idStr);
+  if (mParams.mDependentElemID) {
+    mReferencedElement.ResetWithID(aContextNode,
+        nsDependentAtomString(mParams.mDependentElemID));
+  } else if (IsEventBased()) {
+    Element* target = mOwner->GetTargetElement();
+    mReferencedElement.ResetWithElement(target);
+  } else {
+    NS_ABORT_IF_FALSE(PR_FALSE, "Syncbase element without ID");
+  }
   UpdateReferencedElement(oldReferencedElement, mReferencedElement.get());
 }
 
 PRBool
 nsSMILTimeValueSpec::IsEventBased() const
 {
   return mParams.mType == nsSMILTimeValueSpecParams::EVENT ||
          mParams.mType == nsSMILTimeValueSpecParams::REPEAT ||
@@ -175,16 +179,25 @@ nsSMILTimeValueSpec::HandleNewInterval(n
   // Create the instance time and register it with the interval
   nsRefPtr<nsSMILInstanceTime> newInstance =
     new nsSMILInstanceTime(newTime, nsSMILInstanceTime::SOURCE_SYNCBASE, this,
                            &aInterval);
   mOwner->AddInstanceTime(newInstance, mIsBegin);
 }
 
 void
+nsSMILTimeValueSpec::HandleTargetElementChange(Element* aNewTarget)
+{
+  if (!IsEventBased() || mParams.mDependentElemID)
+    return;
+
+  mReferencedElement.ResetWithElement(aNewTarget);
+}
+
+void
 nsSMILTimeValueSpec::HandleChangedInstanceTime(
     const nsSMILInstanceTime& aBaseTime,
     const nsSMILTimeContainer* aSrcContainer,
     nsSMILInstanceTime& aInstanceTimeToUpdate,
     PRBool aObjectChanged)
 {
   // If the instance time is fixed (e.g. because it's being used as the begin
   // time of an active or postactive interval) we just ignore the change.
@@ -287,17 +300,16 @@ nsSMILTimeValueSpec::GetTimedElement(Ele
 void
 nsSMILTimeValueSpec::RegisterEventListener(Element* aTarget)
 {
   NS_ABORT_IF_FALSE(mParams.mType == nsSMILTimeValueSpecParams::EVENT,
     "Attempting to register event-listener for non-event nsSMILTimeValueSpec");
   NS_ABORT_IF_FALSE(mParams.mEventSymbol,
     "Attempting to register event-listener but there is no event name");
 
-  // XXX Support default event targets
   if (!aTarget)
     return;
 
   if (!mEventListener) {
     mEventListener = new EventListener(this);
   }
 
   nsCOMPtr<nsIDOMEventGroup> sysGroup;
--- a/content/smil/nsSMILTimeValueSpec.h
+++ b/content/smil/nsSMILTimeValueSpec.h
@@ -70,16 +70,17 @@ public:
   ~nsSMILTimeValueSpec();
 
   nsresult SetSpec(const nsAString& aStringSpec, Element* aContextNode);
   void     ResolveReferences(nsIContent* aContextNode);
   PRBool   IsEventBased() const;
 
   void     HandleNewInterval(nsSMILInterval& aInterval,
                              const nsSMILTimeContainer* aSrcContainer);
+  void     HandleTargetElementChange(Element* aNewTarget);
 
   // For created nsSMILInstanceTime objects
   PRBool   DependsOnBegin() const;
   void     HandleChangedInstanceTime(const nsSMILInstanceTime& aBaseTime,
                                      const nsSMILTimeContainer* aSrcContainer,
                                      nsSMILInstanceTime& aInstanceTimeToUpdate,
                                      PRBool aObjectChanged);
   void     HandleDeletedInstanceTime(nsSMILInstanceTime& aInstanceTime);
@@ -107,16 +108,21 @@ protected:
                                           // if we're synced with the begin of
                                           // the target.
   nsSMILTimeValueSpecParams     mParams;
 
   class TimeReferenceElement : public nsReferencedElement
   {
   public:
     TimeReferenceElement(nsSMILTimeValueSpec* aOwner) : mSpec(aOwner) { }
+    void ResetWithElement(Element* aTo) {
+      nsRefPtr<Element> from = get();
+      Unlink();
+      ElementChanged(from, aTo);
+    }
 
   protected:
     virtual void ElementChanged(Element* aFrom, Element* aTo)
     {
       nsReferencedElement::ElementChanged(aFrom, aTo);
       mSpec->UpdateReferencedElement(aFrom, aTo);
     }
     virtual PRBool IsPersistent() { return PR_TRUE; }
--- a/content/smil/nsSMILTimedElement.cpp
+++ b/content/smil/nsSMILTimedElement.cpp
@@ -1117,16 +1117,30 @@ nsSMILTimedElement::BindToTree(nsIConten
   // Clear any previous milestone since it might be been processed whilst we
   // were not bound to the tree.
   mPrevRegisteredMilestone = sMaxMilestone;
 
   RegisterMilestone();
 }
 
 void
+nsSMILTimedElement::HandleTargetElementChange(Element* aNewTarget)
+{
+  PRUint32 count = mBeginSpecs.Length();
+  for (PRUint32 i = 0; i < count; ++i) {
+    mBeginSpecs[i]->HandleTargetElementChange(aNewTarget);
+  }
+
+  count = mEndSpecs.Length();
+  for (PRUint32 j = 0; j < count; ++j) {
+    mEndSpecs[j]->HandleTargetElementChange(aNewTarget);
+  }
+}
+
+void
 nsSMILTimedElement::Traverse(nsCycleCollectionTraversalCallback* aCallback)
 {
   PRUint32 count = mBeginSpecs.Length();
   for (PRUint32 i = 0; i < count; ++i) {
     nsSMILTimeValueSpec* beginSpec = mBeginSpecs[i];
     NS_ABORT_IF_FALSE(beginSpec,
         "null nsSMILTimeValueSpec in list of begin specs");
     beginSpec->Traverse(aCallback);
--- a/content/smil/nsSMILTimedElement.h
+++ b/content/smil/nsSMILTimedElement.h
@@ -74,16 +74,27 @@ public:
   void SetAnimationElement(nsISMILAnimationElement* aElement);
 
   /*
    * Returns the time container with which this timed element is associated or
    * nsnull if it is not associated with a time container.
    */
   nsSMILTimeContainer* GetTimeContainer();
 
+  /*
+   * Returns the element targeted by the animation element. Needed for
+   * registering event listeners against the appropriate element.
+   */
+  mozilla::dom::Element* GetTargetElement()
+  {
+    return mAnimationElement ?
+        mAnimationElement->GetTargetElementContent() :
+        nsnull;
+  }
+
   /**
    * Methods for supporting the nsIDOMElementTimeControl interface.
    */
 
   /*
    * Adds a new begin instance time at the current container time plus or minus
    * the specified offset.
    *
@@ -315,16 +326,22 @@ public:
    * @param aContextNode  The node which provides the necessary context for
    *                      resolving references. This is typically the element in
    *                      the host language that owns this timed element. Should
    *                      not be null.
    */
   void BindToTree(nsIContent* aContextNode);
 
   /**
+   * Called when the target of the animation has changed so that event
+   * registrations can be updated.
+   */
+  void HandleTargetElementChange(mozilla::dom::Element* aNewTarget);
+
+  /**
    * Called when the timed element has been removed from a document so that
    * references to other elements can be broken.
    */
   void DissolveReferences() { Unlink(); }
 
   // Cycle collection
   void Traverse(nsCycleCollectionTraversalCallback* aCallback);
   void Unlink();
--- a/content/svg/content/src/nsSVGAnimationElement.cpp
+++ b/content/svg/content/src/nsSVGAnimationElement.cpp
@@ -367,46 +367,55 @@ nsSVGAnimationElement::ParseAttribute(PR
       if (NS_FAILED(rv)) {
         ReportAttributeParseFailure(GetOwnerDoc(), aAttribute, aValue);
         return PR_FALSE;
       }
       return PR_TRUE;
     }
   }
 
-  PRBool returnVal =
-    nsSVGAnimationElementBase::ParseAttribute(aNamespaceID, aAttribute,
-                                              aValue, aResult);
-  if (aNamespaceID == kNameSpaceID_XLink &&
-      aAttribute == nsGkAtoms::href &&
-      IsInDoc()) {
-    // NOTE: If we fail the IsInDoc call, it's ok -- we'll update the target
-    // on next BindToTree call.
-    UpdateHrefTarget(this, aValue);
-  }
-  return returnVal;
+  return nsSVGAnimationElementBase::ParseAttribute(aNamespaceID, aAttribute,
+                                                   aValue, aResult);
+}
+
+nsresult
+nsSVGAnimationElement::AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
+                                    const nsAString* aValue, PRBool aNotify)
+{
+  nsresult rv =
+    nsSVGAnimationElementBase::AfterSetAttr(aNamespaceID, aName, aValue,
+                                            aNotify);
+
+  if (aNamespaceID != kNameSpaceID_XLink || aName != nsGkAtoms::href)
+    return rv;
+
+  if (!aValue) {
+    mHrefTarget.Unlink();
+    AnimationTargetChanged();
+  } else if (IsInDoc()) {
+    UpdateHrefTarget(this, *aValue);
+  } // else: we're not yet in a document -- we'll update the target on
+    // next BindToTree call.
+
+  return rv;
 }
 
 nsresult
 nsSVGAnimationElement::UnsetAttr(PRInt32 aNamespaceID,
                                  nsIAtom* aAttribute, PRBool aNotify)
 {
   nsresult rv = nsSVGAnimationElementBase::UnsetAttr(aNamespaceID, aAttribute,
                                                      aNotify);
   NS_ENSURE_SUCCESS(rv,rv);
 
   if (aNamespaceID == kNameSpaceID_None) {
     if (AnimationFunction().UnsetAttr(aAttribute) ||
         mTimedElement.UnsetAttr(aAttribute)) {
       AnimationNeedsResample();
     }
-  } else if (aNamespaceID == kNameSpaceID_XLink) {
-    if (aAttribute == nsGkAtoms::href) {
-      mHrefTarget.Unlink();
-    }
   }
 
   return NS_OK;
 }
 
 PRBool
 nsSVGAnimationElement::IsNodeOfType(PRUint32 aFlags) const
 {
@@ -486,9 +495,17 @@ void
 nsSVGAnimationElement::UpdateHrefTarget(nsIContent* aNodeForContext,
                                         const nsAString& aHrefStr)
 {
   nsCOMPtr<nsIURI> targetURI;
   nsCOMPtr<nsIURI> baseURI = GetBaseURI();
   nsContentUtils::NewURIWithDocumentCharset(getter_AddRefs(targetURI),
                                             aHrefStr, GetOwnerDoc(), baseURI);
   mHrefTarget.Reset(aNodeForContext, targetURI);
+  AnimationTargetChanged();
 }
+
+void
+nsSVGAnimationElement::AnimationTargetChanged()
+{
+  mTimedElement.HandleTargetElementChange(GetTargetElementContent());
+  AnimationNeedsResample();
+}
--- a/content/svg/content/src/nsSVGAnimationElement.h
+++ b/content/svg/content/src/nsSVGAnimationElement.h
@@ -79,16 +79,18 @@ public:
 
   virtual PRBool IsNodeOfType(PRUint32 aFlags) const;
 
   // nsGenericElement specializations
   virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
                                 nsAttrValue& aResult);
+  virtual nsresult AfterSetAttr(PRInt32 aNamespaceID, nsIAtom* aName,
+                                const nsAString* aValue, PRBool aNotify);
 
   // nsISMILAnimationElement interface
   virtual const Element& AsElement() const;
   virtual Element& AsElement();
   virtual const nsAttrValue* GetAnimAttr(nsIAtom* aName) const;
   virtual PRBool GetAnimAttr(nsIAtom* aAttName, nsAString& aResult) const;
   virtual PRBool HasAnimAttr(nsIAtom* aAttName) const;
   virtual Element* GetTargetElementContent();
@@ -98,28 +100,29 @@ public:
   virtual nsSMILTimeContainer* GetTimeContainer();
 
 protected:
   // nsSVGElement overrides
   PRBool IsEventName(nsIAtom* aName);
 
   void UpdateHrefTarget(nsIContent* aNodeForContext,
                         const nsAString& aHrefStr);
+  void AnimationTargetChanged();
 
   class TargetReference : public nsReferencedElement {
   public:
     TargetReference(nsSVGAnimationElement* aAnimationElement) :
       mAnimationElement(aAnimationElement) {}
   protected:
     // We need to be notified when target changes, in order to request a
     // sample (which will clear animation effects from old target and apply
-    // them to the new target).
+    // them to the new target) and update any event registrations.
     virtual void ElementChanged(Element* aFrom, Element* aTo) {
       nsReferencedElement::ElementChanged(aFrom, aTo);
-      mAnimationElement->AnimationNeedsResample();
+      mAnimationElement->AnimationTargetChanged();
     }
 
     // We need to override IsPersistent to get persistent tracking (beyond the
     // first time the target changes)
     virtual PRBool IsPersistent() { return PR_TRUE; }
   private:
     nsSVGAnimationElement* const mAnimationElement;
   };
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-default-1.svg
@@ -0,0 +1,15 @@
+<!-- Test default parent target -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <rect width="100" height="100" fill="red" id="rect">
+    <set attributeName="fill" attributeType="CSS" to="green"
+      begin="click" dur="4s"/>
+  </rect>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-default-2.svg
@@ -0,0 +1,14 @@
+<!-- Test default target with xlink:href -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <rect width="100" height="100" fill="red" id="rect"/>
+  <set xlink:href="#rect" attributeName="fill" attributeType="CSS" to="green"
+    begin="click" dur="4s"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-id-change-1.svg
@@ -0,0 +1,15 @@
+<!-- Test that changes to ID assignments are reflected in event registration -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        document.getElementById('square').setAttribute('id', 'rect');
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <rect width="100" height="100" fill="red" id="square"/>
+  <set xlink:href="#rect" attributeName="fill" attributeType="CSS" to="green"
+    begin="click" dur="4s"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-id-change-2.svg
@@ -0,0 +1,16 @@
+<!-- Test that changes to ID assignments are reflected in event registration.
+     Test for when the animation target becomes invalid. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        document.getElementById('rect').setAttribute('id', 'square');
+        click('square');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <rect width="100" height="100" fill="green" id="rect"/>
+  <set xlink:href="#rect" attributeName="fill" attributeType="CSS" to="red"
+    begin="click" dur="4s"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-id-change-3.svg
@@ -0,0 +1,16 @@
+<!-- Test that changes to ID assignments are reflected in event registration.
+     Test with end specifications. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(1);
+        document.getElementById('square').setAttribute('id', 'rect');
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <rect width="100" height="100" fill="green" id="square"/>
+  <set xlink:href="#rect" attributeName="fill" attributeType="CSS" to="red"
+    begin="0s" end="click" dur="4s"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-non-svg-1.xhtml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<html xmlns="http://www.w3.org/1999/xhtml" class="reftest-wait">
+<!--
+  Test for non-SVG event bases. This isn't strictly required but we at very
+  least we want to make sure our behaviour is defined.
+ -->
+<head>
+<script src="event-util.js" type="text/javascript"></script>
+<script>
+function snapshot() {
+  var svg = document.getElementById("svg");
+  svg.pauseAnimations();
+  svg.setCurrentTime(0);
+  click('a');
+  var a = document.getElementById('a');
+  a.style.display = 'none';
+  window.setTimeout(doSnapshot, 10);
+}
+
+function doSnapshot() {
+  var svg = document.getElementById("svg");
+  svg.pauseAnimations();
+  svg.setCurrentTime(2);
+  document.documentElement.removeAttribute("class");
+}
+</script>
+</head>
+<body onload="snapshot()">
+<a href="#" id="a">Play!</a>
+<svg xmlns="http://www.w3.org/2000/svg" width="120px" height="120px" id="svg">
+  <rect width="100" height="100" fill="red">
+    <set attributeName="fill" attributeType="CSS" to="green"
+      begin="a.click" dur="4s"/>
+  </rect>
+</svg>
+</body>
+</html>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-surgery-1.svg
@@ -0,0 +1,17 @@
+<!-- Test that animations are unregistered when removed -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        document.getElementById('rect').removeChild(
+          document.getElementById('anim'));
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <rect width="100" height="100" fill="green" id="rect">
+    <set attributeName="fill" attributeType="CSS"
+      to="red" begin="click" dur="4s" id="anim"/>
+  </rect>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-surgery-2.svg
@@ -0,0 +1,15 @@
+<!-- Test that animations are unregistered when removed -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        document.documentElement.removeChild(document.getElementById('anim'));
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <rect width="100" height="100" fill="green" id="rect"/>
+  <set xlink:href="#rect" attributeName="fill" attributeType="CSS"
+    to="red" begin="click" dur="4s" id="anim"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-surgery-3.svg
@@ -0,0 +1,32 @@
+<!-- Test that event registration is updated when reparenting -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="reparent()">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <script type="text/javascript">
+    function reparent()
+    {
+      var svg = document.documentElement;
+      svg.pauseAnimations();
+      svg.setCurrentTime(0);
+      var circle    = document.getElementById('circle');
+      var rect      = document.getElementById('rect');
+      var animmove  = document.getElementById('anim-move');
+      var animcolor = document.getElementById('anim-color');
+      circle.appendChild(animmove);
+      rect.appendChild(animcolor);
+      click('rect');
+      delayedSnapshot(2);
+    }
+  </script>
+  <circle id="circle" r="10">
+    <set attributeName="fill" attributeType="CSS"
+      to="green" begin="click" dur="4s" id="anim-color"/>
+  </circle>
+  <rect width="100" height="100" fill="red" id="rect">
+    <animateTransform attributeName="transform" type="translate"
+      values="100; 100" calcMode="discrete"
+      begin="click" dur="4s" id="anim-move"/>
+  </rect>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-xlink-change-1.svg
@@ -0,0 +1,17 @@
+<!-- Test that changes to animation targets update event registration. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        document.getElementById('anim').setAttributeNS(
+          'http://www.w3.org/1999/xlink', 'href', '#rect');
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <circle id="circle" r="10"/>
+  <rect width="100" height="100" fill="red" id="rect"/>
+  <set xlink:href="#circle" attributeName="fill" attributeType="CSS" to="green"
+    begin="click" dur="4s" id="anim"/>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-xlink-change-2.svg
@@ -0,0 +1,18 @@
+<!-- Test that changes to animation targets update event registration. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        document.getElementById('anim').setAttributeNS(
+          'http://www.w3.org/1999/xlink', 'href', '#circle');
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <circle id="circle" r="10"/>
+  <rect width="100" height="100" fill="green" id="rect">
+    <set attributeName="fill" attributeType="CSS"
+      to="red" begin="click" dur="4s" id="anim"/>
+  </rect>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-xlink-change-3.svg
@@ -0,0 +1,18 @@
+<!-- Test that changes to animation targets update event registration. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        document.getElementById('anim').removeAttributeNS(
+          'http://www.w3.org/1999/xlink', 'href');
+        click('rect');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <circle id="circle" r="10"/>
+  <rect width="100" height="100" fill="red" id="rect">
+    <set xlink:href="#circle" attributeName="fill" attributeType="CSS"
+      to="green" begin="click" dur="4s" id="anim"/>
+  </rect>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/event-target-xlink-change-4.svg
@@ -0,0 +1,18 @@
+<!-- Test that changes to animation targets update event registration. -->
+<svg xmlns="http://www.w3.org/2000/svg"
+     xmlns:xlink="http://www.w3.org/1999/xlink"
+     class="reftest-wait"
+     onload="
+        document.documentElement.pauseAnimations();
+        document.documentElement.setCurrentTime(0);
+        document.getElementById('anim').removeAttributeNS(
+          'http://www.w3.org/1999/xlink', 'href');
+        click('circle');
+        delayedSnapshot(2)">
+  <script xlink:href="event-util.js" type="text/javascript"/>
+  <circle id="circle" r="10"/>
+  <rect width="100" height="100" fill="green" id="rect">
+    <set xlink:href="#circle" attributeName="fill" attributeType="CSS"
+      to="red" begin="click" dur="4s" id="anim"/>
+  </rect>
+</svg>
new file mode 100644
--- /dev/null
+++ b/layout/reftests/svg/smil/event/green-box-ref.xhtml
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<html xmlns="http://www.w3.org/1999/xhtml">
+<body>
+<svg xmlns="http://www.w3.org/2000/svg" width="120px" height="120px">
+  <rect width="100" height="100" fill="green"/>
+</svg>
+</body>
+</html>
--- a/layout/reftests/svg/smil/event/reftest.list
+++ b/layout/reftests/svg/smil/event/reftest.list
@@ -9,8 +9,21 @@
 == event-begin-load-1.svg green-box-ref.svg
 == event-bubble-1.svg green-box-ref.svg
 == event-custom-1.svg green-box-ref.svg
 == event-end-1.svg green-box-ref.svg
 == event-end-2.svg green-box-ref.svg
 == event-end-open-1.svg green-box-ref.svg
 == event-preventDefault-1.svg green-box-ref.svg
 == event-seek-1.svg green-box-ref.svg
+== event-target-default-1.svg green-box-ref.svg
+== event-target-default-2.svg green-box-ref.svg
+== event-target-id-change-1.svg green-box-ref.svg
+== event-target-id-change-2.svg green-box-ref.svg
+== event-target-id-change-3.svg green-box-ref.svg
+== event-target-xlink-change-1.svg green-box-ref.svg
+== event-target-xlink-change-2.svg green-box-ref.svg
+== event-target-xlink-change-3.svg green-box-ref.svg
+== event-target-xlink-change-4.svg green-box-ref.svg
+== event-target-surgery-1.svg green-box-ref.svg
+== event-target-surgery-2.svg green-box-ref.svg
+== event-target-surgery-3.svg green-box-ref.svg
+== event-target-non-svg-1.xhtml green-box-ref.xhtml