[SVG] Handle endEvent for svg animations

Handled endEvent for svg animations according to http://www.w3.org/TR/SMIL3/smil-timing.html#q135

R=pdr@chromium.org, schenney@chromium.org, dschulze@chromium.org, fmalita@chromium.org
BUG=269648

Review URL: https://chromiumcodereview.appspot.com/22385010

git-svn-id: svn://svn.chromium.org/blink/trunk@157012 bbb929c8-8fbe-4397-9dbb-9b2b20218538
diff --git a/LayoutTests/svg/animations/end-event-expected.svg b/LayoutTests/svg/animations/end-event-expected.svg
new file mode 100755
index 0000000..8830766
--- /dev/null
+++ b/LayoutTests/svg/animations/end-event-expected.svg
@@ -0,0 +1,5 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <rect x="100" y="0" width="50" height="50" fill="green" />
+</svg>
diff --git a/LayoutTests/svg/animations/end-event.svg b/LayoutTests/svg/animations/end-event.svg
new file mode 100755
index 0000000..5f322f3
--- /dev/null
+++ b/LayoutTests/svg/animations/end-event.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1 Tiny//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11-tiny.dtd">
+<svg onload="loaded()" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+  <rect x="0" y="0" width="50" height="50" fill="green">
+    <animate id="anim" attributeName="visibility" to="visible" begin="0s" end="2s" />
+    <set attributeName="x" to="100" begin="anim.endEvent" />
+  </rect>
+  <script>
+    if (window.testRunner)
+        testRunner.waitUntilDone();
+
+    function loaded() {
+        document.documentElement.setCurrentTime(2);
+
+        if (window.testRunner)
+            setTimeout(function() {testRunner.notifyDone();}, 100);
+    }
+  </script>
+</svg>
diff --git a/Source/core/svg/animation/SMILTimeContainer.cpp b/Source/core/svg/animation/SMILTimeContainer.cpp
index 5b6ccc9..2a5e8d8 100644
--- a/Source/core/svg/animation/SMILTimeContainer.cpp
+++ b/Source/core/svg/animation/SMILTimeContainer.cpp
@@ -273,6 +273,16 @@
     GroupedAnimationsMap::iterator end = m_scheduledAnimations.end();
     for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) {
         AnimationsVector* scheduled = it->value.get();
+        unsigned size = scheduled->size();
+        for (unsigned n = 0; n < size; n++) {
+            SVGSMILElement* animation = scheduled->at(n);
+            if (!animation->hasConditionsConnected())
+                animation->connectConditions();
+        }
+    }
+
+    for (GroupedAnimationsMap::iterator it = m_scheduledAnimations.begin(); it != end; ++it) {
+        AnimationsVector* scheduled = it->value.get();
 
         // Sort according to priority. Elements with later begin time have higher priority.
         // In case of a tie, document order decides.
diff --git a/Source/core/svg/animation/SVGSMILElement.cpp b/Source/core/svg/animation/SVGSMILElement.cpp
index c229f95..c79205e 100644
--- a/Source/core/svg/animation/SVGSMILElement.cpp
+++ b/Source/core/svg/animation/SVGSMILElement.cpp
@@ -31,6 +31,7 @@
 #include "bindings/v8/ExceptionStatePlaceholder.h"
 #include "core/dom/Document.h"
 #include "core/dom/EventListener.h"
+#include "core/dom/EventSender.h"
 #include "core/platform/FloatConversion.h"
 #include "core/svg/SVGDocumentExtensions.h"
 #include "core/svg/SVGSVGElement.h"
@@ -44,6 +45,12 @@
 
 namespace WebCore {
 
+static SMILEventSender& smilEndEventSender()
+{
+    DEFINE_STATIC_LOCAL(SMILEventSender, sender, ("endEvent"));
+    return sender;
+}
+
 // This is used for duration type time values that can't be negative.
 static const double invalidCachedTime = -1.;
 
@@ -133,6 +140,7 @@
 SVGSMILElement::~SVGSMILElement()
 {
     clearResourceReferences();
+    smilEndEventSender().cancelEvent(this);
     disconnectConditions();
     if (m_timeContainer && m_targetElement && hasValidAttributeName())
         m_timeContainer->unschedule(this, m_targetElement, m_attributeName);
@@ -1051,9 +1059,6 @@
     ASSERT(m_timeContainer);
     ASSERT(m_isWaitingForFirstInterval || m_intervalBegin.isFinite());
 
-    if (!m_conditionsConnected)
-        connectConditions();
-
     if (!m_intervalBegin.isFinite()) {
         ASSERT(m_activeState == Inactive);
         m_nextProgressTime = SMILTime::unresolved();
@@ -1110,11 +1115,18 @@
     }
 
     if (oldActiveState == Active && m_activeState != Active) {
+        smilEndEventSender().dispatchEventSoon(this);
         endedActiveInterval();
         if (m_activeState != Frozen && this == resultElement)
             clearAnimatedType(m_targetElement);
     }
 
+    // Triggering all the pending events if the animation timeline is changed.
+    if (seekToTime) {
+        if (m_activeState == Inactive || m_activeState == Frozen)
+            smilEndEventSender().dispatchEventSoon(this);
+    }
+
     m_nextProgressTime = calculateNextProgressTime(elapsed);
     return animationIsContributing;
 }
@@ -1191,4 +1203,11 @@
     clearTimesWithDynamicOrigins(m_endTimes);
 }
 
+void SVGSMILElement::dispatchPendingEvent(SMILEventSender* eventSender)
+{
+    ASSERT(eventSender == &smilEndEventSender());
+    const AtomicString& eventType = eventSender->eventType();
+    dispatchEvent(Event::create(eventType));
+}
+
 }
diff --git a/Source/core/svg/animation/SVGSMILElement.h b/Source/core/svg/animation/SVGSMILElement.h
index 5d8c403..c6cbd1a 100644
--- a/Source/core/svg/animation/SVGSMILElement.h
+++ b/Source/core/svg/animation/SVGSMILElement.h
@@ -34,6 +34,10 @@
 
 class ConditionEventListener;
 class SMILTimeContainer;
+class SVGSMILElement;
+
+template<typename T> class EventSender;
+typedef EventSender<SVGSMILElement> SMILEventSender;
 
 // This class implements SMIL interval timing model as needed for SVG animation.
 class SVGSMILElement : public SVGElement {
@@ -109,6 +113,11 @@
     virtual void clearAnimatedType(SVGElement* targetElement) = 0;
     virtual void applyResultsToTarget() = 0;
 
+    void connectConditions();
+    bool hasConditionsConnected() const { return m_conditionsConnected; }
+
+    void dispatchPendingEvent(SMILEventSender*);
+
 protected:
     void addBeginTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin = SMILTimeWithOrigin::ParserOrigin);
     void addEndTime(SMILTime eventTime, SMILTime endTime, SMILTimeWithOrigin::Origin = SMILTimeWithOrigin::ParserOrigin);
@@ -167,7 +176,6 @@
     void parseBeginOrEnd(const String&, BeginOrEnd beginOrEnd);
     Element* eventBaseFor(const Condition&);
 
-    void connectConditions();
     void disconnectConditions();
 
     // Event base timing