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
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