summaryrefslogtreecommitdiffstats
path: root/dom/animation/test/css-transitions
diff options
context:
space:
mode:
Diffstat (limited to 'dom/animation/test/css-transitions')
-rw-r--r--dom/animation/test/css-transitions/file_animation-cancel.html165
-rw-r--r--dom/animation/test/css-transitions/file_animation-computed-timing.html315
-rw-r--r--dom/animation/test/css-transitions/file_animation-currenttime.html307
-rw-r--r--dom/animation/test/css-transitions/file_animation-finished.html61
-rw-r--r--dom/animation/test/css-transitions/file_animation-pausing.html50
-rw-r--r--dom/animation/test/css-transitions/file_animation-ready.html96
-rw-r--r--dom/animation/test/css-transitions/file_animation-starttime.html284
-rw-r--r--dom/animation/test/css-transitions/file_csstransition-events.html223
-rw-r--r--dom/animation/test/css-transitions/file_csstransition-transitionproperty.html24
-rw-r--r--dom/animation/test/css-transitions/file_document-get-animations.html93
-rw-r--r--dom/animation/test/css-transitions/file_effect-target.html66
-rw-r--r--dom/animation/test/css-transitions/file_element-get-animations.html147
-rw-r--r--dom/animation/test/css-transitions/file_keyframeeffect-getkeyframes.html95
-rw-r--r--dom/animation/test/css-transitions/file_pseudoElement-get-animations.html45
-rw-r--r--dom/animation/test/css-transitions/file_setting-effect.html91
-rw-r--r--dom/animation/test/css-transitions/test_animation-cancel.html14
-rw-r--r--dom/animation/test/css-transitions/test_animation-computed-timing.html16
-rw-r--r--dom/animation/test/css-transitions/test_animation-currenttime.html14
-rw-r--r--dom/animation/test/css-transitions/test_animation-finished.html14
-rw-r--r--dom/animation/test/css-transitions/test_animation-pausing.html14
-rw-r--r--dom/animation/test/css-transitions/test_animation-ready.html14
-rw-r--r--dom/animation/test/css-transitions/test_animation-starttime.html14
-rw-r--r--dom/animation/test/css-transitions/test_csstransition-events.html14
-rw-r--r--dom/animation/test/css-transitions/test_csstransition-transitionproperty.html14
-rw-r--r--dom/animation/test/css-transitions/test_document-get-animations.html15
-rw-r--r--dom/animation/test/css-transitions/test_effect-target.html14
-rw-r--r--dom/animation/test/css-transitions/test_element-get-animations.html14
-rw-r--r--dom/animation/test/css-transitions/test_keyframeeffect-getkeyframes.html14
-rw-r--r--dom/animation/test/css-transitions/test_pseudoElement-get-animations.html14
-rw-r--r--dom/animation/test/css-transitions/test_setting-effect.html14
30 files changed, 2275 insertions, 0 deletions
diff --git a/dom/animation/test/css-transitions/file_animation-cancel.html b/dom/animation/test/css-transitions/file_animation-cancel.html
new file mode 100644
index 000000000..6094b383f
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_animation-cancel.html
@@ -0,0 +1,165 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+ flushComputedStyle(div);
+
+ var animation = div.getAnimations()[0];
+ return animation.ready.then(waitForFrame).then(function() {
+ assert_not_equals(getComputedStyle(div).marginLeft, '1000px',
+ 'transform style is animated before cancelling');
+ animation.cancel();
+ assert_equals(getComputedStyle(div).marginLeft, div.style.marginLeft,
+ 'transform style is no longer animated after cancelling');
+ });
+}, 'Animated style is cleared after cancelling a running CSS transition');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+ flushComputedStyle(div);
+
+ div.addEventListener('transitionend', function() {
+ assert_unreached('Got unexpected end event on cancelled transition');
+ });
+
+ var animation = div.getAnimations()[0];
+ return animation.ready.then(function() {
+ // Seek to just before the end then cancel
+ animation.currentTime = 99.9 * 1000;
+ animation.cancel();
+
+ // Then wait a couple of frames and check that no event was dispatched
+ return waitForAnimationFrames(2);
+ });
+}, 'Cancelled CSS transitions do not dispatch events');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+ flushComputedStyle(div);
+
+ var animation = div.getAnimations()[0];
+ return animation.ready.then(function() {
+ animation.cancel();
+ assert_equals(getComputedStyle(div).marginLeft, '1000px',
+ 'margin-left style is not animated after cancelling');
+ animation.play();
+ assert_equals(getComputedStyle(div).marginLeft, '0px',
+ 'margin-left style is animated after re-starting transition');
+ return animation.ready;
+ }).then(function() {
+ assert_equals(animation.playState, 'running',
+ 'Transition succeeds in running after being re-started');
+ });
+}, 'After cancelling a transition, it can still be re-used');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+ flushComputedStyle(div);
+
+ var animation = div.getAnimations()[0];
+ return animation.ready.then(function() {
+ animation.finish();
+ animation.cancel();
+ assert_equals(getComputedStyle(div).marginLeft, '1000px',
+ 'margin-left style is not animated after cancelling');
+ animation.play();
+ assert_equals(getComputedStyle(div).marginLeft, '0px',
+ 'margin-left style is animated after re-starting transition');
+ return animation.ready;
+ }).then(function() {
+ assert_equals(animation.playState, 'running',
+ 'Transition succeeds in running after being re-started');
+ });
+}, 'After cancelling a finished transition, it can still be re-used');
+
+test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+ flushComputedStyle(div);
+
+ var animation = div.getAnimations()[0];
+ animation.cancel();
+ assert_equals(getComputedStyle(div).marginLeft, '1000px',
+ 'margin-left style is not animated after cancelling');
+
+ // Trigger a change to a transition property and check that this
+ // doesn't cause the animation to become live again
+ div.style.transitionDuration = '200s';
+ flushComputedStyle(div);
+ assert_equals(getComputedStyle(div).marginLeft, '1000px',
+ 'margin-left style is still not animated after updating'
+ + ' transition-duration');
+ assert_equals(animation.playState, 'idle',
+ 'Transition is still idle after updating transition-duration');
+}, 'After cancelling a transition, updating transition properties doesn\'t make'
+ + ' it live again');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+ flushComputedStyle(div);
+
+ var animation = div.getAnimations()[0];
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'running');
+ div.style.display = 'none';
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(animation.playState, 'idle');
+ assert_equals(getComputedStyle(div).marginLeft, '1000px');
+ });
+}, 'Setting display:none on an element cancels its transitions');
+
+promise_test(function(t) {
+ var parentDiv = addDiv(t);
+ var childDiv = document.createElement('div');
+ parentDiv.appendChild(childDiv);
+ childDiv.setAttribute('style', 'margin-left: 0px');
+
+ flushComputedStyle(childDiv);
+
+ childDiv.style.transition = 'margin-left 100s';
+ childDiv.style.marginLeft = '1000px';
+ flushComputedStyle(childDiv);
+
+ var animation = childDiv.getAnimations()[0];
+ return animation.ready.then(function() {
+ assert_equals(animation.playState, 'running');
+ parentDiv.style.display = 'none';
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(animation.playState, 'idle');
+ assert_equals(getComputedStyle(childDiv).marginLeft, '1000px');
+ });
+}, 'Setting display:none cancels transitions on a child element');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_animation-computed-timing.html b/dom/animation/test/css-transitions/file_animation-computed-timing.html
new file mode 100644
index 000000000..2dac82d75
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_animation-computed-timing.html
@@ -0,0 +1,315 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+
+.animated-div {
+ margin-left: 100px;
+}
+
+</style>
+<body>
+<script>
+
+'use strict';
+
+// --------------------
+// delay
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().delay, 0,
+ 'Initial value of delay');
+}, 'delay of a new tranisition');
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().delay, 10000,
+ 'Initial value of delay');
+}, 'Positive delay of a new transition');
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s -5s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().delay, -5000,
+ 'Initial value of delay');
+}, 'Negative delay of a new transition');
+
+
+// --------------------
+// endDelay
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().endDelay, 0,
+ 'Initial value of endDelay');
+}, 'endDelay of a new transition');
+
+
+// --------------------
+// fill
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().fill, 'backwards',
+ 'Fill backwards');
+}, 'fill of a new transition');
+
+
+// --------------------
+// iterationStart
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().iterationStart, 0,
+ 'Initial value of iterationStart');
+}, 'iterationStart of a new transition');
+
+
+// --------------------
+// iterations
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().iterations, 1,
+ 'Initial value of iterations');
+}, 'iterations of a new transition');
+
+
+// --------------------
+// duration
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().duration, 10000,
+ 'Initial value of duration');
+}, 'duration of a new transition');
+
+
+// --------------------
+// direction
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().direction, 'normal',
+ 'Initial value of direction');
+}, 'direction of a new transition');
+
+
+// --------------------
+// easing
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().easing, 'linear',
+ 'Initial value of easing');
+}, 'easing of a new transition');
+
+
+// ------------------------------
+// endTime
+// = max(start delay + active duration + end delay, 0)
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 100s -5s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ var answer = 100000 - 5000; // ms
+ assert_equals(effect.getComputedTiming().endTime, answer,
+ 'Initial value of endTime');
+}, 'endTime of a new transition');
+
+
+// --------------------
+// activeDuration
+// = iteration duration * iteration count(==1)
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 100s -5s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().activeDuration, 100000,
+ 'Initial value of activeDuration');
+}, 'activeDuration of a new transition');
+
+
+// --------------------
+// localTime
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 100s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().localTime, 0,
+ 'Initial value of localTime');
+}, 'localTime of a new transition');
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 100s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var anim = div.getAnimations()[0];
+ anim.currentTime = 5000;
+ assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+ 'current localTime after setting currentTime');
+}, 'localTime is always equal to currentTime');
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 100s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var anim = div.getAnimations()[0];
+ anim.playbackRate = 2; // 2 times faster
+
+ anim.ready.then(t.step_func(function() {
+ assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+ 'localTime is equal to currentTime');
+ return waitForFrame();
+ })).then(t.step_func_done(function() {
+ assert_equals(anim.effect.getComputedTiming().localTime, anim.currentTime,
+ 'localTime is equal to currentTime');
+ }));
+}, 'localTime reflects playbackRate immediately');
+
+
+// --------------------
+// progress
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10.5s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().progress, 0.0,
+ 'Initial value of progress');
+}, 'progress of a new transition');
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10.5s 2s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().progress, 0.0,
+ 'Initial value of progress');
+}, 'progress of a new transition with positive delay in before phase');
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10.5s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var anim = div.getAnimations()[0];
+ anim.finish()
+ assert_equals(anim.effect.getComputedTiming().progress, null,
+ 'finished progress');
+}, 'progress of a finished transition');
+
+
+// --------------------
+// currentIteration
+// --------------------
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().currentIteration, 0,
+ 'Initial value of currentIteration');
+}, 'currentIteration of a new transition');
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s 2s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var effect = div.getAnimations()[0].effect;
+ assert_equals(effect.getComputedTiming().currentIteration, 0,
+ 'Initial value of currentIteration');
+}, 'currentIteration of a new transition with positive delay in before phase');
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ div.style.transition = 'margin-left 10s';
+ flushComputedStyle(div);
+ div.style.marginLeft = '10px';
+
+ var anim = div.getAnimations()[0];
+ anim.finish();
+ assert_equals(anim.effect.getComputedTiming().currentIteration, null,
+ 'finished currentIteration');
+}, 'currentIteration of a finished transition');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_animation-currenttime.html b/dom/animation/test/css-transitions/file_animation-currenttime.html
new file mode 100644
index 000000000..2a0f105d2
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_animation-currenttime.html
@@ -0,0 +1,307 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8>
+ <title>Tests for the effect of setting a CSS transition's
+ Animation.currentTime</title>
+ <style>
+
+.animated-div {
+ margin-left: 100px;
+ transition: margin-left 1000s linear 1000s;
+}
+
+ </style>
+ <script src="../testcommon.js"></script>
+ </head>
+ <body>
+ <script type="text/javascript">
+
+'use strict';
+
+// TODO: Once the computedTiming property is implemented, add checks to the
+// checker helpers to ensure that computedTiming's properties are updated as
+// expected.
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
+
+
+const ANIM_DELAY_MS = 1000000; // 1000s
+const ANIM_DUR_MS = 1000000; // 1000s
+
+/**
+ * These helpers get the value that the currentTime needs to be set to, to put
+ * an animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into
+ * the middle of various phases or points through the active duration.
+ */
+function currentTimeForBeforePhase() {
+ return ANIM_DELAY_MS / 2;
+}
+function currentTimeForActivePhase() {
+ return ANIM_DELAY_MS + ANIM_DUR_MS / 2;
+}
+function currentTimeForAfterPhase() {
+ return ANIM_DELAY_MS + ANIM_DUR_MS + ANIM_DELAY_MS / 2;
+}
+function currentTimeForStartOfActiveInterval() {
+ return ANIM_DELAY_MS;
+}
+function currentTimeForFiftyPercentThroughActiveInterval() {
+ return ANIM_DELAY_MS + ANIM_DUR_MS * 0.5;
+}
+function currentTimeForEndOfActiveInterval() {
+ return ANIM_DELAY_MS + ANIM_DUR_MS;
+}
+
+
+// Expected computed 'margin-left' values at points during the active interval:
+// When we assert_between_inclusive using these values we could in theory cause
+// intermittent failure due to very long delays between paints, but since the
+// active duration is 1000s long, a delay would need to be around 100s to cause
+// that. If that's happening then there are likely other issues that should be
+// fixed, so a failure to make us look into that seems like a good thing.
+const INITIAL_POSITION = 100;
+const TEN_PCT_POSITION = 110;
+const FIFTY_PCT_POSITION = 150;
+const END_POSITION = 200;
+
+
+// The terms used for the naming of the following helper functions refer to
+// terms used in the Web Animations specification for specific phases of an
+// animation. The terms can be found here:
+//
+// http://w3c.github.io/web-animations/#animation-effect-phases-and-states
+
+// Called when currentTime is set to zero (the beginning of the start delay).
+function checkStateOnSettingCurrentTimeToZero(animation)
+{
+ // We don't test animation.currentTime since our caller just set it.
+
+ assert_equals(animation.playState, 'running',
+ 'Animation.playState should be "running" at the start of ' +
+ 'the start delay');
+
+ assert_equals(animation.effect.target.style.animationPlayState, 'running',
+ 'Animation.effect.target.style.animationPlayState should be ' +
+ '"running" at the start of the start delay');
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_equals(marginLeft, UNANIMATED_POSITION,
+ 'the computed value of margin-left should be unaffected ' +
+ 'at the beginning of the start delay');
+}
+
+// Called when the ready Promise's callbacks should happen
+function checkStateOnReadyPromiseResolved(animation)
+{
+ // the 0.0001 here is for rounding error
+ assert_less_than_equal(animation.currentTime,
+ animation.timeline.currentTime - animation.startTime + 0.0001,
+ 'Animation.currentTime should be less than the local time ' +
+ 'equivalent of the timeline\'s currentTime on the first paint tick ' +
+ 'after animation creation');
+
+ assert_equals(animation.playState, 'running',
+ 'Animation.playState should be "running" on the first paint ' +
+ 'tick after animation creation');
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_equals(marginLeft, INITIAL_POSITION,
+ 'the computed value of margin-left should be unaffected ' +
+ 'by an animation with a delay on ready Promise resolve');
+}
+
+// Called when currentTime is set to the time the active interval starts.
+function checkStateAtActiveIntervalStartTime(animation)
+{
+ // We don't test animation.currentTime since our caller just set it.
+
+ assert_equals(animation.playState, 'running',
+ 'Animation.playState should be "running" at the start of ' +
+ 'the active interval');
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
+ 'the computed value of margin-left should be close to the value at the ' +
+ 'beginning of the animation');
+}
+
+function checkStateAtFiftyPctOfActiveInterval(animation)
+{
+ // We don't test animation.currentTime since our caller just set it.
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_equals(marginLeft, FIFTY_PCT_POSITION,
+ 'the computed value of margin-left should be half way through the ' +
+ 'animation at the midpoint of the active interval');
+}
+
+// Called when currentTime is set to the time the active interval ends.
+function checkStateAtActiveIntervalEndTime(animation)
+{
+ // We don't test animation.currentTime since our caller just set it.
+
+ assert_equals(animation.playState, 'finished',
+ 'Animation.playState should be "finished" at the end of ' +
+ 'the active interval');
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_equals(marginLeft, END_POSITION,
+ 'the computed value of margin-left should be the final transitioned-to ' +
+ 'value at the end of the active duration');
+}
+
+test(function(t)
+{
+ var div = addDiv(t, {'class': 'animated-div'});
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+ assert_equals(animation.currentTime, 0, 'currentTime should be zero');
+}, 'currentTime of a newly created transition is zero');
+
+
+test(function(t)
+{
+ var div = addDiv(t, {'class': 'animated-div'});
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ // So that animation is running instead of paused when we set currentTime:
+ animation.startTime = animation.timeline.currentTime;
+
+ animation.currentTime = 10;
+ assert_equals(animation.currentTime, 10,
+ 'Check setting of currentTime actually works');
+}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
+ 'currentTime');
+
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ var eventWatcher = new EventWatcher(t, div, 'transitionend');
+
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ animation.ready.then(t.step_func(function() {
+ checkStateOnReadyPromiseResolved(animation);
+
+ animation.currentTime = currentTimeForStartOfActiveInterval();
+ checkStateAtActiveIntervalStartTime(animation);
+
+ animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
+ checkStateAtFiftyPctOfActiveInterval(animation);
+
+ animation.currentTime = currentTimeForEndOfActiveInterval();
+ return eventWatcher.wait_for('transitionend');
+ })).then(t.step_func(function() {
+ checkStateAtActiveIntervalEndTime(animation);
+ })).catch(t.step_func(function(reason) {
+ assert_unreached(reason);
+ })).then(function() {
+ t.done();
+ });
+}, 'Skipping forward through transition');
+
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ var eventWatcher = new EventWatcher(t, div, 'transitionend');
+
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ // Unlike in the case of CSS animations, we cannot skip to the end and skip
+ // backwards since when we reach the end the transition effect is removed and
+ // changes to the Animation object no longer affect the element. For
+ // this reason we only skip forwards as far as the 50% through point.
+
+ animation.ready.then(t.step_func(function() {
+ animation.currentTime = currentTimeForFiftyPercentThroughActiveInterval();
+ checkStateAtFiftyPctOfActiveInterval(animation);
+
+ animation.currentTime = currentTimeForStartOfActiveInterval();
+
+ // Despite going backwards from being in the active interval to being
+ // before it, we now expect a 'transitionend' event because the transition
+ // should go from being active to inactive.
+ //
+ // Calling checkStateAtActiveIntervalStartTime will check computed style,
+ // causing computed style to be updated and the 'transitionend' event to
+ // be dispatched synchronously. We need to call wait_for first
+ // otherwise eventWatcher will assert that the event was unexpected.
+ eventWatcher.wait_for('transitionend').then(function() {
+ t.done();
+ });
+ checkStateAtActiveIntervalStartTime(animation);
+ }));
+}, 'Skipping backwards through transition');
+
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ animation.ready.then(t.step_func(function() {
+ var exception;
+ try {
+ animation.currentTime = null;
+ } catch (e) {
+ exception = e;
+ }
+ assert_equals(exception.name, 'TypeError',
+ 'Expect TypeError exception on trying to set ' +
+ 'Animation.currentTime to null');
+ })).catch(t.step_func(function(reason) {
+ assert_unreached(reason);
+ })).then(function() {
+ t.done();
+ });
+}, 'Setting currentTime to null');
+
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+ var pauseTime;
+
+ animation.ready.then(t.step_func(function() {
+ assert_not_equals(animation.currentTime, null,
+ 'Animation.currentTime not null on ready Promise resolve');
+ animation.pause();
+ return animation.ready;
+ })).then(t.step_func(function() {
+ pauseTime = animation.currentTime;
+ return waitForFrame();
+ })).then(t.step_func(function() {
+ assert_equals(animation.currentTime, pauseTime,
+ 'Animation.currentTime is unchanged after pausing');
+ })).catch(t.step_func(function(reason) {
+ assert_unreached(reason);
+ })).then(function() {
+ t.done();
+ });
+}, 'Animation.currentTime after pausing');
+
+done();
+ </script>
+ </body>
+</html>
diff --git a/dom/animation/test/css-transitions/file_animation-finished.html b/dom/animation/test/css-transitions/file_animation-finished.html
new file mode 100644
index 000000000..2f6bcf47d
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_animation-finished.html
@@ -0,0 +1,61 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+
+.animated-div {
+ margin-left: 100px;
+ transition: margin-left 1000s linear 1000s;
+}
+
+</style>
+<body>
+<script>
+
+'use strict';
+
+const ANIM_DELAY_MS = 1000000; // 1000s
+const ANIM_DUR_MS = 1000000; // 1000s
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ animation.finish();
+
+ animation.finished.then(t.step_func(function() {
+ animation.play();
+ assert_equals(animation.currentTime, 0,
+ 'Replaying a finished transition should reset its ' +
+ 'currentTime');
+ t.done();
+ }));
+}, 'Test restarting a finished transition');
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ animation.ready.then(function() {
+ animation.playbackRate = -1;
+ return animation.finished;
+ }).then(t.step_func(function() {
+ animation.play();
+ // FIXME: once animation.effect.computedTiming.endTime is available (bug
+ // 1108055) we should use that here.
+ assert_equals(animation.currentTime, ANIM_DELAY_MS + ANIM_DUR_MS,
+ 'Replaying a finished reversed transition should reset ' +
+ 'its currentTime to the end of the effect');
+ t.done();
+ }));
+}, 'Test restarting a reversed finished transition');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_animation-pausing.html b/dom/animation/test/css-transitions/file_animation-pausing.html
new file mode 100644
index 000000000..b2f2d4618
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_animation-pausing.html
@@ -0,0 +1,50 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+function getMarginLeft(cs) {
+ return parseFloat(cs.marginLeft);
+}
+
+async_test(function(t) {
+ var div = addDiv(t);
+ var cs = window.getComputedStyle(div);
+
+ div.style.marginLeft = '0px';
+ cs.marginLeft; // Flush style to set up transition start point
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '10000px';
+ cs.marginLeft;
+
+ var animation = div.getAnimations()[0];
+ assert_equals(getMarginLeft(cs), 0,
+ 'Initial value of margin-left is zero');
+ var previousAnimVal = getMarginLeft(cs);
+
+ animation.ready.then(waitForFrame).then(t.step_func(function() {
+ assert_true(getMarginLeft(cs) > previousAnimVal,
+ 'margin-left is initially increasing');
+ animation.pause();
+ return animation.ready;
+ })).then(t.step_func(function() {
+ previousAnimVal = getMarginLeft(cs);
+ return waitForFrame();
+ })).then(t.step_func(function() {
+ assert_equals(getMarginLeft(cs), previousAnimVal,
+ 'margin-left does not increase after calling pause()');
+ previousAnimVal = getMarginLeft(cs);
+ animation.play();
+ return animation.ready.then(waitForFrame);
+ })).then(t.step_func(function() {
+ assert_true(getMarginLeft(cs) > previousAnimVal,
+ 'margin-left increases after calling play()');
+ t.done();
+ }));
+}, 'pause() and play() a transition');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_animation-ready.html b/dom/animation/test/css-transitions/file_animation-ready.html
new file mode 100644
index 000000000..f141da796
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_animation-ready.html
@@ -0,0 +1,96 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+async_test(function(t) {
+ var div = addDiv(t);
+ div.style.transform = 'translate(0px)';
+ window.getComputedStyle(div).transform;
+ div.style.transition = 'transform 100s';
+ div.style.transform = 'translate(10px)';
+ window.getComputedStyle(div).transform;
+
+ var animation = div.getAnimations()[0];
+ var originalReadyPromise = animation.ready;
+
+ animation.ready.then(t.step_func(function() {
+ assert_equals(animation.ready, originalReadyPromise,
+ 'Ready promise is the same object when playing completes');
+ animation.pause();
+ assert_not_equals(animation.ready, originalReadyPromise,
+ 'Ready promise object identity differs when pausing');
+ t.done();
+ }));
+}, 'A new ready promise is created each time play() is called'
+ + ' the animation property');
+
+async_test(function(t) {
+ var div = addDiv(t);
+
+ // Set up pending transition
+ div.style.transform = 'translate(0px)';
+ window.getComputedStyle(div).transform;
+ div.style.transition = 'transform 100s';
+ div.style.transform = 'translate(10px)';
+ window.getComputedStyle(div).transform;
+
+ var animation = div.getAnimations()[0];
+ assert_equals(animation.playState, 'pending', 'Animation is initially pending');
+
+ // Set up listeners on ready promise
+ animation.ready.then(t.step_func(function() {
+ assert_unreached('ready promise was fulfilled');
+ })).catch(t.step_func(function(err) {
+ assert_equals(err.name, 'AbortError',
+ 'ready promise is rejected with AbortError');
+ assert_equals(animation.playState, 'idle',
+ 'Animation is idle after transition was cancelled');
+ })).then(t.step_func(function() {
+ t.done();
+ }));
+
+ // Now remove transform from transition-property and flush styles
+ div.style.transitionProperty = 'none';
+ window.getComputedStyle(div).transitionProperty;
+
+}, 'ready promise is rejected when a transition is cancelled by updating'
+ + ' transition-property');
+
+async_test(function(t) {
+ var div = addDiv(t);
+
+ // Set up pending transition
+ div.style.marginLeft = '0px';
+ window.getComputedStyle(div).marginLeft;
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '100px';
+ window.getComputedStyle(div).marginLeft;
+
+ var animation = div.getAnimations()[0];
+ assert_equals(animation.playState, 'pending', 'Animation is initially pending');
+
+ // Set up listeners on ready promise
+ animation.ready.then(t.step_func(function() {
+ assert_unreached('ready promise was fulfilled');
+ })).catch(t.step_func(function(err) {
+ assert_equals(err.name, 'AbortError',
+ 'ready promise is rejected with AbortError');
+ assert_equals(animation.playState, 'idle',
+ 'Animation is idle after transition was cancelled');
+ })).then(t.step_func(function() {
+ t.done();
+ }));
+
+ // Now update the transition to animate to something not-interpolable
+ div.style.marginLeft = 'auto';
+ window.getComputedStyle(div).marginLeft;
+
+}, 'ready promise is rejected when a transition is cancelled by changing'
+ + ' the transition property to something not interpolable');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_animation-starttime.html b/dom/animation/test/css-transitions/file_animation-starttime.html
new file mode 100644
index 000000000..a156ba0a0
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_animation-starttime.html
@@ -0,0 +1,284 @@
+<!doctype html>
+<html>
+ <head>
+ <meta charset=utf-8>
+ <title>Tests for the effect of setting a CSS transition's
+ Animation.startTime</title>
+ <style>
+
+.animated-div {
+ margin-left: 100px;
+ transition: margin-left 1000s linear 1000s;
+}
+
+ </style>
+ <script src="../testcommon.js"></script>
+ </head>
+ <body>
+ <script type="text/javascript">
+
+'use strict';
+
+// TODO: Once the computedTiming property is implemented, add checks to the
+// checker helpers to ensure that computedTiming's properties are updated as
+// expected.
+// See https://bugzilla.mozilla.org/show_bug.cgi?id=1108055
+
+
+const ANIM_DELAY_MS = 1000000; // 1000s
+const ANIM_DUR_MS = 1000000; // 1000s
+
+/**
+ * These helpers get the value that the startTime needs to be set to, to put an
+ * animation that uses the above ANIM_DELAY_MS and ANIM_DUR_MS values into the
+ * middle of various phases or points through the active duration.
+ */
+function startTimeForBeforePhase(timeline) {
+ return timeline.currentTime - ANIM_DELAY_MS / 2;
+}
+function startTimeForActivePhase(timeline) {
+ return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS / 2;
+}
+function startTimeForAfterPhase(timeline) {
+ return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS - ANIM_DELAY_MS / 2;
+}
+function startTimeForStartOfActiveInterval(timeline) {
+ return timeline.currentTime - ANIM_DELAY_MS;
+}
+function startTimeForFiftyPercentThroughActiveInterval(timeline) {
+ return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS * 0.5;
+}
+function startTimeForEndOfActiveInterval(timeline) {
+ return timeline.currentTime - ANIM_DELAY_MS - ANIM_DUR_MS;
+}
+
+
+// Expected computed 'margin-left' values at points during the active interval:
+// When we assert_between_inclusive using these values we could in theory cause
+// intermittent failure due to very long delays between paints, but since the
+// active duration is 1000s long, a delay would need to be around 100s to cause
+// that. If that's happening then there are likely other issues that should be
+// fixed, so a failure to make us look into that seems like a good thing.
+const INITIAL_POSITION = 100;
+const TEN_PCT_POSITION = 110;
+const FIFTY_PCT_POSITION = 150;
+const END_POSITION = 200;
+
+// The terms used for the naming of the following helper functions refer to
+// terms used in the Web Animations specification for specific phases of an
+// animation. The terms can be found here:
+//
+// https://w3c.github.io/web-animations/#animation-effect-phases-and-states
+//
+// Note the distinction between the "animation start time" which occurs before
+// the start delay and the start of the active interval which occurs after it.
+
+// Called when the ready Promise's callbacks should happen
+function checkStateOnReadyPromiseResolved(animation)
+{
+ assert_less_than_equal(animation.startTime, animation.timeline.currentTime,
+ 'Animation.startTime should be less than the timeline\'s ' +
+ 'currentTime on the first paint tick after animation creation');
+
+ assert_equals(animation.playState, 'running',
+ 'Animation.playState should be "running" on the first paint ' +
+ 'tick after animation creation');
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_equals(marginLeft, INITIAL_POSITION,
+ 'the computed value of margin-left should be unaffected ' +
+ 'by an animation with a delay on ready Promise resolve');
+}
+
+// Called when startTime is set to the time the active interval starts.
+function checkStateAtActiveIntervalStartTime(animation)
+{
+ // We don't test animation.startTime since our caller just set it.
+
+ assert_equals(animation.playState, 'running',
+ 'Animation.playState should be "running" at the start of ' +
+ 'the active interval');
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_between_inclusive(marginLeft, INITIAL_POSITION, TEN_PCT_POSITION,
+ 'the computed value of margin-left should be close to the value at the ' +
+ 'beginning of the animation');
+}
+
+function checkStateAtFiftyPctOfActiveInterval(animation)
+{
+ // We don't test animation.startTime since our caller just set it.
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_equals(marginLeft, FIFTY_PCT_POSITION,
+ 'the computed value of margin-left should be half way through the ' +
+ 'animation at the midpoint of the active interval');
+}
+
+// Called when startTime is set to the time the active interval ends.
+function checkStateAtActiveIntervalEndTime(animation)
+{
+ // We don't test animation.startTime since our caller just set it.
+
+ assert_equals(animation.playState, 'finished',
+ 'Animation.playState should be "finished" at the end of ' +
+ 'the active interval');
+
+ var div = animation.effect.target;
+ var marginLeft = parseFloat(getComputedStyle(div).marginLeft);
+ assert_equals(marginLeft, END_POSITION,
+ 'the computed value of margin-left should be the final transitioned-to ' +
+ 'value at the end of the active duration');
+}
+
+test(function(t)
+{
+ var div = addDiv(t, {'class': 'animated-div'});
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+ assert_equals(animation.startTime, null, 'startTime is unresolved');
+}, 'startTime of a newly created transition is unresolved');
+
+
+test(function(t)
+{
+ var div = addDiv(t, {'class': 'animated-div'});
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+ var currentTime = animation.timeline.currentTime;
+ animation.startTime = currentTime;
+ assert_approx_equals(animation.startTime, currentTime, 0.0001, // rounding error
+ 'Check setting of startTime actually works');
+}, 'Sanity test to check round-tripping assigning to new animation\'s ' +
+ 'startTime');
+
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ var eventWatcher = new EventWatcher(t, div, 'transitionend');
+
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ animation.ready.then(t.step_func(function() {
+ checkStateOnReadyPromiseResolved(animation);
+
+ animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
+ checkStateAtActiveIntervalStartTime(animation);
+
+ animation.startTime =
+ startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
+ checkStateAtFiftyPctOfActiveInterval(animation);
+
+ animation.startTime = startTimeForEndOfActiveInterval(animation.timeline);
+ return eventWatcher.wait_for('transitionend');
+ })).then(t.step_func(function() {
+ checkStateAtActiveIntervalEndTime(animation);
+ })).catch(t.step_func(function(reason) {
+ assert_unreached(reason);
+ })).then(function() {
+ t.done();
+ });
+}, 'Skipping forward through animation');
+
+
+test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+ var eventWatcher = new EventWatcher(t, div, 'transitionend');
+
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ // Unlike in the case of CSS animations, we cannot skip to the end and skip
+ // backwards since when we reach the end the transition effect is removed and
+ // changes to the Animation object no longer affect the element. For
+ // this reason we only skip forwards as far as the 90% through point.
+
+ animation.startTime =
+ startTimeForFiftyPercentThroughActiveInterval(animation.timeline);
+ checkStateAtFiftyPctOfActiveInterval(animation);
+
+ animation.startTime = startTimeForStartOfActiveInterval(animation.timeline);
+
+ // Despite going backwards from being in the active interval to being before
+ // it, we now expect an 'animationend' event because the animation should go
+ // from being active to inactive.
+ //
+ // Calling checkStateAtActiveIntervalStartTime will check computed style,
+ // causing computed style to be updated and the 'transitionend' event to
+ // be dispatched synchronously. We need to call waitForEvent first
+ // otherwise eventWatcher will assert that the event was unexpected.
+ eventWatcher.wait_for('transitionend').then(function() {
+ t.done();
+ });
+ checkStateAtActiveIntervalStartTime(animation);
+}, 'Skipping backwards through transition');
+
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ var storedCurrentTime;
+
+ animation.ready.then(t.step_func(function() {
+ storedCurrentTime = animation.currentTime;
+ animation.startTime = null;
+ return animation.ready;
+ })).catch(t.step_func(function(reason) {
+ assert_unreached(reason);
+ })).then(t.step_func(function() {
+ assert_equals(animation.currentTime, storedCurrentTime,
+ 'Test that hold time is correct');
+ t.done();
+ }));
+}, 'Setting startTime to null');
+
+
+async_test(function(t) {
+ var div = addDiv(t, {'class': 'animated-div'});
+
+ flushComputedStyle(div);
+ div.style.marginLeft = '200px'; // initiate transition
+
+ var animation = div.getAnimations()[0];
+
+ animation.ready.then(t.step_func(function() {
+ var savedStartTime = animation.startTime;
+
+ assert_not_equals(animation.startTime, null,
+ 'Animation.startTime not null on ready Promise resolve');
+
+ animation.pause();
+ return animation.ready;
+ })).then(t.step_func(function() {
+ assert_equals(animation.startTime, null,
+ 'Animation.startTime is null after paused');
+ assert_equals(animation.playState, 'paused',
+ 'Animation.playState is "paused" after pause() call');
+ })).catch(t.step_func(function(reason) {
+ assert_unreached(reason);
+ })).then(function() {
+ t.done();
+ });
+}, 'Animation.startTime after paused');
+
+done();
+ </script>
+ </body>
+</html>
diff --git a/dom/animation/test/css-transitions/file_csstransition-events.html b/dom/animation/test/css-transitions/file_csstransition-events.html
new file mode 100644
index 000000000..5011bc130
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_csstransition-events.html
@@ -0,0 +1,223 @@
+<!doctype html>
+<meta charset=utf-8>
+<title>Tests for CSS-Transition events</title>
+<link rel="help" href="https://drafts.csswg.org/css-transitions-2/#transition-events">
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+/**
+ * Helper class to record the elapsedTime member of each event.
+ * The EventWatcher class in testharness.js allows us to wait on
+ * multiple events in a certain order but only records the event
+ * parameters of the most recent event.
+ */
+function TransitionEventHandler(target) {
+ this.target = target;
+ this.target.ontransitionrun = function(evt) {
+ this.transitionrun = evt.elapsedTime;
+ }.bind(this);
+ this.target.ontransitionstart = function(evt) {
+ this.transitionstart = evt.elapsedTime;
+ }.bind(this);
+ this.target.ontransitionend = function(evt) {
+ this.transitionend = evt.elapsedTime;
+ }.bind(this);
+}
+
+TransitionEventHandler.prototype.clear = function() {
+ this.transitionrun = undefined;
+ this.transitionstart = undefined;
+ this.transitionend = undefined;
+};
+
+function setupTransition(t, transitionStyle) {
+ var div, watcher, handler, transition;
+ transitionStyle = transitionStyle || 'transition: margin-left 100s 100s';
+ div = addDiv(t, { style: transitionStyle });
+ watcher = new EventWatcher(t, div, [ 'transitionrun',
+ 'transitionstart',
+ 'transitionend' ]);
+ handler = new TransitionEventHandler(div);
+ flushComputedStyle(div);
+
+ div.style.marginLeft = '100px';
+ flushComputedStyle(div);
+
+ transition = div.getAnimations()[0];
+
+ return [transition, watcher, handler];
+}
+
+// On the next frame (i.e. when events are queued), whether or not the
+// transition is still pending depends on the implementation.
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Idle -> Pending or Before');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ // Force the transition to leave the idle phase
+ transition.startTime = document.timeline.currentTime;
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Idle -> Before');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ transition.pause();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ assert_equals(handler.transitionrun, 0.0);
+ assert_equals(handler.transitionstart, 0.0);
+ });
+}, 'Idle or Pending -> Active');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ // Seek to After phase.
+ transition.finish();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart',
+ 'transitionend' ]).then(function(evt) {
+ assert_equals(handler.transitionrun, 0.0);
+ assert_equals(handler.transitionstart, 0.0);
+ assert_equals(handler.transitionend, 100.0);
+ });
+}, 'Idle or Pending -> After');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for('transitionstart');
+ }).then(function() {
+ assert_equals(handler.transitionstart, 0.0);
+ });
+}, 'Before -> Active');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ // Seek to After phase.
+ transition.currentTime = 200 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionstart', 'transitionend' ]);
+ }).then(function(evt) {
+ assert_equals(handler.transitionstart, 0.0);
+ assert_equals(handler.transitionend, 100.0);
+ });
+}, 'Before -> After');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Seek to Before phase.
+ transition.currentTime = 0;
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Before');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Seek to After phase.
+ transition.currentTime = 200 * MS_PER_SEC;
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100.0);
+ });
+}, 'Active -> After');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ // Seek to After phase.
+ transition.finish();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart',
+ 'transitionend' ]).then(function(evt) {
+ // Seek to Before phase.
+ transition.currentTime = 0;
+ return watcher.wait_for([ 'transitionstart', 'transitionend' ]);
+ }).then(function(evt) {
+ assert_equals(handler.transitionstart, 100.0);
+ assert_equals(handler.transitionend, 0.0);
+ });
+}, 'After -> Before');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+ // Seek to After phase.
+ transition.finish();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart',
+ 'transitionend' ]).then(function(evt) {
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for('transitionstart');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100.0);
+ });
+}, 'After -> Active');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] =
+ setupTransition(t, 'transition: margin-left 100s -50s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function() {
+ assert_equals(handler.transitionrun, 50.0);
+ assert_equals(handler.transitionstart, 50.0);
+ transition.finish();
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 100.0);
+ });
+}, 'Calculating the interval start and end time with negative start delay.');
+
+promise_test(function(t) {
+ var [transition, watcher, handler] = setupTransition(t);
+
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ // We can't set the end delay via generated effect timing.
+ // Because CSS-Transition use the AnimationEffectTimingReadOnly.
+ transition.effect = new KeyframeEffect(handler.target,
+ { marginleft: [ '0px', '100px' ]},
+ { duration: 100 * MS_PER_SEC,
+ endDelay: -50 * MS_PER_SEC });
+ // Seek to Before and play.
+ transition.cancel();
+ transition.play();
+ return watcher.wait_for('transitionstart');
+ }).then(function() {
+ assert_equals(handler.transitionstart, 0.0);
+
+ // Seek to After phase.
+ transition.finish();
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 50.0);
+ });
+}, 'Calculating the interval start and end time with negative end delay.');
+
+done();
+</script>
+</body>
+</html>
diff --git a/dom/animation/test/css-transitions/file_csstransition-transitionproperty.html b/dom/animation/test/css-transitions/file_csstransition-transitionproperty.html
new file mode 100644
index 000000000..176cc5a4d
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_csstransition-transitionproperty.html
@@ -0,0 +1,24 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+ var div = addDiv(t);
+
+ // Add a transition
+ div.style.left = '0px';
+ window.getComputedStyle(div).transitionProperty;
+ div.style.transition = 'all 100s';
+ div.style.left = '100px';
+
+ assert_equals(div.getAnimations()[0].transitionProperty, 'left',
+ 'The transitionProperty for the corresponds to the specific ' +
+ 'property being transitioned');
+}, 'CSSTransition.transitionProperty');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_document-get-animations.html b/dom/animation/test/css-transitions/file_document-get-animations.html
new file mode 100644
index 000000000..a5d55b76c
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_document-get-animations.html
@@ -0,0 +1,93 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+ assert_equals(document.getAnimations().length, 0,
+ 'getAnimations returns an empty sequence for a document'
+ + ' with no animations');
+}, 'getAnimations for non-animated content');
+
+test(function(t) {
+ var div = addDiv(t);
+
+ // Add a couple of transitions
+ div.style.left = '0px';
+ div.style.top = '0px';
+ getComputedStyle(div).transitionProperty;
+
+ div.style.transition = 'all 100s';
+ div.style.left = '100px';
+ div.style.top = '100px';
+ assert_equals(document.getAnimations().length, 2,
+ 'getAnimations returns two running CSS Transitions');
+
+ // Remove both
+ div.style.transitionProperty = 'none';
+ assert_equals(document.getAnimations().length, 0,
+ 'getAnimations returns no running CSS Transitions');
+}, 'getAnimations for CSS Transitions');
+
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; ' +
+ 'transition: all 100s;',
+ '.init::before': 'content: ""; width: 0px; ' +
+ 'transition: all 10s;',
+ '.change::after': 'width: 100px;',
+ '.change::before': 'width: 100px;' });
+ // create two divs with these arrangement:
+ // parent
+ // ::before,
+ // ::after
+ // |
+ // child
+ var parent = addDiv(t);
+ var child = addDiv(t);
+ parent.appendChild(child);
+
+ parent.style.left = '0px';
+ parent.style.transition = 'left 10s';
+ parent.classList.add('init');
+ child.style.left = '0px';
+ child.style.transition = 'left 10s';
+ flushComputedStyle(parent);
+
+ parent.style.left = '100px';
+ parent.classList.add('change');
+ child.style.left = '100px';
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 4,
+ 'CSS transition on both pseudo-elements and elements ' +
+ 'are returned');
+ assert_equals(anims[0].effect.target, parent,
+ 'The animation targeting the parent element comes first');
+ assert_equals(anims[1].effect.target.type, '::before',
+ 'The animation targeting the ::before element comes second');
+ assert_equals(anims[2].effect.target.type, '::after',
+ 'The animation targeting the ::after element comes third');
+ assert_equals(anims[3].effect.target, child,
+ 'The animation targeting the child element comes last');
+}, 'CSS Transitions targetting (pseudo-)elements should have correct order ' +
+ 'after sorting');
+
+async_test(function(t) {
+ var div = addDiv(t, { style: 'left: 0px; transition: all 50ms' });
+ flushComputedStyle(div);
+
+ div.style.left = '100px';
+ var animations = div.getAnimations();
+ assert_equals(animations.length, 1, 'Got transition');
+ animations[0].finished.then(t.step_func(function() {
+ assert_equals(document.getAnimations().length, 0,
+ 'No animations returned');
+ t.done();
+ }));
+}, 'Transitions are not returned after they have finished');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_effect-target.html b/dom/animation/test/css-transitions/file_effect-target.html
new file mode 100644
index 000000000..0f67b0b9a
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_effect-target.html
@@ -0,0 +1,66 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+ var div = addDiv(t);
+
+ div.style.left = '0px';
+ window.getComputedStyle(div).transitionProperty;
+ div.style.transition = 'left 100s';
+ div.style.left = '100px';
+
+ var animation = div.getAnimations()[0];
+ assert_equals(animation.effect.target, div,
+ 'Animation.target is the animatable div');
+}, 'Returned CSS transitions have the correct Animation.target');
+
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; height: 0px; ' +
+ 'transition: all 10s;',
+ '.change::after': 'width: 100px; height: 100px;' });
+ var div = addDiv(t, { class: 'init' });
+ flushComputedStyle(div);
+ div.classList.add('change');
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got transitions running on ::after pseudo element');
+ assert_equals(anims[0].effect.target, anims[1].effect.target,
+ 'Both transitions return the same target object');
+}, 'effect.target should return the same CSSPseudoElement object each time');
+
+test(function(t) {
+ addStyle(t, { '.init::after': 'content: ""; width: 0px; transition: all 10s;',
+ '.change::after': 'width: 100px;' });
+ var div = addDiv(t, { class: 'init' });
+ flushComputedStyle(div);
+ div.classList.add('change');
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ var effect = new KeyframeEffectReadOnly(pseudoTarget,
+ { background: ["blue", "red"] },
+ 3000);
+ var newAnim = new Animation(effect, document.timeline);
+ newAnim.play();
+
+ var anims = document.getAnimations();
+ assert_equals(anims.length, 2,
+ 'Got animations running on ::after pseudo element');
+ assert_not_equals(anims[0], newAnim,
+ 'The scriped-generated animation appears last');
+ assert_equals(newAnim.effect.target, pseudoTarget,
+ 'The effect.target of the scripted-generated animation is ' +
+ 'the same as the one from the argument of ' +
+ 'KeyframeEffectReadOnly constructor');
+ assert_equals(anims[0].effect.target, newAnim.effect.target,
+ 'Both the transition and the scripted-generated animation ' +
+ 'return the same target object');
+}, 'effect.target from the script-generated animation should return the same ' +
+ 'CSSPseudoElement object as that from the CSS generated transition');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_element-get-animations.html b/dom/animation/test/css-transitions/file_element-get-animations.html
new file mode 100644
index 000000000..0ce145da0
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_element-get-animations.html
@@ -0,0 +1,147 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<body>
+<script>
+'use strict';
+
+async_test(function(t) {
+ var div = addDiv(t);
+
+ // FIXME: This test does too many things. It should be split up.
+
+ // Add a couple of transitions
+ div.style.left = '0px';
+ div.style.top = '0px';
+ window.getComputedStyle(div).transitionProperty;
+
+ div.style.transition = 'all 100s';
+ div.style.left = '100px';
+ div.style.top = '100px';
+
+ var animations = div.getAnimations();
+ assert_equals(animations.length, 2,
+ 'getAnimations() returns one Animation per transitioning property');
+ waitForAllAnimations(animations).then(t.step_func(function() {
+ var startTime = animations[0].startTime;
+ assert_true(startTime > 0 && startTime <= document.timeline.currentTime,
+ 'CSS transitions have sensible start times');
+ assert_equals(animations[0].startTime, animations[1].startTime,
+ 'CSS transitions started together have the same start time');
+ // Wait a moment then add a third transition
+ return waitForFrame();
+ })).then(t.step_func(function() {
+ div.style.backgroundColor = 'green';
+ animations = div.getAnimations();
+ assert_equals(animations.length, 3,
+ 'getAnimations returns Animations for all running CSS Transitions');
+ return waitForAllAnimations(animations);
+ })).then(t.step_func(function() {
+ assert_less_than(animations[1].startTime, animations[2].startTime,
+ 'Animation for additional CSS transition starts after the original'
+ + ' transitions and appears later in the list');
+ t.done();
+ }));
+}, 'getAnimations for CSS Transitions');
+
+test(function(t) {
+ var div = addDiv(t, { style: 'left: 0px; transition: all 100s' });
+
+ flushComputedStyle(div);
+ div.style.left = '100px';
+
+ assert_class_string(div.getAnimations()[0], 'CSSTransition',
+ 'Interface of returned animation is CSSTransition');
+}, 'getAnimations returns CSSTransition objects for CSS Transitions');
+
+async_test(function(t) {
+ var div = addDiv(t);
+
+ // Set up event listener
+ div.addEventListener('transitionend', t.step_func(function() {
+ assert_equals(div.getAnimations().length, 0,
+ 'getAnimations does not return finished CSS Transitions');
+ t.done();
+ }));
+
+ // Add a very short transition
+ div.style.left = '0px';
+ window.getComputedStyle(div).left;
+
+ div.style.transition = 'all 0.01s';
+ div.style.left = '100px';
+ window.getComputedStyle(div).left;
+}, 'getAnimations for CSS Transitions that have finished');
+
+test(function(t) {
+ var div = addDiv(t);
+
+ // Try to transition non-animatable property animation-duration
+ div.style.animationDuration = '10s';
+ window.getComputedStyle(div).animationDuration;
+
+ div.style.transition = 'all 100s';
+ div.style.animationDuration = '100s';
+
+ assert_equals(div.getAnimations().length, 0,
+ 'getAnimations returns an empty sequence for a transition'
+ + ' of a non-animatable property');
+}, 'getAnimations for transition on non-animatable property');
+
+test(function(t) {
+ var div = addDiv(t);
+
+ div.style.setProperty('-vendor-unsupported', '0px', '');
+ window.getComputedStyle(div).transitionProperty;
+ div.style.transition = 'all 100s';
+ div.style.setProperty('-vendor-unsupported', '100px', '');
+
+ assert_equals(div.getAnimations().length, 0,
+ 'getAnimations returns an empty sequence for a transition'
+ + ' of an unsupported property');
+}, 'getAnimations for transition on unsupported property');
+
+test(function(t) {
+ var div = addDiv(t, { style: 'transform: translate(0px); ' +
+ 'opacity: 0; ' +
+ 'border-width: 0px; ' + // Shorthand
+ 'border-style: solid' });
+ getComputedStyle(div).transform;
+
+ div.style.transition = 'all 100s';
+ div.style.transform = 'translate(100px)';
+ div.style.opacity = '1';
+ div.style.borderWidth = '1px';
+
+ var animations = div.getAnimations();
+ assert_equals(animations.length, 6,
+ 'Generated expected number of transitions');
+ assert_equals(animations[0].transitionProperty, 'border-bottom-width');
+ assert_equals(animations[1].transitionProperty, 'border-left-width');
+ assert_equals(animations[2].transitionProperty, 'border-right-width');
+ assert_equals(animations[3].transitionProperty, 'border-top-width');
+ assert_equals(animations[4].transitionProperty, 'opacity');
+ assert_equals(animations[5].transitionProperty, 'transform');
+}, 'getAnimations sorts simultaneous transitions by name');
+
+test(function(t) {
+ var div = addDiv(t, { style: 'transform: translate(0px); ' +
+ 'opacity: 0' });
+ getComputedStyle(div).transform;
+
+ div.style.transition = 'all 100s';
+ div.style.transform = 'translate(100px)';
+ assert_equals(div.getAnimations().length, 1,
+ 'Initially there is only one (transform) transition');
+ div.style.opacity = '1';
+ assert_equals(div.getAnimations().length, 2,
+ 'Then a second (opacity) transition is added');
+
+ var animations = div.getAnimations();
+ assert_equals(animations[0].transitionProperty, 'transform');
+ assert_equals(animations[1].transitionProperty, 'opacity');
+}, 'getAnimations sorts transitions by when they were generated');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_keyframeeffect-getkeyframes.html b/dom/animation/test/css-transitions/file_keyframeeffect-getkeyframes.html
new file mode 100644
index 000000000..7bbf76fa7
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_keyframeeffect-getkeyframes.html
@@ -0,0 +1,95 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+:root {
+ --var-100px: 100px;
+}
+</style>
+<body>
+<script>
+'use strict';
+
+function getKeyframes(e) {
+ return e.getAnimations()[0].effect.getKeyframes();
+}
+
+function assert_frames_equal(a, b, name) {
+ assert_equals(Object.keys(a).sort().toString(),
+ Object.keys(b).sort().toString(),
+ "properties on " + name);
+ for (var p in a) {
+ assert_equals(a[p], b[p], "value for '" + p + "' on " + name);
+ }
+}
+
+test(function(t) {
+ var div = addDiv(t);
+
+ div.style.left = '0px';
+ window.getComputedStyle(div).transitionProperty;
+ div.style.transition = 'left 100s';
+ div.style.left = '100px';
+
+ var frames = getKeyframes(div);
+
+ assert_equals(frames.length, 2, "number of frames");
+
+ var expected = [
+ { offset: 0, computedOffset: 0, easing: "ease", left: "0px" },
+ { offset: 1, computedOffset: 1, easing: "linear", left: "100px" },
+ ];
+
+ for (var i = 0; i < frames.length; i++) {
+ assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+ }
+}, 'KeyframeEffectReadOnly.getKeyframes() returns expected frames for a simple'
+ + ' transition');
+
+test(function(t) {
+ var div = addDiv(t);
+
+ div.style.left = '0px';
+ window.getComputedStyle(div).transitionProperty;
+ div.style.transition = 'left 100s steps(2,end)';
+ div.style.left = '100px';
+
+ var frames = getKeyframes(div);
+
+ assert_equals(frames.length, 2, "number of frames");
+
+ var expected = [
+ { offset: 0, computedOffset: 0, easing: "steps(2)", left: "0px" },
+ { offset: 1, computedOffset: 1, easing: "linear", left: "100px" },
+ ];
+
+ for (var i = 0; i < frames.length; i++) {
+ assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+ }
+}, 'KeyframeEffectReadOnly.getKeyframes() returns expected frames for a simple'
+ + ' transition with a non-default easing function');
+
+test(function(t) {
+ var div = addDiv(t);
+ div.style.left = '0px';
+ window.getComputedStyle(div).transitionProperty;
+ div.style.transition = 'left 100s';
+ div.style.left = 'var(--var-100px)';
+
+ var frames = getKeyframes(div);
+
+ // CSS transition endpoints are based on the computed value so we
+ // shouldn't see the variable reference
+ var expected = [
+ { offset: 0, computedOffset: 0, easing: 'ease', left: '0px' },
+ { offset: 1, computedOffset: 1, easing: 'linear', left: '100px' },
+ ];
+ for (var i = 0; i < frames.length; i++) {
+ assert_frames_equal(frames[i], expected[i], "ComputedKeyframe #" + i);
+ }
+}, 'KeyframeEffectReadOnly.getKeyframes() returns expected frames for a'
+ + ' transition with a CSS variable endpoint');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_pseudoElement-get-animations.html b/dom/animation/test/css-transitions/file_pseudoElement-get-animations.html
new file mode 100644
index 000000000..5683a14a1
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_pseudoElement-get-animations.html
@@ -0,0 +1,45 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="../testcommon.js"></script>
+<style>
+.init::before {
+ content: '';
+ height: 0px;
+ width: 0px;
+ opacity: 0;
+ transition: all 100s;
+}
+.change::before {
+ height: 100px;
+ width: 100px;
+ opacity: 1;
+}
+</style>
+<body>
+<script>
+'use strict';
+
+test(function(t) {
+ var div = addDiv(t, { class: 'init' });
+ flushComputedStyle(div);
+ div.classList.add('change');
+
+ // Sanity checks
+ assert_equals(document.getAnimations().length, 3,
+ 'Got expected number of animations on document');
+ var pseudoTarget = document.getAnimations()[0].effect.target;
+ assert_class_string(pseudoTarget, 'CSSPseudoElement',
+ 'Got pseudo-element target');
+
+ // Check animations returned from the pseudo element are in correct order
+ var anims = pseudoTarget.getAnimations();
+ assert_equals(anims.length, 3,
+ 'Got expected number of animations on pseudo-element');
+ assert_equals(anims[0].transitionProperty, 'height');
+ assert_equals(anims[1].transitionProperty, 'opacity');
+ assert_equals(anims[2].transitionProperty, 'width');
+}, 'getAnimations sorts simultaneous transitions by name');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/file_setting-effect.html b/dom/animation/test/css-transitions/file_setting-effect.html
new file mode 100644
index 000000000..c61877194
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_setting-effect.html
@@ -0,0 +1,91 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src='../testcommon.js'></script>
+<body>
+<script>
+'use strict';
+
+promise_test(function(t) {
+ var div = addDiv(t);
+ div.style.left = '0px';
+
+ div.style.transition = 'left 100s';
+ flushComputedStyle(div);
+ div.style.left = '100px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ transition.currentTime = 50 * MS_PER_SEC;
+ transition.effect = null;
+ assert_equals(transition.transitionProperty, 'left');
+ assert_equals(transition.playState, 'finished');
+ assert_equals(window.getComputedStyle(div).left, '100px');
+ });
+}, 'Test for removing a transition effect');
+
+promise_test(function(t) {
+ var div = addDiv(t);
+ div.style.left = '0px';
+
+ div.style.transition = 'left 100s';
+ flushComputedStyle(div);
+ div.style.left = '100px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ transition.currentTime = 50 * MS_PER_SEC;
+ transition.effect = new KeyframeEffect(div,
+ { marginLeft: [ '0px' , '100px'] },
+ 100 * MS_PER_SEC);
+ assert_equals(transition.transitionProperty, 'left');
+ assert_equals(transition.playState, 'running');
+ assert_equals(window.getComputedStyle(div).left, '100px');
+ assert_equals(window.getComputedStyle(div).marginLeft, '50px');
+ });
+}, 'Test for replacing the transition effect by a new keyframe effect');
+
+promise_test(function(t) {
+ var div = addDiv(t);
+ div.style.left = '0px';
+ div.style.width = '0px';
+
+ div.style.transition = 'left 100s';
+ flushComputedStyle(div);
+ div.style.left = '100px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ transition.currentTime = 50 * MS_PER_SEC;
+ transition.effect = new KeyframeEffect(div,
+ { marginLeft: [ '0px' , '100px'] },
+ 20 * MS_PER_SEC);
+ assert_equals(transition.playState, 'finished');
+ });
+}, 'Test for setting a new keyframe effect with a shorter duration');
+
+promise_test(function(t) {
+ var div = addDiv(t);
+ div.style.left = '0px';
+ div.style.width = '0px';
+
+ div.style.transition = 'left 100s';
+ flushComputedStyle(div);
+ div.style.left = '100px';
+
+ var transition = div.getAnimations()[0];
+ assert_equals(transition.playState, 'pending');
+
+ transition.effect = new KeyframeEffect(div,
+ { marginLeft: [ '0px' , '100px'] },
+ 100 * MS_PER_SEC);
+ assert_equals(transition.transitionProperty, 'left');
+ assert_equals(transition.playState, 'pending');
+
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ });
+}, 'Test for setting a new keyframe effect to a pending transition');
+
+done();
+</script>
+</body>
diff --git a/dom/animation/test/css-transitions/test_animation-cancel.html b/dom/animation/test/css-transitions/test_animation-cancel.html
new file mode 100644
index 000000000..949e0843e
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-cancel.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_animation-cancel.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_animation-computed-timing.html b/dom/animation/test/css-transitions/test_animation-computed-timing.html
new file mode 100644
index 000000000..c1b40aaf3
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-computed-timing.html
@@ -0,0 +1,16 @@
+<!doctype html>
+<html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_animation-computed-timing.html");
+ });
+</script>
+</html>
diff --git a/dom/animation/test/css-transitions/test_animation-currenttime.html b/dom/animation/test/css-transitions/test_animation-currenttime.html
new file mode 100644
index 000000000..30b0ed030
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-currenttime.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_animation-currenttime.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_animation-finished.html b/dom/animation/test/css-transitions/test_animation-finished.html
new file mode 100644
index 000000000..f2ed7f80b
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-finished.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_animation-finished.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_animation-pausing.html b/dom/animation/test/css-transitions/test_animation-pausing.html
new file mode 100644
index 000000000..67484a2a5
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-pausing.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_animation-pausing.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_animation-ready.html b/dom/animation/test/css-transitions/test_animation-ready.html
new file mode 100644
index 000000000..a928ded64
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-ready.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_animation-ready.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_animation-starttime.html b/dom/animation/test/css-transitions/test_animation-starttime.html
new file mode 100644
index 000000000..8a8c85f2d
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_animation-starttime.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_animation-starttime.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_csstransition-events.html b/dom/animation/test/css-transitions/test_csstransition-events.html
new file mode 100644
index 000000000..92559ad67
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_csstransition-events.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_csstransition-events.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_csstransition-transitionproperty.html b/dom/animation/test/css-transitions/test_csstransition-transitionproperty.html
new file mode 100644
index 000000000..0aa1912d9
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_csstransition-transitionproperty.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_csstransition-transitionproperty.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_document-get-animations.html b/dom/animation/test/css-transitions/test_document-get-animations.html
new file mode 100644
index 000000000..dc964e62c
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_document-get-animations.html
@@ -0,0 +1,15 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_document-get-animations.html");
+ });
+</script>
+</html>
diff --git a/dom/animation/test/css-transitions/test_effect-target.html b/dom/animation/test/css-transitions/test_effect-target.html
new file mode 100644
index 000000000..f3ae72229
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_effect-target.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_effect-target.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_element-get-animations.html b/dom/animation/test/css-transitions/test_element-get-animations.html
new file mode 100644
index 000000000..87abdfa73
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_element-get-animations.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_element-get-animations.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_keyframeeffect-getkeyframes.html b/dom/animation/test/css-transitions/test_keyframeeffect-getkeyframes.html
new file mode 100644
index 000000000..dcc54255d
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_keyframeeffect-getkeyframes.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_keyframeeffect-getkeyframes.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_pseudoElement-get-animations.html b/dom/animation/test/css-transitions/test_pseudoElement-get-animations.html
new file mode 100644
index 000000000..1e0dc5c82
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_pseudoElement-get-animations.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<div id="log"></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { "set": [["dom.animations-api.core.enabled", true]]},
+ function() {
+ window.open("file_pseudoElement-get-animations.html");
+ });
+</script>
diff --git a/dom/animation/test/css-transitions/test_setting-effect.html b/dom/animation/test/css-transitions/test_setting-effect.html
new file mode 100644
index 000000000..a9654ec55
--- /dev/null
+++ b/dom/animation/test/css-transitions/test_setting-effect.html
@@ -0,0 +1,14 @@
+<!doctype html>
+<meta charset=utf-8>
+<script src='/resources/testharness.js'></script>
+<script src='/resources/testharnessreport.js'></script>
+<div id='log'></div>
+<script>
+'use strict';
+setup({explicit_done: true});
+SpecialPowers.pushPrefEnv(
+ { 'set': [['dom.animations-api.core.enabled', true]]},
+ function() {
+ window.open('file_setting-effect.html');
+ });
+</script>