summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--dom/animation/ComputedTiming.h2
-rw-r--r--dom/animation/test/css-transitions/file_animation-cancel.html182
-rw-r--r--dom/animation/test/css-transitions/file_csstransition-events.html223
-rw-r--r--dom/animation/test/css-transitions/file_event-dispatch.html474
-rw-r--r--dom/animation/test/css-transitions/file_setting-effect.html9
-rw-r--r--dom/animation/test/css-transitions/test_event-dispatch.html (renamed from dom/animation/test/css-transitions/test_csstransition-events.html)2
-rw-r--r--dom/animation/test/mochitest.ini4
-rw-r--r--layout/style/test/test_animations_event_handler_attribute.html30
-rw-r--r--layout/style/test/test_animations_event_order.html40
9 files changed, 679 insertions, 287 deletions
diff --git a/dom/animation/ComputedTiming.h b/dom/animation/ComputedTiming.h
index 525d59d23d..b1c6674a51 100644
--- a/dom/animation/ComputedTiming.h
+++ b/dom/animation/ComputedTiming.h
@@ -30,7 +30,7 @@ struct ComputedTiming
// indefinitely.
StickyTimeDuration mActiveDuration;
// The time within the active interval.
- StickyTimeDuration mActiveTime;
+ StickyTimeDuration mActiveTime;
// The effect end time in local time (i.e. an offset from the effect's
// start time). Will equal StickyTimeDuration::Forever() if the animation
// plays indefinitely.
diff --git a/dom/animation/test/css-transitions/file_animation-cancel.html b/dom/animation/test/css-transitions/file_animation-cancel.html
index 6094b383f3..71f02fb118 100644
--- a/dom/animation/test/css-transitions/file_animation-cancel.html
+++ b/dom/animation/test/css-transitions/file_animation-cancel.html
@@ -11,13 +11,12 @@ promise_test(function(t) {
div.style.transition = 'margin-left 100s';
div.style.marginLeft = '1000px';
- flushComputedStyle(div);
- var animation = div.getAnimations()[0];
- return animation.ready.then(waitForFrame).then(function() {
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(waitForFrame).then(function() {
assert_not_equals(getComputedStyle(div).marginLeft, '1000px',
'transform style is animated before cancelling');
- animation.cancel();
+ transition.cancel();
assert_equals(getComputedStyle(div).marginLeft, div.style.marginLeft,
'transform style is no longer animated after cancelling');
});
@@ -29,45 +28,21 @@ promise_test(function(t) {
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();
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ transition.cancel();
assert_equals(getComputedStyle(div).marginLeft, '1000px',
'margin-left style is not animated after cancelling');
- animation.play();
+ transition.play();
assert_equals(getComputedStyle(div).marginLeft, '0px',
'margin-left style is animated after re-starting transition');
- return animation.ready;
+ return transition.ready;
}).then(function() {
- assert_equals(animation.playState, 'running',
+ assert_equals(transition.playState, 'running',
'Transition succeeds in running after being re-started');
});
-}, 'After cancelling a transition, it can still be re-used');
+}, 'After canceling a transition, it can still be re-used');
promise_test(function(t) {
var div = addDiv(t, { style: 'margin-left: 0px' });
@@ -75,20 +50,19 @@ promise_test(function(t) {
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();
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ transition.finish();
+ transition.cancel();
assert_equals(getComputedStyle(div).marginLeft, '1000px',
'margin-left style is not animated after cancelling');
- animation.play();
+ transition.play();
assert_equals(getComputedStyle(div).marginLeft, '0px',
'margin-left style is animated after re-starting transition');
- return animation.ready;
+ return transition.ready;
}).then(function() {
- assert_equals(animation.playState, 'running',
+ assert_equals(transition.playState, 'running',
'Transition succeeds in running after being re-started');
});
}, 'After cancelling a finished transition, it can still be re-used');
@@ -99,10 +73,9 @@ test(function(t) {
div.style.transition = 'margin-left 100s';
div.style.marginLeft = '1000px';
- flushComputedStyle(div);
- var animation = div.getAnimations()[0];
- animation.cancel();
+ var transition = div.getAnimations()[0];
+ transition.cancel();
assert_equals(getComputedStyle(div).marginLeft, '1000px',
'margin-left style is not animated after cancelling');
@@ -113,7 +86,7 @@ test(function(t) {
assert_equals(getComputedStyle(div).marginLeft, '1000px',
'margin-left style is still not animated after updating'
+ ' transition-duration');
- assert_equals(animation.playState, 'idle',
+ assert_equals(transition.playState, 'idle',
'Transition is still idle after updating transition-duration');
}, 'After cancelling a transition, updating transition properties doesn\'t make'
+ ' it live again');
@@ -124,15 +97,14 @@ promise_test(function(t) {
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');
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
div.style.display = 'none';
return waitForFrame();
}).then(function() {
- assert_equals(animation.playState, 'idle');
+ assert_equals(transition.playState, 'idle');
assert_equals(getComputedStyle(div).marginLeft, '1000px');
});
}, 'Setting display:none on an element cancels its transitions');
@@ -147,19 +119,115 @@ promise_test(function(t) {
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');
+ var transition = childDiv.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
parentDiv.style.display = 'none';
return waitForFrame();
}).then(function() {
- assert_equals(animation.playState, 'idle');
+ assert_equals(transition.playState, 'idle');
assert_equals(getComputedStyle(childDiv).marginLeft, '1000px');
});
}, 'Setting display:none cancels transitions on a child element');
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ // Set an unrecognized property value
+ div.style.transitionProperty = 'none';
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(transition.playState, 'idle');
+ assert_equals(getComputedStyle(div).marginLeft, '1000px');
+ });
+}, 'Removing a property from transition-property cancels transitions on that '+
+ 'property');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ div.style.transition = 'margin-top 10s -10s'; // combined duration is zero
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(transition.playState, 'idle');
+ assert_equals(getComputedStyle(div).marginLeft, '1000px');
+ });
+}, 'Setting zero combined duration');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ div.style.marginLeft = '2000px';
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(transition.playState, 'idle');
+ });
+}, 'Changing style to another interpolable value cancels the original ' +
+ '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';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ div.style.marginLeft = 'auto';
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(div.getAnimations().length, 0,
+ 'There should be no transitions');
+ assert_equals(transition.playState, 'idle');
+ });
+}, 'An after-change style value can\'t be interpolated');
+
+promise_test(function(t) {
+ var div = addDiv(t, { style: 'margin-left: 0px' });
+ flushComputedStyle(div);
+
+ div.style.transition = 'margin-left 100s';
+ div.style.marginLeft = '1000px';
+
+ var transition = div.getAnimations()[0];
+ return transition.ready.then(function() {
+ assert_equals(transition.playState, 'running');
+ div.style.marginLeft = '0px';
+ flushComputedStyle(div);
+ return waitForFrame();
+ }).then(function() {
+ assert_equals(transition.playState, 'idle');
+ });
+}, 'Reversing a running transition cancels the original transition');
+
done();
</script>
</body>
diff --git a/dom/animation/test/css-transitions/file_csstransition-events.html b/dom/animation/test/css-transitions/file_csstransition-events.html
deleted file mode 100644
index 5011bc1309..0000000000
--- a/dom/animation/test/css-transitions/file_csstransition-events.html
+++ /dev/null
@@ -1,223 +0,0 @@
-<!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_event-dispatch.html b/dom/animation/test/css-transitions/file_event-dispatch.html
new file mode 100644
index 0000000000..7140cda36a
--- /dev/null
+++ b/dom/animation/test/css-transitions/file_event-dispatch.html
@@ -0,0 +1,474 @@
+<!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);
+ this.target.ontransitioncancel = function(evt) {
+ this.transitioncancel = evt.elapsedTime;
+ }.bind(this);
+}
+
+TransitionEventHandler.prototype.clear = function() {
+ this.transitionrun = undefined;
+ this.transitionstart = undefined;
+ this.transitionend = undefined;
+ this.transitioncancel = undefined;
+};
+
+function setupTransition(t, transitionStyle) {
+ var div = addDiv(t, { style: 'transition: ' + transitionStyle });
+ var watcher = new EventWatcher(t, div, [ 'transitionrun',
+ 'transitionstart',
+ 'transitionend',
+ 'transitioncancel' ]);
+ flushComputedStyle(div);
+
+ div.style.marginLeft = '100px';
+ var transition = div.getAnimations()[0];
+
+ return [transition, watcher, div];
+}
+
+// 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] =
+ setupTransition(t, 'margin-left 100s 100s');
+ 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] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // 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, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ // 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, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ // 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, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Before -> Idle (display: none)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ // Make idle
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Before -> Idle (Animation.timeline = null)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return Promise.all([ watcher.wait_for('transitionrun'),
+ transition.ready ]).then(function() {
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for('transitionstart');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Before -> Active');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ 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, div] =
+ setupTransition(t, 'margin-left 100s');
+
+ // Seek to Active start position.
+ transition.pause();
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, no delay (display: none)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ transition.currentTime = 0;
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, no delay (Animation.timeline = null)');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // Pause so the currentTime is fixed and we can accurately compare the event
+ // time in transition cancel events.
+ transition.pause();
+
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, with positive delay (display: none)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ // Seek to Active phase.
+ transition.currentTime = 100 * MS_PER_SEC;
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ transition.currentTime = 100 * MS_PER_SEC;
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, with positive delay (Animation.timeline = null)');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s -50s');
+
+ // Pause so the currentTime is fixed and we can accurately compare the event
+ // time in transition cancel events.
+ transition.pause();
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 50.0);
+ });
+}, 'Active -> Idle, with negative delay (display: none)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s -50s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ transition.currentTime = 50 * MS_PER_SEC;
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ assert_equals(evt.elapsedTime, 0.0);
+ });
+}, 'Active -> Idle, with negative delay (Animation.timeline = null)');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // 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] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // 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, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ // 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] =
+ setupTransition(t, 'margin-left 100s 100s');
+ // 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, div] =
+ setupTransition(t, 'margin-left 100s -50s');
+ var handler = new TransitionEventHandler(div);
+
+ 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, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+ var handler = new TransitionEventHandler(div);
+
+ 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(div,
+ { 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([ 'transitioncancel',
+ 'transitionrun',
+ '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.');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return watcher.wait_for('transitionrun').then(function() {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ return watcher.wait_for('transitioncancel');
+ }).then(function() {
+ transition.cancel();
+ // Then wait a couple of frames and check that no event was dispatched
+ return waitForAnimationFrames(2);
+ });
+}, 'Call Animation.cancel after cancelling transition.');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ transition.play();
+ watcher.wait_for([ 'transitioncancel',
+ 'transitionrun',
+ 'transitionstart' ]);
+ });
+}, 'Restart transition after cancelling transition immediately');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s 100s');
+
+ return watcher.wait_for('transitionrun').then(function(evt) {
+ // Make idle
+ div.style.display = 'none';
+ flushComputedStyle(div);
+ transition.play();
+ transition.cancel();
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ // Then wait a couple of frames and check that no event was dispatched
+ return waitForAnimationFrames(2);
+ });
+}, 'Call Animation.cancel after restarting transition immediately');
+
+promise_test(function(t) {
+ var [transition, watcher] =
+ setupTransition(t, 'margin-left 100s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ // Make idle
+ transition.timeline = null;
+ return watcher.wait_for('transitioncancel');
+ }).then(function(evt) {
+ transition.timeline = document.timeline;
+ transition.play();
+
+ return watcher.wait_for(['transitionrun', 'transitionstart']);
+ });
+}, 'Set timeline and play transition after clear the timeline');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function() {
+ transition.cancel();
+ return watcher.wait_for('transitioncancel');
+ }).then(function() {
+ // Make After phase
+ transition.effect = null;
+
+ // Then wait a couple of frames and check that no event was dispatched
+ return waitForAnimationFrames(2);
+ });
+}, 'Set null target effect after cancel the transition');
+
+promise_test(function(t) {
+ var [transition, watcher, div] =
+ setupTransition(t, 'margin-left 100s');
+
+ return watcher.wait_for([ 'transitionrun',
+ 'transitionstart' ]).then(function(evt) {
+ transition.effect = null;
+ return watcher.wait_for('transitionend');
+ }).then(function(evt) {
+ transition.cancel();
+ return watcher.wait_for('transitioncancel');
+ });
+}, 'Cancel the transition after clearing the target effect');
+
+done();
+</script>
+</body>
+</html>
diff --git a/dom/animation/test/css-transitions/file_setting-effect.html b/dom/animation/test/css-transitions/file_setting-effect.html
index c61877194e..81279ea554 100644
--- a/dom/animation/test/css-transitions/file_setting-effect.html
+++ b/dom/animation/test/css-transitions/file_setting-effect.html
@@ -7,6 +7,8 @@
promise_test(function(t) {
var div = addDiv(t);
+ var watcher = new EventWatcher(t, div, [ 'transitionend',
+ 'transitioncancel' ]);
div.style.left = '0px';
div.style.transition = 'left 100s';
@@ -20,11 +22,14 @@ promise_test(function(t) {
assert_equals(transition.transitionProperty, 'left');
assert_equals(transition.playState, 'finished');
assert_equals(window.getComputedStyle(div).left, '100px');
+ return watcher.wait_for('transitionend');
});
}, 'Test for removing a transition effect');
promise_test(function(t) {
var div = addDiv(t);
+ var watcher = new EventWatcher(t, div, [ 'transitionend',
+ 'transitioncancel' ]);
div.style.left = '0px';
div.style.transition = 'left 100s';
@@ -46,6 +51,8 @@ promise_test(function(t) {
promise_test(function(t) {
var div = addDiv(t);
+ var watcher = new EventWatcher(t, div, [ 'transitionend',
+ 'transitioncancel' ]);
div.style.left = '0px';
div.style.width = '0px';
@@ -65,6 +72,8 @@ promise_test(function(t) {
promise_test(function(t) {
var div = addDiv(t);
+ var watcher = new EventWatcher(t, div, [ 'transitionend',
+ 'transitioncancel' ]);
div.style.left = '0px';
div.style.width = '0px';
diff --git a/dom/animation/test/css-transitions/test_csstransition-events.html b/dom/animation/test/css-transitions/test_event-dispatch.html
index 92559ad679..c90431cd18 100644
--- a/dom/animation/test/css-transitions/test_csstransition-events.html
+++ b/dom/animation/test/css-transitions/test_event-dispatch.html
@@ -9,6 +9,6 @@ setup({explicit_done: true});
SpecialPowers.pushPrefEnv(
{ "set": [["dom.animations-api.core.enabled", true]]},
function() {
- window.open("file_csstransition-events.html");
+ window.open("file_event-dispatch.html");
});
</script>
diff --git a/dom/animation/test/mochitest.ini b/dom/animation/test/mochitest.ini
index 07e04d4d61..db6dffada0 100644
--- a/dom/animation/test/mochitest.ini
+++ b/dom/animation/test/mochitest.ini
@@ -30,11 +30,11 @@ support-files =
css-transitions/file_animation-pausing.html
css-transitions/file_animation-ready.html
css-transitions/file_animation-starttime.html
- css-transitions/file_csstransition-events.html
css-transitions/file_csstransition-transitionproperty.html
css-transitions/file_document-get-animations.html
css-transitions/file_effect-target.html
css-transitions/file_element-get-animations.html
+ css-transitions/file_event-dispatch.html
css-transitions/file_keyframeeffect-getkeyframes.html
css-transitions/file_pseudoElement-get-animations.html
css-transitions/file_setting-effect.html
@@ -86,11 +86,11 @@ support-files =
[css-transitions/test_animation-pausing.html]
[css-transitions/test_animation-ready.html]
[css-transitions/test_animation-starttime.html]
-[css-transitions/test_csstransition-events.html]
[css-transitions/test_csstransition-transitionproperty.html]
[css-transitions/test_document-get-animations.html]
[css-transitions/test_effect-target.html]
[css-transitions/test_element-get-animations.html]
+[css-transitions/test_event-dispatch.html]
[css-transitions/test_keyframeeffect-getkeyframes.html]
[css-transitions/test_pseudoElement-get-animations.html]
[css-transitions/test_setting-effect.html]
diff --git a/layout/style/test/test_animations_event_handler_attribute.html b/layout/style/test/test_animations_event_handler_attribute.html
index 23a749daa8..036a77779f 100644
--- a/layout/style/test/test_animations_event_handler_attribute.html
+++ b/layout/style/test/test_animations_event_handler_attribute.html
@@ -88,11 +88,12 @@ checkReceivedEvents("animationend", targets);
targets.forEach(div => { div.remove(); });
-// 2. Test CSS Transition event handlers.
+// 2a. Test CSS Transition event handlers (without transitioncancel)
var targets = createAndRegisterTargets([ 'ontransitionrun',
'ontransitionstart',
- 'ontransitionend' ]);
+ 'ontransitionend',
+ 'ontransitioncancel' ]);
targets.forEach(div => {
div.style.transition = 'margin-left 100ms 200ms';
getComputedStyle(div).marginLeft; // flush
@@ -111,6 +112,31 @@ checkReceivedEvents("transitionend", targets);
targets.forEach(div => { div.remove(); });
+// 2b. Test CSS Transition cancel event handler.
+
+var targets = createAndRegisterTargets([ 'ontransitioncancel' ]);
+targets.forEach(div => {
+ div.style.transition = 'margin-left 100ms 200ms';
+ getComputedStyle(div).marginLeft; // flush
+ div.style.marginLeft = "200px";
+ getComputedStyle(div).marginLeft; // flush
+});
+
+advance_clock(200);
+
+targets.forEach(div => {
+ div.style.display = "none"
+});
+getComputedStyle(targets[0]).display; // flush
+
+advance_clock(0);
+checkReceivedEvents("transitioncancel", targets);
+
+advance_clock(100);
+targets.forEach( div => { is(div.receivedEventType, undefined); });
+
+targets.forEach(div => { div.remove(); });
+
// 3. Test prefixed CSS Animation event handlers.
var targets = createAndRegisterTargets([ 'onwebkitanimationstart',
diff --git a/layout/style/test/test_animations_event_order.html b/layout/style/test/test_animations_event_order.html
index f948bf0a5b..7204934d22 100644
--- a/layout/style/test/test_animations_event_order.html
+++ b/layout/style/test/test_animations_event_order.html
@@ -48,7 +48,8 @@ var gDisplay = document.getElementById('display');
'animationend',
'transitionrun',
'transitionstart',
- 'transitionend' ]
+ 'transitionend',
+ 'transitioncancel' ]
.forEach(event =>
gDisplay.addEventListener(event,
event => gEventsReceived.push(event),
@@ -623,6 +624,43 @@ checkEventOrder([ divs[0], 'transitionrun' ],
divs.forEach(div => div.remove());
divs = [];
+// 4j. Test sorting transitions with cancel
+// The order of transitioncancel is based on StyleManager.
+// So this test looks like wrong result at a glance. However
+// the gecko will cancel div1's transition before div2 in this case.
+
+divs = [ document.createElement('div'),
+ document.createElement('div') ];
+divs.forEach((div, i) => {
+ gDisplay.appendChild(div);
+ div.style.marginLeft = '0px';
+ div.setAttribute('id', 'div' + i);
+});
+
+divs[0].style.transition = 'margin-left 10s 5s';
+divs[1].style.transition = 'margin-left 10s';
+
+getComputedStyle(divs[0]).marginLeft;
+divs.forEach(div => div.style.marginLeft = '100px');
+getComputedStyle(divs[0]).marginLeft;
+
+advance_clock(0);
+advance_clock(5 * 1000);
+divs.forEach(div => div.style.display = 'none' );
+getComputedStyle(divs[0]).display;
+advance_clock(10 * 1000);
+
+checkEventOrder([ divs[0], 'transitionrun' ],
+ [ divs[1], 'transitionrun' ],
+ [ divs[1], 'transitionstart' ],
+ [ divs[0], 'transitionstart' ],
+ [ divs[1], 'transitioncancel' ],
+ [ divs[0], 'transitioncancel' ],
+ 'Simultaneous transitionrun/start/cancel on siblings');
+
+divs.forEach(div => div.remove());
+divs = [];
+
SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
</script>