| <!DOCTYPE html> | 
 | <meta charset=utf-8> | 
 | <title>Setting the playback rate of an animation that is using a ScrollTimeline</title> | 
 | <link rel="help" href="https://drafts.csswg.org/web-animations/#setting-the-playback-rate-of-an-animation"> | 
 | <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); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  // this forces a layout which results in an active timeline | 
 |  scroller.scrollTop = 0; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  | 
 |  animation.playbackRate = 0.5; | 
 |  animation.play(); | 
 |  | 
 |  assert_percents_equal(animation.currentTime, 0, | 
 |  'Zero current time is not affected by playbackRate change.'); | 
 |  }, 'Zero current time is not affected by playbackRate set while the ' + | 
 |  'animation is in idle state.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  // this forces a layout which results in an active timeline | 
 |  scroller.scrollTop = 0; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  | 
 |  animation.play(); | 
 |  animation.playbackRate = 0.5; | 
 |  | 
 |  assert_percents_equal(animation.currentTime, 0, | 
 |  'Zero current time is not affected by playbackRate change.'); | 
 |  }, 'Zero current time is not affected by playbackRate set while the ' + | 
 |  'animation is in play-pending state.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  scroller.scrollTop = 0.2 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  | 
 |  animation.playbackRate = 0.5; | 
 |  animation.play(); | 
 |  await animation.ready; | 
 |  assert_percents_equal(animation.currentTime, 10, | 
 |  'Initial current time is scaled by playbackRate change.'); | 
 |  }, 'Initial current time is scaled by playbackRate set while ' + | 
 |  'scroll-linked animation is in running state.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  const playbackRate = 2; | 
 |  | 
 |  scroller.scrollTop = 0.2 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  | 
 |  animation.play(); | 
 |  await animation.ready; | 
 |  // Set playback rate while the animation is playing. | 
 |  animation.playbackRate = playbackRate; | 
 |  assert_percents_equal(animation.currentTime, 20, | 
 |  'The current time should stay unaffected by setting playback rate.'); | 
 |  }, 'The current time is not affected by playbackRate set while the ' + | 
 |  'scroll-linked animation is in play state.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  | 
 |  // Set playback rate while the animation is in 'idle' state. | 
 |  animation.playbackRate = 2; | 
 |  animation.play(); | 
 |  await animation.ready; | 
 |  scroller.scrollTop = 0.2 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  | 
 |  assert_percents_equal(animation.currentTime, 40, | 
 |  'The current time should increase two times faster ' + | 
 |  'than timeline time.'); | 
 |  }, 'The playback rate set before scroll-linked animation started playing ' + | 
 |  'affects the rate of progress of the current time'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  animation.play(); | 
 |  | 
 |  await animation.ready; | 
 |  | 
 |  animation.playbackRate = 2; | 
 |  scroller.scrollTop = 0.25 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  | 
 |  assert_percents_equal( | 
 |  animation.currentTime, | 
 |  animation.timeline.currentTime.value * animation.playbackRate, | 
 |  'The current time should increase two times faster than timeline time'); | 
 |  }, 'The playback rate affects the rate of progress of the current time' + | 
 |  ' when scrolling'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  scroller.scrollTop = 0.25 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  animation.play(); | 
 |  | 
 |  animation.playbackRate = 2; | 
 |  | 
 |  assert_equals(animation.playState, "running"); | 
 |  assert_true(animation.pending); | 
 |  assert_percents_equal( | 
 |  animation.currentTime, animation.timeline.currentTime); | 
 |  }, 'Setting the playback rate while play-pending preserves the current time' + | 
 |  ' from scrollTimeline.'); | 
 |  | 
 |  test(t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  animation.play(); | 
 |  animation.currentTime = CSSNumericValue.parse("25%"); | 
 |  animation.playbackRate = 2; | 
 |  | 
 |  assert_equals(animation.playState, "running"); | 
 |  assert_true(animation.pending); | 
 |  assert_percents_equal(animation.currentTime, 25); | 
 |  }, 'Setting the playback rate while play-pending preserves the set current' + | 
 |  ' time.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  scroller.scrollTop = 0.25 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  animation.play(); | 
 |  | 
 |  await animation.ready; | 
 |  animation.playbackRate = 2; | 
 |  | 
 |  assert_percents_equal( | 
 |  animation.currentTime, animation.timeline.currentTime); | 
 |  }, 'Setting the playback rate while playing preserves the current time' + | 
 |  ' from scrollTimeline.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  | 
 |  /* Wait for animation frame is here for now to avoid a renderer crash | 
 |  caused by crbug.com/1042924. Once that is fixed, these can be removed */ | 
 |  await waitForAnimationFrames(2); | 
 |  | 
 |  animation.play(); | 
 |  | 
 |  animation.currentTime = CSSNumericValue.parse("25%"); | 
 |  await animation.ready; | 
 |  animation.playbackRate = 2; | 
 |  | 
 |  assert_percents_equal(animation.currentTime, 25); | 
 |  }, 'Setting the playback rate while playing preserves the set current time.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  animation.playbackRate = -1; | 
 |  scroller.scrollTop = 0.3 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  animation.play(); | 
 |  | 
 |  await animation.ready; | 
 |  const expectedCurrentTime = 100 - animation.timeline.currentTime.value; | 
 |  assert_percents_equal(animation.currentTime, expectedCurrentTime); | 
 |  }, 'Negative initial playback rate should correctly modify initial current' + | 
 |  ' time.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  scroller.scrollTop = 0.5 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  animation.play(); | 
 |  | 
 |  await animation.ready; | 
 |  const startingTimelineTime = animation.timeline.currentTime; | 
 |  const startingCurrentTime = animation.currentTime; | 
 |  assert_percents_equal(startingCurrentTime, startingTimelineTime); | 
 |  | 
 |  animation.playbackRate = -1; | 
 |  | 
 |  scroller.scrollTop = 0.8 * maxScroll; | 
 |  await waitForNextFrame(); | 
 |  // -300 = 500 - 800 | 
 |  let timelineDiff = | 
 |  startingTimelineTime.value - animation.timeline.currentTime.value; | 
 |  // 200 = 500 + (-300) | 
 |  let expected = startingCurrentTime.value + timelineDiff; | 
 |  assert_percents_equal(animation.currentTime, expected); | 
 |  | 
 |  scroller.scrollTop = 0.2 * maxScroll; | 
 |  await waitForNextFrame(); | 
 |  // 300 = 500 - 200 | 
 |  timelineDiff = | 
 |  startingTimelineTime.value - animation.timeline.currentTime.value; | 
 |  // 800 = 500 + 300 | 
 |  expected = startingCurrentTime.value + timelineDiff; | 
 |  assert_percents_equal(animation.currentTime, expected); | 
 |  }, 'Reversing the playback rate while playing correctly impacts current' + | 
 |  ' time during future scrolls'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  animation.playbackRate = 0; | 
 |  scroller.scrollTop = 0.3 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  animation.play(); | 
 |  | 
 |  await animation.ready; | 
 |  assert_percents_equal(animation.currentTime, 0); | 
 |  }, 'Zero initial playback rate should correctly modify initial current' + | 
 |  ' time.'); | 
 |  | 
 |  promise_test(async t => { | 
 |  const animation = createScrollLinkedAnimation(t); | 
 |  const scroller = animation.timeline.scrollSource; | 
 |  const maxScroll = scroller.scrollHeight - scroller.clientHeight; | 
 |  scroller.scrollTop = 0.2 * maxScroll; | 
 |  // Wait for new animation frame which allows the timeline to compute new | 
 |  // current time. | 
 |  await waitForNextFrame(); | 
 |  animation.play(); | 
 |  | 
 |  await animation.ready; | 
 |  assert_percents_equal(animation.currentTime, 20); | 
 |  animation.playbackRate = 0; | 
 |  scroller.scrollTop = 0.5 * maxScroll; | 
 |  await waitForNextFrame(); | 
 |  | 
 |  // Ensure that current time does not change. | 
 |  assert_percents_equal(animation.timeline.currentTime, 50); | 
 |  assert_percents_equal(animation.currentTime, 20); | 
 |  }, 'Setting a zero playback rate while running preserves the current time'); | 
 | </script> | 
 | </body> |