| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
 100
 101
 102
 103
 104
 105
 106
 107
 108
 109
 110
 111
 112
 113
 114
 115
 116
 117
 118
 119
 120
 121
 122
 123
 124
 125
 126
 127
 128
 129
 130
 131
 132
 133
 134
 135
 136
 137
 138
 139
 140
 141
 142
 143
 144
 145
 146
 147
 148
 149
 150
 151
 152
 153
 154
 155
 156
 157
 158
 159
 160
 161
 162
 163
 164
 165
 166
 167
 168
 169
 170
 171
 172
 173
 174
 175
 176
 177
 178
 179
 180
 181
 182
 183
 184
 185
 186
 187
 188
 189
 190
 191
 192
 193
 194
 195
 196
 197
 198
 199
 200
 201
 202
 203
 204
 205
 206
 207
 208
 209
 210
 211
 212
 213
 214
 
 | <!DOCTYPE html>
<meta charset=utf-8>
<title>Canceling an animation</title>
<link rel="help"
    href="https://drafts.csswg.org/web-animations/#canceling-an-animation-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<script src="testcommon.js"></script>
<style>
.scroller {
  overflow: auto;
  height: 100px;
  width: 100px;
  will-change: transform;
}
.contents {
  height: 1000px;
  width: 100%;
}
</style>
<body>
<script>
'use strict';
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  animation.play();
  animation.cancel();
  assert_equals(animation.startTime, null,
                'The start time of a canceled animation should be unresolved');
  assert_equals(animation.currentTime, null,
                'The hold time of a canceled animation should be unresolved');
}, 'Canceling an animation should cause its start time and hold time to be'
   + ' unresolved');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  animation.play();
  const retPromise = animation.ready.then(() => {
    assert_unreached('ready promise was fulfilled');
  }).catch(err => {
    assert_equals(err.name, 'AbortError',
                  'ready promise is rejected with AbortError');
  });
  animation.cancel();
  return retPromise;
}, 'A play-pending ready promise should be rejected when the animation is'
   + ' canceled');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  animation.play();
  await animation.ready;
  // Make it pause-pending
  animation.pause();
  // We need to store the original ready promise since cancel() will
  // replace it
  const originalPromise = animation.ready;
  animation.cancel();
  await promise_rejects_dom(t, 'AbortError', originalPromise,
                            'Cancel should abort ready promise');
}, 'A pause-pending ready promise should be rejected when the animation is'
   + ' canceled');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  animation.play();
  animation.cancel();
  const promiseResult = await animation.ready;
  assert_equals(promiseResult, animation);
}, 'When an animation is canceled, it should create a resolved Promise');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  animation.play();
  const promise = animation.ready;
  animation.cancel();
  assert_not_equals(animation.ready, promise);
  promise_rejects_dom(t, 'AbortError', promise,
                      'Cancel should abort ready promise');
}, 'The ready promise should be replaced when the animation is canceled');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  assert_equals(animation.playState, 'idle',
                'The animation should be initially idle');
  animation.finished.then(t.step_func(() => {
    assert_unreached('Finished promise should not resolve');
  }), t.step_func(() => {
    assert_unreached('Finished promise should not reject');
  }));
  animation.cancel();
  return waitForAnimationFrames(3);
}, 'The finished promise should NOT be rejected if the animation is already'
   + ' idle');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  assert_equals(animation.playState, 'idle',
                'The animation should be initially idle');
  animation.oncancel = t.step_func(() => {
    assert_unreached('Cancel event should not be fired');
  });
  animation.cancel();
  return waitForAnimationFrames(3);
}, 'The cancel event should NOT be fired if the animation is already idle');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  animation.play();
  animation.effect.target.remove();
  const eventWatcher = new EventWatcher(t, animation, 'cancel');
  await animation.ready;
  animation.cancel();
  await eventWatcher.wait_for('cancel');
  assert_equals(animation.effect.target.parentNode, null,
      'cancel event should be fired for the animation on an orphaned element');
}, 'Canceling an animation should fire cancel event on orphaned element');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  const scroller = animation.timeline.source;
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  animation.play();
  await animation.ready;
  // Make the scroll timeline inactive.
  scroller.style.overflow = 'visible';
  scroller.scrollTop;
  await waitForNextFrame();
  assert_equals(animation.timeline.currentTime, null,
                'Sanity check the timeline is inactive.');
  animation.cancel();
  assert_equals(animation.startTime, null,
                'The start time of a canceled animation should be unresolved');
  assert_equals(animation.currentTime, null,
              'The current time of a canceled animation should be unresolved');
}, 'Canceling an animation with inactive timeline should cause its start time'
   + ' and hold time to be unresolved');
promise_test(async t => {
  const animation = createScrollLinkedAnimation(t);
  const scroller = animation.timeline.source;
  // Wait for new animation frame which allows the timeline to compute new
  // current time.
  await waitForNextFrame();
  animation.play();
  await animation.ready;
  // Make the scroll timeline inactive.
  scroller.style.overflow = 'visible';
  scroller.scrollTop;
  await waitForNextFrame();
  assert_equals(animation.timeline.currentTime, null,
                'Sanity check the timeline is inactive.');
  const eventWatcher = new EventWatcher(t, animation, 'cancel');
  animation.cancel();
  const cancelEvent = await eventWatcher.wait_for('cancel');
  assert_equals(cancelEvent.currentTime, null,
      'event.currentTime should be unresolved when the timeline is inactive.');
  assert_equals(cancelEvent.timelineTime, null,
      'event.timelineTime should be unresolved when the timeline is inactive');
}, 'oncancel event is fired when the timeline is inactive.');
</script>
</body>
 |