tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

test_running_on_compositor.html (63477B)


      1 <!doctype html>
      2 <head>
      3 <meta charset=utf-8>
      4 <title>Bug 1045994 - Add a chrome-only property to inspect if an animation is
      5       running on the compositor or not</title>
      6 <script type="application/javascript" src="../testharness.js"></script>
      7 <script type="application/javascript" src="../testharnessreport.js"></script>
      8 <script type="application/javascript" src="../testcommon.js"></script>
      9 <style>
     10 @keyframes anim {
     11  to { transform: translate(100px) }
     12 }
     13 @keyframes transform-starts-with-none {
     14    0% { transform: none }
     15   99% { transform: none }
     16  100% { transform: translate(100px) }
     17 }
     18 @keyframes opacity {
     19  to { opacity: 0 }
     20 }
     21 @keyframes zIndex_and_translate {
     22  to { z-index: 999; transform: translate(100px); }
     23 }
     24 @keyframes z-index {
     25  to { z-index: 999; }
     26 }
     27 @keyframes rotate {
     28  from { transform: rotate(0deg); }
     29  to { transform: rotate(360deg); }
     30 }
     31 @keyframes rotate-and-opacity {
     32  from { transform: rotate(0deg); opacity: 1;}
     33  to { transform: rotate(360deg); opacity: 0;}
     34 }
     35 div {
     36  /* Element needs geometry to be eligible for layerization */
     37  width: 100px;
     38  height: 100px;
     39  background-color: white;
     40 }
     41 </style>
     42 </head>
     43 <body>
     44 <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=1045994"
     45  target="_blank">Mozilla Bug 1045994</a>
     46 <div id="log"></div>
     47 <script>
     48 'use strict';
     49 
     50 /**
     51 * Test for bug 1045994 - Add a chrome-only property to inspect if an
     52 * animation is running on the compositor or not
     53 */
     54 
     55 const omtaEnabled = isOMTAEnabled();
     56 
     57 function assert_animation_is_running_on_compositor(animation, desc) {
     58  assert_equals(animation.isRunningOnCompositor, omtaEnabled,
     59                desc + ' at ' + animation.currentTime + 'ms');
     60 }
     61 
     62 function assert_animation_is_not_running_on_compositor(animation, desc) {
     63  assert_equals(animation.isRunningOnCompositor, false,
     64                desc + ' at ' + animation.currentTime + 'ms');
     65 }
     66 
     67 promise_test(async t => {
     68  // FIXME: When we implement Element.animate, use that here instead of CSS
     69  // so that we remove any dependency on the CSS mapping.
     70  var div = addDiv(t, { style: 'animation: anim 100s' });
     71  var animation = div.getAnimations()[0];
     72 
     73  // If the animation starts at the current timeline time, we need to wait for
     74  // one more frame to avoid receiving the fake timer-based MozAfterPaint event.
     75  // FIXME: Bug 1419226: Drop this 'animation.ready' and 'waitForFrame'.  Once
     76  // MozAfterPaint is fired reliably, we just need to wait for a MozAfterPaint
     77  // here.
     78  await animation.ready;
     79 
     80  if (animationStartsRightNow(animation)) {
     81    await waitForNextFrame();
     82  }
     83 
     84  await waitForPaints();
     85 
     86  assert_animation_is_running_on_compositor(animation,
     87     'Animation reports that it is running on the compositor'
     88     + ' during playback');
     89 
     90  div.style.animationPlayState = 'paused';
     91 
     92  await animation.ready;
     93  await waitForPaints();
     94 
     95  assert_animation_is_not_running_on_compositor(animation,
     96     'Animation reports that it is NOT running on the compositor'
     97     + ' when paused');
     98 }, 'Basic test');
     99 
    100 promise_test(async t => {
    101  var div = addDiv(t, { style: 'animation: z-index 100s' });
    102  var animation = div.getAnimations()[0];
    103 
    104  await waitForPaints();
    105 
    106  assert_animation_is_not_running_on_compositor(animation,
    107     'Animation reports that it is NOT running on the compositor'
    108     + ' for animation of "z-index"');
    109 }, 'isRunningOnCompositor is false for animation of "z-index"');
    110 
    111 promise_test(async t => {
    112  var div = addDiv(t, { style: 'animation: zIndex_and_translate 100s' });
    113  var animation = div.getAnimations()[0];
    114 
    115  await waitForPaints();
    116 
    117  assert_animation_is_running_on_compositor(animation,
    118     'Animation reports that it is running on the compositor'
    119      + ' when the animation has two properties, where one can run'
    120      + ' on the compositor, the other cannot');
    121 }, 'isRunningOnCompositor is true if the animation has at least one ' +
    122   'property can run on compositor');
    123 
    124 promise_test(async t => {
    125  var div = addDiv(t, { style: 'animation: anim 100s' });
    126  var animation = div.getAnimations()[0];
    127 
    128  await waitForPaints();
    129 
    130  animation.pause();
    131  await animation.ready;
    132 
    133  await waitForPaints();
    134 
    135  assert_animation_is_not_running_on_compositor(animation,
    136     'Animation reports that it is NOT running on the compositor'
    137     + ' when animation.pause() is called');
    138 }, 'isRunningOnCompositor is false when the animation.pause() is called');
    139 
    140 promise_test(async t => {
    141  var div = addDiv(t, { style: 'animation: anim 100s' });
    142  var animation = div.getAnimations()[0];
    143 
    144  await waitForPaints();
    145 
    146  animation.finish();
    147  assert_animation_is_not_running_on_compositor(animation,
    148     'Animation reports that it is NOT running on the compositor'
    149     + ' immediately after animation.finish() is called');
    150  // Check that we don't set the flag back again on the next tick.
    151  await waitForFrame();
    152 
    153  assert_animation_is_not_running_on_compositor(animation,
    154     'Animation reports that it is NOT running on the compositor'
    155     + ' on the next tick after animation.finish() is called');
    156 }, 'isRunningOnCompositor is false when the animation.finish() is called');
    157 
    158 promise_test(async t => {
    159  var div = addDiv(t, { style: 'animation: anim 100s' });
    160  var animation = div.getAnimations()[0];
    161 
    162  await waitForPaints();
    163 
    164  animation.currentTime = 100 * MS_PER_SEC;
    165  assert_animation_is_not_running_on_compositor(animation,
    166     'Animation reports that it is NOT running on the compositor'
    167     + ' immediately after manually seeking the animation to the end');
    168  // Check that we don't set the flag back again on the next tick.
    169  await waitForFrame();
    170 
    171  assert_animation_is_not_running_on_compositor(animation,
    172     'Animation reports that it is NOT running on the compositor'
    173     + ' on the next tick after manually seeking the animation to the end');
    174 }, 'isRunningOnCompositor is false when manually seeking the animation to ' +
    175   'the end');
    176 
    177 promise_test(async t => {
    178  var div = addDiv(t, { style: 'animation: anim 100s' });
    179  var animation = div.getAnimations()[0];
    180 
    181  await waitForPaints();
    182 
    183  animation.cancel();
    184  assert_animation_is_not_running_on_compositor(animation,
    185     'Animation reports that it is NOT running on the compositor'
    186     + ' immediately after animation.cancel() is called');
    187  // Check that we don't set the flag back again on the next tick.
    188  await waitForFrame();
    189 
    190  assert_animation_is_not_running_on_compositor(animation,
    191     'Animation reports that it is NOT running on the compositor'
    192     + ' on the next tick after animation.cancel() is called');
    193 }, 'isRunningOnCompositor is false when animation.cancel() is called');
    194 
    195 // This is to test that we don't simply clobber the flag when ticking
    196 // animations and then set it again during painting.
    197 promise_test(async t => {
    198  var div = addDiv(t, { style: 'animation: anim 100s' });
    199  var animation = div.getAnimations()[0];
    200 
    201  await waitForPaints();
    202 
    203  await new Promise(resolve => {
    204    window.requestAnimationFrame(() => {
    205      t.step(() => {
    206        assert_animation_is_running_on_compositor(animation,
    207          'Animation reports that it is running on the compositor'
    208           + ' in requestAnimationFrame callback');
    209      });
    210 
    211      resolve();
    212    });
    213  });
    214 }, 'isRunningOnCompositor is true in requestAnimationFrame callback');
    215 
    216 promise_test(async t => {
    217  var div = addDiv(t, { style: 'animation: anim 100s' });
    218  var animation = div.getAnimations()[0];
    219 
    220  await waitForPaints();
    221 
    222  await new Promise(resolve => {
    223    var observer = new MutationObserver(records => {
    224      var changedAnimation;
    225 
    226      records.forEach(record => {
    227        changedAnimation =
    228          record.changedAnimations.find(changedAnim => {
    229            return changedAnim == animation;
    230          });
    231      });
    232 
    233      t.step(() => {
    234        assert_true(!!changedAnimation, 'The animation should be recorded '
    235          + 'as one of the changedAnimations');
    236 
    237        assert_animation_is_running_on_compositor(animation,
    238          'Animation reports that it is running on the compositor'
    239           + ' in MutationObserver callback');
    240      });
    241 
    242      resolve();
    243    });
    244    observer.observe(div, { animations: true, subtree: false });
    245    t.add_cleanup(() => {
    246      observer.disconnect();
    247    });
    248    div.style.animationDuration = "200s";
    249  });
    250 }, 'isRunningOnCompositor is true in MutationObserver callback');
    251 
    252 // This is to test that we don't temporarily clear the flag when forcing
    253 // an unthrottled sample.
    254 promise_test(async t => {
    255  var div = addDiv(t, { style: 'animation: rotate 100s' });
    256  var animation = div.getAnimations()[0];
    257 
    258  await waitForPaints();
    259 
    260  await new Promise(resolve => {
    261    var timeAtStart = window.performance.now();
    262    function handleFrame() {
    263      t.step(() => {
    264        assert_animation_is_running_on_compositor(animation,
    265          'Animation reports that it is running on the compositor'
    266           + ' in requestAnimationFrame callback');
    267      });
    268 
    269      // we have to wait at least 200ms because this animation is
    270      // unthrottled on every 200ms.
    271      // See https://hg.mozilla.org/mozilla-central/file/cafb1c90f794/layout/style/AnimationCommon.cpp#l863
    272      if (window.performance.now() - timeAtStart > 200) {
    273        resolve();
    274        return;
    275      }
    276      window.requestAnimationFrame(handleFrame);
    277    }
    278    window.requestAnimationFrame(handleFrame);
    279  });
    280 }, 'isRunningOnCompositor remains true in requestAnimationFrameCallback for ' +
    281   'overflow animation');
    282 
    283 promise_test(async t => {
    284  var div = addDiv(t, { style: 'transition: opacity 100s; opacity: 1' });
    285 
    286  getComputedStyle(div).opacity;
    287 
    288  div.style.opacity = 0;
    289  var animation = div.getAnimations()[0];
    290 
    291  await waitForPaints();
    292 
    293  assert_animation_is_running_on_compositor(animation,
    294     'Transition reports that it is running on the compositor'
    295     + ' during playback for opacity transition');
    296 }, 'isRunningOnCompositor for transitions');
    297 
    298 promise_test(async t => {
    299  var div = addDiv(t, { style: 'animation: rotate-and-opacity 100s; ' +
    300                               'backface-visibility: hidden; ' +
    301                               'transform: none !important;' });
    302  var animation = div.getAnimations()[0];
    303 
    304  await waitForPaints();
    305 
    306  assert_animation_is_running_on_compositor(animation,
    307     'If an animation has a property that can run on the compositor and a '
    308     + 'property that cannot (due to Gecko limitations) but where the latter'
    309     + 'property is overridden in the CSS cascade, the animation should '
    310     + 'still report that it is running on the compositor');
    311 }, 'isRunningOnCompositor is true when a property that would otherwise block ' +
    312   'running on the compositor is overridden in the CSS cascade');
    313 
    314 promise_test(async t => {
    315  var animation = addDivAndAnimate(t,
    316                                   {},
    317                                   { opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
    318 
    319  await waitForPaints();
    320 
    321  assert_animation_is_running_on_compositor(animation,
    322    'Animation reports that it is running on the compositor');
    323 
    324  animation.currentTime = 150 * MS_PER_SEC;
    325  animation.effect.updateTiming({ duration: 100 * MS_PER_SEC });
    326 
    327  assert_animation_is_not_running_on_compositor(animation,
    328     'Animation reports that it is NOT running on the compositor'
    329     + ' when the animation is set a shorter duration than current time');
    330 }, 'animation is immediately removed from compositor' +
    331   'when the duration is made shorter than the current time');
    332 
    333 promise_test(async t => {
    334  var animation = addDivAndAnimate(t,
    335                                   {},
    336                                   { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
    337 
    338  await waitForPaints();
    339 
    340  assert_animation_is_running_on_compositor(animation,
    341    'Animation reports that it is running on the compositor');
    342 
    343  animation.currentTime = 500 * MS_PER_SEC;
    344 
    345  assert_animation_is_not_running_on_compositor(animation,
    346    'Animation reports that it is NOT running on the compositor'
    347    + ' when finished');
    348 
    349  animation.effect.updateTiming({ duration: 1000 * MS_PER_SEC });
    350  await waitForFrame();
    351 
    352  assert_animation_is_running_on_compositor(animation,
    353    'Animation reports that it is running on the compositor'
    354    + ' when restarted');
    355 }, 'animation is added to compositor' +
    356   ' when the duration is made longer than the current time');
    357 
    358 promise_test(async t => {
    359  var animation = addDivAndAnimate(t,
    360                                   {},
    361                                   { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
    362 
    363  await waitForPaints();
    364 
    365  assert_animation_is_running_on_compositor(animation,
    366    'Animation reports that it is running on the compositor');
    367 
    368  animation.effect.updateTiming({ endDelay: 100 * MS_PER_SEC });
    369 
    370  assert_animation_is_running_on_compositor(animation,
    371    'Animation reports that it is running on the compositor'
    372    + ' when endDelay is changed');
    373 
    374  animation.currentTime = 110 * MS_PER_SEC;
    375  await waitForFrame();
    376 
    377  assert_animation_is_not_running_on_compositor(animation,
    378    'Animation reports that it is NOT running on the compositor'
    379    + ' when currentTime is during endDelay');
    380 }, 'animation is removed from compositor' +
    381   ' when current time is made longer than the duration even during endDelay');
    382 
    383 promise_test(async t => {
    384  var animation = addDivAndAnimate(t,
    385                                   {},
    386                                   { opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
    387 
    388  await waitForPaints();
    389 
    390  assert_animation_is_running_on_compositor(animation,
    391    'Animation reports that it is running on the compositor');
    392 
    393  animation.effect.updateTiming({ endDelay: -200 * MS_PER_SEC });
    394  await waitForFrame();
    395 
    396  assert_animation_is_not_running_on_compositor(animation,
    397    'Animation reports that it is NOT running on the compositor'
    398    + ' when endTime is negative value');
    399 }, 'animation is removed from compositor' +
    400   ' when endTime is negative value');
    401 
    402 promise_test(async t => {
    403  var animation = addDivAndAnimate(t,
    404                                   {},
    405                                   { opacity: [ 0, 1 ] }, 200 * MS_PER_SEC);
    406 
    407  await waitForPaints();
    408 
    409  assert_animation_is_running_on_compositor(animation,
    410    'Animation reports that it is running on the compositor');
    411 
    412  animation.effect.updateTiming({ endDelay: -100 * MS_PER_SEC });
    413  await waitForFrame();
    414 
    415  assert_animation_is_running_on_compositor(animation,
    416    'Animation reports that it is running on the compositor'
    417    + ' when endTime is positive and endDelay is negative');
    418  animation.currentTime = 110 * MS_PER_SEC;
    419  await waitForFrame();
    420 
    421  assert_animation_is_not_running_on_compositor(animation,
    422    'Animation reports that it is NOT running on the compositor'
    423    + ' when currentTime is after endTime');
    424 }, 'animation is NOT running on compositor' +
    425   ' when endTime is positive and endDelay is negative');
    426 
    427 promise_test(async t => {
    428  var effect = new KeyframeEffect(null,
    429                                  { opacity: [ 0, 1 ] },
    430                                  100 * MS_PER_SEC);
    431  var animation = new Animation(effect, document.timeline);
    432  animation.play();
    433 
    434  var div = addDiv(t);
    435 
    436  await waitForPaints();
    437 
    438  assert_animation_is_not_running_on_compositor(animation,
    439                'Animation with null target reports that it is not running ' +
    440                'on the compositor');
    441 
    442  animation.effect.target = div;
    443  await waitForFrame();
    444 
    445  assert_animation_is_running_on_compositor(animation,
    446                'Animation reports that it is running on the compositor ' +
    447                'after setting a valid target');
    448 }, 'animation is added to the compositor when setting a valid target');
    449 
    450 promise_test(async t => {
    451  var div = addDiv(t);
    452  var animation = div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
    453 
    454  await waitForPaints();
    455 
    456  assert_animation_is_running_on_compositor(animation,
    457                'Animation reports that it is running on the compositor');
    458 
    459  animation.effect.target = null;
    460  assert_animation_is_not_running_on_compositor(animation,
    461                'Animation reports that it is NOT running on the ' +
    462                'compositor after setting null target');
    463 }, 'animation is removed from the compositor when setting null target');
    464 
    465 promise_test(async t => {
    466  var div = addDiv(t);
    467  var animation = div.animate({ opacity: [ 0, 1 ] },
    468                              { duration: 100 * MS_PER_SEC,
    469                                delay: 100 * MS_PER_SEC,
    470                                fill: 'backwards' });
    471 
    472  await waitForPaints();
    473 
    474  assert_animation_is_running_on_compositor(animation,
    475                'Animation with fill:backwards in delay phase reports ' +
    476                'that it is running on the compositor');
    477 
    478  animation.currentTime = 100 * MS_PER_SEC;
    479  await waitForFrame();
    480 
    481  assert_animation_is_running_on_compositor(animation,
    482      'Animation with fill:backwards in delay phase reports ' +
    483      'that it is running on the compositor after delay phase');
    484 }, 'animation with fill:backwards in delay phase is running on the ' +
    485   ' compositor while it is in delay phase');
    486 
    487 promise_test(async t => {
    488  const animation = addDiv(t).animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
    489  animation.playbackRate = -1;
    490  animation.currentTime = 200 * MS_PER_SEC;
    491 
    492  await waitForPaints();
    493 
    494  assert_animation_is_running_on_compositor(animation,
    495                'Animation with negative playback rate is runnning on the'
    496                + ' compositor even before it reaches the active interval');
    497 }, 'animation with negative playback rate is sent to the compositor even in'
    498   + ' after phase');
    499 
    500 promise_test(async t => {
    501  var div = addDiv(t);
    502  var animation = div.animate([{ opacity: 1, offset: 0 },
    503                               { opacity: 1, offset: 0.99 },
    504                               { opacity: 0, offset: 1 }], 100 * MS_PER_SEC);
    505 
    506  var another = addDiv(t);
    507 
    508  await waitForPaints();
    509 
    510  assert_animation_is_running_on_compositor(animation,
    511                'Opacity animation on a 100% opacity keyframe reports ' +
    512                'that it is running on the compositor from the begining');
    513 
    514  animation.effect.target = another;
    515  await waitForFrame();
    516 
    517  assert_animation_is_running_on_compositor(animation,
    518                'Opacity animation on a 100% opacity keyframe keeps ' +
    519                'running on the compositor after changing the target ' +
    520                'element');
    521 }, '100% opacity animations with keeps running on the ' +
    522   'compositor after changing the target element');
    523 
    524 promise_test(async t => {
    525  var div = addDiv(t);
    526  var animation = div.animate({ color: ['red', 'black'] }, 100 * MS_PER_SEC);
    527 
    528  await waitForPaints();
    529 
    530  assert_animation_is_not_running_on_compositor(animation,
    531                'Color animation reports that it is not running on the ' +
    532                'compositor');
    533 
    534  animation.effect.setKeyframes([{ opacity: 1, offset: 0 },
    535                                 { opacity: 1, offset: 0.99 },
    536                                 { opacity: 0, offset: 1 }]);
    537  await waitForFrame();
    538 
    539  assert_animation_is_running_on_compositor(animation,
    540                '100% opacity animation set by using setKeyframes reports ' +
    541                'that it is running on the compositor');
    542 }, '100% opacity animation set up by converting an existing animation with ' +
    543   'cannot be run on the compositor, is running on the compositor');
    544 
    545 promise_test(async t => {
    546  var div = addDiv(t);
    547  var animation = div.animate({ color: ['red', 'black'] }, 100 * MS_PER_SEC);
    548  var effect = new KeyframeEffect(div,
    549                                  [{ opacity: 1, offset: 0 },
    550                                   { opacity: 1, offset: 0.99 },
    551                                   { opacity: 0, offset: 1 }],
    552                                  100 * MS_PER_SEC);
    553 
    554  await waitForPaints();
    555 
    556  assert_animation_is_not_running_on_compositor(animation,
    557                'Color animation reports that it is not running on the ' +
    558                'compositor');
    559 
    560  animation.effect = effect;
    561  await waitForFrame();
    562 
    563  assert_animation_is_running_on_compositor(animation,
    564                '100% opacity animation set up by changing effects reports ' +
    565                'that it is running on the compositor');
    566 }, '100% opacity animation set up by changing the effects on an existing ' +
    567   'animation which cannot be run on the compositor, is running on the ' +
    568   'compositor');
    569 
    570 promise_test(async t => {
    571  var div = addDiv(t, { style: "opacity: 1 ! important" });
    572 
    573  var animation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
    574 
    575  await waitForPaints();
    576 
    577  assert_animation_is_not_running_on_compositor(animation,
    578    'Opacity animation on an element which has 100% opacity style with ' +
    579    '!important flag reports that it is not running on the compositor');
    580  // Clear important flag from the opacity style on the target element.
    581  div.style.setProperty("opacity", "1", "");
    582  await waitForFrame();
    583 
    584  assert_animation_is_running_on_compositor(animation,
    585    'Opacity animation reports that it is running on the compositor after '
    586    + 'clearing the !important flag');
    587 }, 'Clearing *important* opacity style on the target element sends the ' +
    588   'animation to the compositor');
    589 
    590 promise_test(async t => {
    591  var div = addDiv(t);
    592  var lowerAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
    593  var higherAnimation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
    594 
    595  await waitForPaints();
    596 
    597  assert_animation_is_running_on_compositor(higherAnimation,
    598                'A higher-priority opacity animation on an element ' +
    599                'reports that it is running on the compositor');
    600  assert_animation_is_running_on_compositor(lowerAnimation,
    601                'A lower-priority opacity animation on the same ' +
    602                'element also reports that it is running on the compositor');
    603 }, 'Opacity animations on the same element run on the compositor');
    604 
    605 promise_test(async t => {
    606  var div = addDiv(t, { style: 'transition: opacity 100s; opacity: 1' });
    607 
    608  getComputedStyle(div).opacity;
    609 
    610  div.style.opacity = 0;
    611  getComputedStyle(div).opacity;
    612 
    613  var transition = div.getAnimations()[0];
    614  var animation = div.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
    615 
    616  await waitForPaints();
    617 
    618  assert_animation_is_running_on_compositor(animation,
    619                'An opacity animation on an element reports that' +
    620                'that it is running on the compositor');
    621  assert_animation_is_running_on_compositor(transition,
    622                'An opacity transition on the same element reports that ' +
    623                'it is running on the compositor');
    624 }, 'Both of transition and script animation on the same element run on the ' +
    625   'compositor');
    626 
    627 promise_test(async t => {
    628  var div = addDiv(t);
    629  var importantOpacityElement = addDiv(t, { style: "opacity: 1 ! important" });
    630 
    631  var animation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
    632 
    633  await waitForPaints();
    634 
    635  assert_animation_is_running_on_compositor(animation,
    636                'Opacity animation on an element reports ' +
    637                'that it is running on the compositor');
    638 
    639  animation.effect.target = null;
    640  await waitForFrame();
    641 
    642  assert_animation_is_not_running_on_compositor(animation,
    643                'Animation is no longer running on the compositor after ' +
    644                'removing from the element');
    645  animation.effect.target = importantOpacityElement;
    646  await waitForFrame();
    647 
    648  assert_animation_is_not_running_on_compositor(animation,
    649                'Animation is NOT running on the compositor even after ' +
    650                'being applied to a different element which has an ' +
    651                '!important opacity declaration');
    652 }, 'Animation continues not running on the compositor after being ' +
    653   'applied to an element which has an important declaration and ' +
    654   'having previously been temporarily associated with no target element');
    655 
    656 promise_test(async t => {
    657  var div = addDiv(t);
    658  var another = addDiv(t);
    659 
    660  var lowerAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
    661  var higherAnimation = another.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
    662 
    663  await waitForPaints();
    664 
    665  assert_animation_is_running_on_compositor(lowerAnimation,
    666                'An opacity animation on an element reports that ' +
    667                'it is running on the compositor');
    668  assert_animation_is_running_on_compositor(higherAnimation,
    669                'Opacity animation on a different element reports ' +
    670                'that it is running on the compositor');
    671 
    672  lowerAnimation.effect.target = null;
    673  await waitForFrame();
    674 
    675  assert_animation_is_not_running_on_compositor(lowerAnimation,
    676                'Animation is no longer running on the compositor after ' +
    677                'being removed from the element');
    678  lowerAnimation.effect.target = another;
    679  await waitForFrame();
    680 
    681  assert_animation_is_running_on_compositor(lowerAnimation,
    682                'A lower-priority animation begins running ' +
    683                'on the compositor after being applied to an element ' +
    684                'which has a higher-priority animation');
    685  assert_animation_is_running_on_compositor(higherAnimation,
    686                'A higher-priority animation continues to run on the ' +
    687                'compositor even after a lower-priority animation is ' +
    688                'applied to the same element');
    689 }, 'Animation begins running on the compositor after being applied ' +
    690   'to an element which has a higher-priority animation and after ' +
    691   'being temporarily associated with no target element');
    692 
    693 promise_test(async t => {
    694  var div = addDiv(t);
    695  var another = addDiv(t);
    696 
    697  var lowerAnimation = div.animate({ opacity: [1, 0] }, 100 * MS_PER_SEC);
    698  var higherAnimation = another.animate({ opacity: [0, 1] }, 100 * MS_PER_SEC);
    699 
    700  await waitForPaints();
    701 
    702  assert_animation_is_running_on_compositor(lowerAnimation,
    703                'An opacity animation on an element reports that ' +
    704                'it is running on the compositor');
    705  assert_animation_is_running_on_compositor(higherAnimation,
    706                'Opacity animation on a different element reports ' +
    707                'that it is running on the compositor');
    708 
    709  higherAnimation.effect.target = null;
    710  await waitForFrame();
    711 
    712  assert_animation_is_not_running_on_compositor(higherAnimation,
    713                'Animation is no longer running on the compositor after ' +
    714                'being removed from the element');
    715  higherAnimation.effect.target = div;
    716  await waitForFrame();
    717 
    718  assert_animation_is_running_on_compositor(lowerAnimation,
    719                'Animation continues running on the compositor after ' +
    720                'a higher-priority animation applied to the same element');
    721  assert_animation_is_running_on_compositor(higherAnimation,
    722                'A higher-priority animation begins to running on the ' +
    723                'compositor after being applied to an element which has ' +
    724                'a lower-priority-animation');
    725 }, 'Animation begins running on the compositor after being applied ' +
    726   'to an element which has a lower-priority animation once after ' +
    727   'disassociating with an element');
    728 
    729 var delayPhaseTests = [
    730  {
    731    desc: 'script animation of opacity',
    732    setupAnimation: t => {
    733      return addDiv(t).animate(
    734        { opacity: [0, 1] },
    735        { delay: 100 * MS_PER_SEC, duration: 100 * MS_PER_SEC });
    736    },
    737  },
    738  {
    739    desc: 'script animation of transform',
    740    setupAnimation: t => {
    741      return addDiv(t).animate(
    742        { transform: ['translateX(0px)', 'translateX(100px)'] },
    743        { delay: 100 * MS_PER_SEC, duration: 100 * MS_PER_SEC });
    744    },
    745  },
    746  {
    747    desc: 'CSS animation of opacity',
    748    setupAnimation: t => {
    749      return addDiv(t, { style: 'animation: opacity 100s 100s' })
    750        .getAnimations()[0];
    751    },
    752  },
    753  {
    754    desc: 'CSS animation of transform',
    755    setupAnimation: t => {
    756      return addDiv(t, { style: 'animation: anim 100s 100s' })
    757        .getAnimations()[0];
    758    },
    759  },
    760  {
    761    desc: 'CSS transition of opacity',
    762    setupAnimation: t => {
    763      var div = addDiv(t, { style: 'transition: opacity 100s 100s' });
    764      getComputedStyle(div).opacity;
    765 
    766      div.style.opacity = 0;
    767      return div.getAnimations()[0];
    768    },
    769  },
    770  {
    771    desc: 'CSS transition of transform',
    772    setupAnimation: t => {
    773      var div = addDiv(t, { style: 'transition: transform 100s 100s' });
    774      getComputedStyle(div).transform;
    775 
    776      div.style.transform = 'translateX(100px)';
    777      return div.getAnimations()[0];
    778    },
    779  },
    780 ];
    781 
    782 delayPhaseTests.forEach(test => {
    783  promise_test(async t => {
    784    var animation = test.setupAnimation(t);
    785 
    786    await waitForPaints();
    787 
    788    assert_animation_is_running_on_compositor(animation,
    789       test.desc + ' reports that it is running on the '
    790       + 'compositor even though it is in the delay phase');
    791  }, 'isRunningOnCompositor for ' + test.desc + ' is true even though ' +
    792     'it is in the delay phase');
    793 });
    794 
    795 // The purpose of thie test cases is to check that
    796 // NS_FRAME_MAY_BE_TRANSFORMED flag on the associated nsIFrame persists
    797 // after transform style on the frame is removed.
    798 var delayPhaseWithTransformStyleTests = [
    799  {
    800    desc: 'script animation of transform with transform style',
    801    setupAnimation: t => {
    802      return addDiv(t, { style: 'transform: translateX(10px)' }).animate(
    803        { transform: ['translateX(0px)', 'translateX(100px)'] },
    804        { delay: 100 * MS_PER_SEC, duration: 100 * MS_PER_SEC });
    805    },
    806  },
    807  {
    808    desc: 'CSS animation of transform with transform style',
    809    setupAnimation: t => {
    810      return addDiv(t, { style: 'animation: anim 100s 100s;' +
    811                                'transform: translateX(10px)' })
    812        .getAnimations()[0];
    813    },
    814  },
    815  {
    816    desc: 'CSS transition of transform with transform style',
    817    setupAnimation: t => {
    818      var div = addDiv(t, { style: 'transition: transform 100s 100s;' +
    819                                   'transform: translateX(10px)'});
    820      getComputedStyle(div).transform;
    821 
    822      div.style.transform = 'translateX(100px)';
    823      return div.getAnimations()[0];
    824    },
    825  },
    826 ];
    827 
    828 delayPhaseWithTransformStyleTests.forEach(test => {
    829  promise_test(async t => {
    830    var animation = test.setupAnimation(t);
    831 
    832    await waitForPaints();
    833 
    834    assert_animation_is_running_on_compositor(animation,
    835       test.desc + ' reports that it is running on the '
    836       + 'compositor even though it is in the delay phase');
    837 
    838    // Remove the initial transform style during delay phase.
    839    animation.effect.target.style.transform = 'none';
    840    await animation.ready;
    841 
    842    assert_animation_is_running_on_compositor(animation,
    843       test.desc + ' reports that it keeps running on the '
    844       + 'compositor after removing the initial transform style');
    845  }, 'isRunningOnCompositor for ' + test.desc + ' is true after removing ' +
    846     'the initial transform style during the delay phase');
    847 });
    848 
    849 var startsWithNoneTests = [
    850  {
    851    desc: 'script animation of transform starts with transform:none segment',
    852    setupAnimation: t => {
    853      return addDiv(t).animate(
    854        { transform: ['none', 'none', 'translateX(100px)'] }, 100 * MS_PER_SEC);
    855    },
    856  },
    857  {
    858    desc: 'CSS animation of transform starts with transform:none segment',
    859    setupAnimation: t => {
    860      return addDiv(t,
    861        { style: 'animation: transform-starts-with-none 100s 100s' })
    862          .getAnimations()[0];
    863    },
    864  },
    865 ];
    866 
    867 startsWithNoneTests.forEach(test => {
    868  promise_test(async t => {
    869    var animation = test.setupAnimation(t);
    870 
    871    await waitForPaints();
    872 
    873    assert_animation_is_running_on_compositor(animation,
    874       test.desc + ' reports that it is running on the '
    875       + 'compositor even though it is in transform:none segment');
    876  }, 'isRunningOnCompositor for ' + test.desc + ' is true even though ' +
    877     'it is in transform:none segment');
    878 });
    879 
    880 promise_test(async t => {
    881  var div = addDiv(t, { style: 'opacity: 1 ! important' });
    882 
    883  var animation = div.animate(
    884    { opacity: [0, 1] },
    885    { delay: 100 * MS_PER_SEC, duration: 100 * MS_PER_SEC });
    886 
    887  await waitForPaints();
    888 
    889  assert_animation_is_not_running_on_compositor(animation,
    890    'Opacity animation on an element which has opacity:1 important style'
    891    + 'reports that it is not running on the compositor');
    892  // Clear the opacity style on the target element.
    893  div.style.setProperty("opacity", "1", "");
    894  await waitForFrame();
    895 
    896  assert_animation_is_running_on_compositor(animation,
    897    'Opacity animations reports that it is running on the compositor after '
    898    + 'clearing the opacity style on the element');
    899 }, 'Clearing *important* opacity style on the target element sends the ' +
    900   'animation to the compositor even if the animation is in the delay phase');
    901 
    902 promise_test(async t => {
    903  var opaqueDiv = addDiv(t, { style: 'opacity: 1 ! important' });
    904  var anotherDiv = addDiv(t);
    905 
    906  var animation = opaqueDiv.animate(
    907    { opacity: [0, 1] },
    908    { delay: 100 * MS_PER_SEC, duration: 100 * MS_PER_SEC });
    909 
    910  await waitForPaints();
    911 
    912  assert_animation_is_not_running_on_compositor(animation,
    913    'Opacity animation on an element which has opacity:1 important style'
    914    + 'reports that it is not running on the compositor');
    915  // Changing target element to another element which has no opacity style.
    916  animation.effect.target = anotherDiv;
    917  await waitForFrame();
    918 
    919  assert_animation_is_running_on_compositor(animation,
    920    'Opacity animations reports that it is running on the compositor after '
    921    + 'changing the target element to another elemenent having no '
    922    + 'opacity style');
    923 }, 'Changing target element of opacity animation sends the animation to the ' +
    924   'the compositor even if the animation is in the delay phase');
    925 
    926 promise_test(async t => {
    927  var animation =
    928    addDivAndAnimate(t,
    929                     {},
    930                     { width: ['100px', '200px'] },
    931                     { duration: 100 * MS_PER_SEC, delay: 100 * MS_PER_SEC });
    932 
    933  await waitForPaints();
    934 
    935  assert_animation_is_not_running_on_compositor(animation,
    936    'Width animation reports that it is not running on the compositor '
    937    + 'in the delay phase');
    938  // Changing to property runnable on the compositor.
    939  animation.effect.setKeyframes({ opacity: [0, 1] });
    940  await waitForFrame();
    941 
    942  assert_animation_is_running_on_compositor(animation,
    943    'Opacity animation reports that it is running on the compositor '
    944    + 'after changing the property from width property in the delay phase');
    945 }, 'Dynamic change to a property runnable on the compositor ' +
    946   'in the delay phase');
    947 
    948 promise_test(async t => {
    949  var div = addDiv(t, { style: 'transition: opacity 100s; ' +
    950                               'opacity: 0 !important' });
    951  getComputedStyle(div).opacity;
    952 
    953  div.style.setProperty('opacity', '1', 'important');
    954  getComputedStyle(div).opacity;
    955 
    956  var animation = div.getAnimations()[0];
    957 
    958  await waitForPaints();
    959 
    960  assert_animation_is_running_on_compositor(animation,
    961     'Transition reports that it is running on the compositor even if the ' +
    962     'property is overridden by an !important rule');
    963 }, 'Transitions override important rules');
    964 
    965 promise_test(async t => {
    966  var div = addDiv(t, { style: 'transition: opacity 100s; ' +
    967                               'opacity: 0 !important' });
    968  getComputedStyle(div).opacity;
    969 
    970  div.animate({ opacity: [ 0, 1 ] }, 100 * MS_PER_SEC);
    971 
    972  div.style.setProperty('opacity', '1', 'important');
    973  getComputedStyle(div).opacity;
    974 
    975  var [transition, animation] = div.getAnimations();
    976 
    977  await waitForPaints();
    978 
    979  assert_animation_is_not_running_on_compositor(transition,
    980     'Transition suppressed by an animation which is overridden by an ' +
    981     '!important rule reports that it is NOT running on the compositor');
    982  assert_animation_is_not_running_on_compositor(animation,
    983     'Animation overridden by an !important rule reports that it is ' +
    984     'NOT running on the compositor');
    985 }, 'Neither transition nor animation does run on the compositor if the ' +
    986   'property is overridden by an !important rule');
    987 
    988 promise_test(async t => {
    989  var div = addDiv(t, { style: 'display: table' });
    990  var animation =
    991    div.animate({ transform: ['rotate(0deg)', 'rotate(360deg)'] },
    992                100 * MS_PER_SEC);
    993 
    994  await waitForAnimationReadyToRestyle(animation);
    995 
    996  await waitForPaints();
    997 
    998  assert_animation_is_running_on_compositor(animation,
    999    'Transform animation on display:table element should be running on the'
   1000    + ' compositor');
   1001 }, 'Transform animation on display:table element runs on the compositor');
   1002 
   1003 promise_test(async t => {
   1004  const div = addDiv(t, { style: 'display: table' });
   1005  const animation = div.animate(null, 100 * MS_PER_SEC);
   1006  const effect = new KeyframeEffect(div,
   1007                                    { transform: ['none', 'none']},
   1008                                    100 * MS_PER_SEC);
   1009 
   1010  await waitForAnimationReadyToRestyle(animation);
   1011 
   1012  animation.effect = effect;
   1013 
   1014  await waitForNextFrame();
   1015  await waitForPaints();
   1016 
   1017  assert_animation_is_running_on_compositor(
   1018    animation,
   1019    'Transform animation on table element should be running on the compositor'
   1020  );
   1021 }, 'Empty transform effect assigned after the fact to display:table content'
   1022   + ' runs on the compositor');
   1023 
   1024 promise_test(async t => {
   1025  const div = addDiv(t);
   1026  const animation = div.animate({ backgroundColor: ['blue', 'green'] },
   1027                                100 * MS_PER_SEC);
   1028 
   1029  await waitForAnimationReadyToRestyle(animation);
   1030  await waitForPaints();
   1031 
   1032  assert_animation_is_running_on_compositor(animation,
   1033    'background-color animation should be running on the compositor');
   1034 }, 'background-color animation runs on the compositor');
   1035 
   1036 promise_test(async t => {
   1037  const div = addDiv(t);
   1038  const animation = div.animate({ backgroundColor: ['blue', 'green'] },
   1039                                100 * MS_PER_SEC);
   1040 
   1041  await waitForAnimationReadyToRestyle(animation);
   1042  await waitForPaints();
   1043 
   1044  assert_animation_is_running_on_compositor(animation,
   1045    'background-color animation should be running on the compositor');
   1046 
   1047  // Add a red opaque background image covering the background color animation.
   1048  div.style.backgroundImage =
   1049    'url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAUCAIAAAAC64' +
   1050    'paAAAAG0lEQVR42mP8z0A%2BYKJA76jmUc2jmkc1U0EzACKcASfOgGoMAAAAAElFTkSuQmCC)';
   1051 
   1052  await waitForAnimationReadyToRestyle(animation);
   1053  await waitForPaints();
   1054 
   1055  // Bug 1712246. We should optimize this case eventually.
   1056  //assert_animation_is_not_running_on_compositor(animation,
   1057  //  'Opaque background image stops background-color animations from running ' +
   1058  //  'on the compositor');
   1059 }, 'Opaque background image stops background-color animations from running ' +
   1060   ' on the compositor');
   1061 
   1062 promise_test(async t => {
   1063  await SpecialPowers.pushPrefEnv({
   1064    set: [["gfx.omta.background-color", false]]
   1065  });
   1066 
   1067  const div = addDiv(t);
   1068  const animation = div.animate({ backgroundColor: ['blue', 'green'] },
   1069                                100 * MS_PER_SEC);
   1070 
   1071  await waitForAnimationReadyToRestyle(animation);
   1072  await waitForPaints();
   1073 
   1074  assert_animation_is_not_running_on_compositor(animation,
   1075    'background-color animation should NOT be running on the compositor ' +
   1076    'if the pref is disabled');
   1077 }, 'background-color animation does not run on the compositor if the pref ' +
   1078   'is disabled');
   1079 
   1080 promise_test(async t => {
   1081  const div = addDiv(t);
   1082  const animation = div.animate({ translate: ['0px', '100px'] },
   1083                                100 * MS_PER_SEC);
   1084 
   1085  await waitForAnimationReadyToRestyle(animation);
   1086  await waitForPaints();
   1087 
   1088  assert_animation_is_running_on_compositor(animation,
   1089    'translate animation should be running on the compositor');
   1090 }, 'translate animation runs on the compositor');
   1091 
   1092 promise_test(async t => {
   1093  const div = addDiv(t);
   1094  const animation = div.animate({ rotate: ['0deg', '45deg'] },
   1095                                100 * MS_PER_SEC);
   1096 
   1097  await waitForAnimationReadyToRestyle(animation);
   1098  await waitForPaints();
   1099 
   1100  assert_animation_is_running_on_compositor(animation,
   1101    'rotate animation should be running on the compositor');
   1102 }, 'rotate animation runs on the compositor');
   1103 
   1104 promise_test(async t => {
   1105  const div = addDiv(t);
   1106  const animation = div.animate({ scale: ['1 1', '2 2'] },
   1107                                100 * MS_PER_SEC);
   1108 
   1109  await waitForAnimationReadyToRestyle(animation);
   1110  await waitForPaints();
   1111 
   1112  assert_animation_is_running_on_compositor(animation,
   1113    'scale animation should be running on the compositor');
   1114 }, 'scale animation runs on the compositor');
   1115 
   1116 promise_test(async t => {
   1117  const div = addDiv(t);
   1118  const animation = div.animate({ translate: ['0px', '100px'],
   1119                                  rotate: ['0deg', '45deg'],
   1120                                  transform: ['translate(20px)',
   1121                                              'translate(30px)'] },
   1122                                100 * MS_PER_SEC);
   1123 
   1124  await waitForAnimationReadyToRestyle(animation);
   1125  await waitForPaints();
   1126 
   1127  assert_animation_is_running_on_compositor(animation,
   1128    'multiple transform-like properties animation should be running on the ' +
   1129    'compositor');
   1130 
   1131  const properties = animation.effect.getProperties();
   1132  properties.forEach(property => {
   1133    assert_true(property.runningOnCompositor,
   1134                property.property + ' is running on the compositor');
   1135  });
   1136 }, 'Multiple transform-like properties animation runs on the compositor');
   1137 
   1138 promise_test(async t => {
   1139  const div = addDiv(t);
   1140  const animation = div.animate({ offsetPath: ['none', 'none'] },
   1141                                100 * MS_PER_SEC);
   1142 
   1143  await waitForAnimationReadyToRestyle(animation);
   1144  await waitForPaints();
   1145 
   1146  assert_animation_is_running_on_compositor(animation,
   1147    'offset-path animation should be running on the compositor even if ' +
   1148    'it is always none');
   1149 }, 'offset-path none animation runs on the compositor');
   1150 
   1151 promise_test(async t => {
   1152  const div = addDiv(t);
   1153  const animation = div.animate({ offsetPath: ['path("M0 0l100 100")',
   1154                                               'path("M0 0l200 200")'] },
   1155                                100 * MS_PER_SEC);
   1156 
   1157  await waitForAnimationReadyToRestyle(animation);
   1158  await waitForPaints();
   1159 
   1160  assert_animation_is_running_on_compositor(animation,
   1161    'offset-path:path() animation should be running on the compositor');
   1162 }, 'offset-path:path() animation runs on the compositor');
   1163 
   1164 promise_test(async t => {
   1165  const div = addDiv(t);
   1166  const animation = div.animate({ offsetPath: ['ray(0deg)',
   1167                                               'ray(180deg)'] },
   1168                                100 * MS_PER_SEC);
   1169 
   1170  await waitForAnimationReadyToRestyle(animation);
   1171  await waitForPaints();
   1172 
   1173  assert_animation_is_running_on_compositor(animation,
   1174    'offset-path:ray() animation should be running on the compositor');
   1175 }, 'offset-path:ray() animation runs on the compositor');
   1176 
   1177 promise_test(async t => {
   1178  const div = addDiv(t);
   1179  const animation = div.animate({ offsetPath: ['inset(0px)',
   1180                                               'inset(10px)'] },
   1181                                100 * MS_PER_SEC);
   1182 
   1183  await waitForAnimationReadyToRestyle(animation);
   1184  await waitForPaints();
   1185 
   1186  assert_animation_is_running_on_compositor(animation,
   1187    'offset-path:inset() animation should be running on the compositor');
   1188 }, 'offset-path:inset() animation runs on the compositor');
   1189 
   1190 promise_test(async t => {
   1191  const div = addDiv(t);
   1192  const animation = div.animate({ offsetPath: ['circle(10px)',
   1193                                               'circle(20px)'] },
   1194                                100 * MS_PER_SEC);
   1195 
   1196  await waitForAnimationReadyToRestyle(animation);
   1197  await waitForPaints();
   1198 
   1199  assert_animation_is_running_on_compositor(animation,
   1200    'offset-path:circle() animation should be running on the compositor');
   1201 }, 'offset-path:circle() animation runs on the compositor');
   1202 
   1203 promise_test(async t => {
   1204  const div = addDiv(t);
   1205  const animation = div.animate({ offsetPath: ['ellipse(10px 20px)',
   1206                                               'ellipse(20px 40px)'] },
   1207                                100 * MS_PER_SEC);
   1208 
   1209  await waitForAnimationReadyToRestyle(animation);
   1210  await waitForPaints();
   1211 
   1212  assert_animation_is_running_on_compositor(animation,
   1213    'offset-path:ellipse() animation should be running on the compositor');
   1214 }, 'offset-path:ellipse() animation runs on the compositor');
   1215 
   1216 promise_test(async t => {
   1217  const div = addDiv(t);
   1218  const animation = div.animate({ offsetPath: ['polygon(0px 0px)',
   1219                                               'polygon(50px 50px)'] },
   1220                                100 * MS_PER_SEC);
   1221 
   1222  await waitForAnimationReadyToRestyle(animation);
   1223  await waitForPaints();
   1224 
   1225  assert_animation_is_running_on_compositor(animation,
   1226    'offset-path:polygon() animation should be running on the compositor');
   1227 }, 'offset-path:polygon() animation runs on the compositor');
   1228 
   1229 promise_test(async t => {
   1230  const div = addDiv(t);
   1231  const animation = div.animate({ offsetPath: ['padding-box',
   1232                                               'padding-box'] },
   1233                                100 * MS_PER_SEC);
   1234 
   1235  await waitForAnimationReadyToRestyle(animation);
   1236  await waitForPaints();
   1237 
   1238  assert_animation_is_running_on_compositor(animation,
   1239    'offset-path:padding-box animation should be running on the compositor');
   1240 }, 'offset-path:padding-box animation runs on the compositor');
   1241 
   1242 promise_test(async t => {
   1243  const div = addDiv(t);
   1244  const animation = div.animate({ offsetPath: ['content-box',
   1245                                               'content-box'] },
   1246                                100 * MS_PER_SEC);
   1247 
   1248  await waitForAnimationReadyToRestyle(animation);
   1249  await waitForPaints();
   1250 
   1251  assert_animation_is_running_on_compositor(animation,
   1252    'offset-path:content-box animation should be running on the compositor');
   1253 }, 'offset-path:content-box animation runs on the compositor');
   1254 
   1255 promise_test(async t => {
   1256  const div = addDiv(t);
   1257  const animation = div.animate({ offsetPath: ['xywh(0% 0% 10px 10px)',
   1258                                               'xywh(10% 10% 20px 20px)'] },
   1259                                100 * MS_PER_SEC);
   1260 
   1261  await waitForAnimationReadyToRestyle(animation);
   1262  await waitForPaints();
   1263 
   1264  assert_animation_is_running_on_compositor(animation,
   1265    'offset-path:xywh() animation should be running on the compositor');
   1266 }, 'offset-path:xywh() animation runs on the compositor');
   1267 
   1268 promise_test(async t => {
   1269  const div = addDiv(t);
   1270  const animation = div.animate({ offsetPath: ['rect(0% 0% 10px 10px)',
   1271                                               'rect(10% 10% 20px 20px)'] },
   1272                                100 * MS_PER_SEC);
   1273 
   1274  await waitForAnimationReadyToRestyle(animation);
   1275  await waitForPaints();
   1276 
   1277  assert_animation_is_running_on_compositor(animation,
   1278    'offset-path:rect() animation should be running on the compositor');
   1279 }, 'offset-path:rect() animation runs on the compositor');
   1280 
   1281 promise_test(async t => {
   1282  const div = addDiv(t);
   1283  const animation = div.animate({ offsetDistance: ['0%', '100%'] },
   1284                                100 * MS_PER_SEC);
   1285 
   1286  await waitForAnimationReadyToRestyle(animation);
   1287  await waitForPaints();
   1288 
   1289  assert_animation_is_not_running_on_compositor(animation,
   1290    'offset-distance animation is not running on the compositor because ' +
   1291    'offset-path is none');
   1292 
   1293  const newAnim = div.animate({ offsetPath: ['None', 'None'] },
   1294                              100 * MS_PER_SEC);
   1295  await waitForAnimationReadyToRestyle(newAnim);
   1296  await waitForPaints();
   1297 
   1298  assert_animation_is_running_on_compositor(animation,
   1299    'offset-distance animation should be running on the compositor');
   1300  assert_animation_is_running_on_compositor(newAnim,
   1301    'new added offset-path animation should be running on the compositor');
   1302 }, 'offset-distance animation runs on the compositor');
   1303 
   1304 promise_test(async t => {
   1305  const div = addDiv(t);
   1306  const animation = div.animate({ offsetRotate: ['0deg', '45deg'] },
   1307                                100 * MS_PER_SEC);
   1308 
   1309  await waitForAnimationReadyToRestyle(animation);
   1310  await waitForPaints();
   1311 
   1312  assert_animation_is_not_running_on_compositor(animation,
   1313    'offset-rotate animation is not running on the compositor because ' +
   1314    'offset-path is none');
   1315 
   1316  const newAnim = div.animate({ offsetPath: ['None', 'None'] },
   1317                              100 * MS_PER_SEC);
   1318  await waitForAnimationReadyToRestyle(newAnim);
   1319  await waitForPaints();
   1320 
   1321  assert_animation_is_running_on_compositor(animation,
   1322    'offset-rotate animation should be running on the compositor');
   1323  assert_animation_is_running_on_compositor(newAnim,
   1324    'new added offset-path animation should be running on the compositor');
   1325 }, 'offset-rotate animation runs on the compositor');
   1326 
   1327 promise_test(async t => {
   1328  const div = addDiv(t);
   1329  const animation = div.animate({ offsetAnchor: ['0% 0%', '100% 100%'] },
   1330                                100 * MS_PER_SEC);
   1331 
   1332  await waitForAnimationReadyToRestyle(animation);
   1333  await waitForPaints();
   1334 
   1335  assert_animation_is_not_running_on_compositor(animation,
   1336    'offset-anchor animation is not running on the compositor because ' +
   1337    'offset-path is none');
   1338 
   1339  const newAnim = div.animate({ offsetPath: ['None', 'None'] },
   1340                              100 * MS_PER_SEC);
   1341  await waitForAnimationReadyToRestyle(newAnim);
   1342  await waitForPaints();
   1343 
   1344  assert_animation_is_running_on_compositor(animation,
   1345    'offset-anchor animation should be running on the compositor');
   1346  assert_animation_is_running_on_compositor(newAnim,
   1347    'new added offset-path animation should be running on the compositor');
   1348 }, 'offset-anchor animation runs on the compositor');
   1349 
   1350 promise_test(async t => {
   1351  const div = addDiv(t);
   1352  const animation = div.animate({ offsetPosition: ['0% 0%', '100% 100%'] },
   1353                                100 * MS_PER_SEC);
   1354 
   1355  await waitForAnimationReadyToRestyle(animation);
   1356  await waitForPaints();
   1357 
   1358  assert_animation_is_not_running_on_compositor(animation,
   1359    'offset-position animation is not running on the compositor because ' +
   1360    'offset-path is none');
   1361 
   1362  const newAnim = div.animate({ offsetPath: ['None', 'None'] },
   1363                              100 * MS_PER_SEC);
   1364  await waitForAnimationReadyToRestyle(newAnim);
   1365  await waitForPaints();
   1366 
   1367  assert_animation_is_running_on_compositor(animation,
   1368    'offset-position animation should be running on the compositor');
   1369  assert_animation_is_running_on_compositor(newAnim,
   1370    'new added offset-path animation should be running on the compositor');
   1371 }, 'offset-position animation runs on the compositor');
   1372 
   1373 promise_test(async t => {
   1374  const div = addDiv(t);
   1375  const animation = div.animate({ translate: ['0px', '100px'],
   1376                                  rotate: ['0deg', '45deg'],
   1377                                  transform: ['translate(0px)',
   1378                                              'translate(100px)'],
   1379                                  offsetDistance: ['0%', '100%'] },
   1380                                100 * MS_PER_SEC);
   1381  await waitForAnimationReadyToRestyle(animation);
   1382  await waitForPaints();
   1383 
   1384  assert_animation_is_running_on_compositor(animation,
   1385    'Animation is running on the compositor even though we do not have ' +
   1386    'offset-path');
   1387 
   1388  div.style.offsetPath = 'path("M50 0v100")';
   1389  getComputedStyle(div).offsetPath;
   1390 
   1391  await waitForPaints();
   1392 
   1393  assert_animation_is_running_on_compositor(animation,
   1394    'Animation is running on the compositor');
   1395 
   1396 }, 'Multiple transform-like properties (include motion-path) animation runs ' +
   1397   'on the compositor');
   1398 
   1399 promise_test(async t => {
   1400  const div = addDiv(t);
   1401  const animation = div.animate({ translate: ['0px', '100px'],
   1402                                  rotate: ['0deg', '45deg'],
   1403                                  transform: ['translate(20px)',
   1404                                              'translate(30px)'],
   1405                                  offsetDistance: ['0%', '100%'] },
   1406                                100 * MS_PER_SEC);
   1407 
   1408  div.style.setProperty('translate', '50px', 'important');
   1409  getComputedStyle(div).translate;
   1410 
   1411  await waitForAnimationReadyToRestyle(animation);
   1412  await waitForPaints();
   1413 
   1414  assert_animation_is_not_running_on_compositor(animation,
   1415    'Animation overridden by an !important rule reports that it is ' +
   1416    'NOT running on the compositor');
   1417 
   1418  const properties = animation.effect.getProperties();
   1419  properties.forEach(property => {
   1420    assert_true(!property.runningOnCompositor,
   1421                property.property + ' is not running on the compositor');
   1422  });
   1423 }, 'Multiple transform-like properties animation does not runs on the ' +
   1424   'compositor because one of the transform-like property is overridden ' +
   1425   'by an !important rule');
   1426 
   1427 // FIXME:  Bug 1593106: We should still run the animations on the compositor if
   1428 // offset-* doesn't have any effect.
   1429 promise_test(async t => {
   1430  const div = addDiv(t);
   1431  const animation = div.animate({ translate: ['0px', '100px'],
   1432                                  rotate: ['0deg', '45deg'],
   1433                                  transform: ['translate(0px)',
   1434                                              'translate(100px)'],
   1435                                  offsetDistance: ['0%', '100%'] },
   1436                                100 * MS_PER_SEC);
   1437 
   1438  div.style.setProperty('offset-distance', '50%', 'important');
   1439  getComputedStyle(div).offsetDistance;
   1440 
   1441  await waitForAnimationReadyToRestyle(animation);
   1442  await waitForPaints();
   1443 
   1444  assert_animation_is_not_running_on_compositor(animation,
   1445    'Animation overridden by an !important rule reports that it is ' +
   1446    'NOT running on the compositor');
   1447 
   1448  const properties = animation.effect.getProperties();
   1449  properties.forEach(property => {
   1450    assert_true(!property.runningOnCompositor,
   1451                property.property + ' is not running on the compositor');
   1452  });
   1453 }, 'Multiple transform-like properties animation does not runs on the ' +
   1454   'compositor because one of the offset-* property is overridden ' +
   1455   'by an !important rule');
   1456 
   1457 promise_test(async t => {
   1458  const div = addDiv(t);
   1459  const animation = div.animate({ rotate: ['0deg', '45deg'],
   1460                                  transform: ['translate(20px)',
   1461                                              'translate(30px)'],
   1462                                  offsetDistance: ['0%', '100%'] },
   1463                                100 * MS_PER_SEC);
   1464 
   1465  div.style.setProperty('translate', '50px', 'important');
   1466  getComputedStyle(div).translate;
   1467 
   1468  await waitForAnimationReadyToRestyle(animation);
   1469  await waitForPaints();
   1470 
   1471  assert_animation_is_running_on_compositor(animation,
   1472    'Animation is still running on the compositor');
   1473 
   1474  const properties = animation.effect.getProperties();
   1475  properties.forEach(property => {
   1476    assert_true(property.runningOnCompositor,
   1477                property.property + ' is running on the compositor');
   1478  });
   1479 }, 'Multiple transform-like properties animation still runs on the ' +
   1480   'compositor because the overridden-by-!important property does not have ' +
   1481   'animation');
   1482 
   1483 promise_test(async t => {
   1484  // We should run the animations on the compositor for this case:
   1485  // 1. A transition of 'translate'
   1486  // 2. An !important rule on 'translate'
   1487  // 3. An animation of 'scale'
   1488  const div = addDiv(t, { style: 'translate: 100px !important;' });
   1489  const animation = div.animate({ rotate: ['0deg', '45deg'] },
   1490                                100 * MS_PER_SEC);
   1491  div.style.transition = 'translate 100s';
   1492  getComputedStyle(div).transition;
   1493 
   1494  await waitForAnimationReadyToRestyle(animation);
   1495  await waitForPaints();
   1496 
   1497  assert_animation_is_running_on_compositor(animation,
   1498    'rotate animation should be running on the compositor');
   1499 
   1500  div.style.setProperty('translate', '200px', 'important');
   1501  getComputedStyle(div).translate;
   1502 
   1503  const anims = div.getAnimations();
   1504  await waitForPaints();
   1505 
   1506  assert_animation_is_running_on_compositor(anims[0],
   1507    `${anims[0].effect.getProperties()[0].property} animation should be ` +
   1508    `running on the compositor`);
   1509  assert_animation_is_running_on_compositor(anims[1],
   1510    `${anims[1].effect.getProperties()[0].property} animation should be ` +
   1511    `running on the compositor`);
   1512 }, 'Transform-like animations and transitions still runs on the compositor ' +
   1513   'because the !important rule is overridden by a transition, and the ' +
   1514   'transition property does not have animations');
   1515 
   1516 promise_test(async t => {
   1517  const container = addDiv(t, { style: 'transform-style: preserve-3d;' });
   1518  const targetA = addDiv(t, { style: 'transform-style: preserve-3d' });
   1519  const targetB = addDiv(t, { style: 'transform-style: preserve-3d' });
   1520  const targetC = addDiv(t);
   1521  container.appendChild(targetA);
   1522  targetA.append(targetB);
   1523  targetB.append(targetC);
   1524 
   1525  const animation1 = targetA.animate({ rotate: ['0 0 1 0deg', '1 1 1 45deg'] },
   1526                                     100 * MS_PER_SEC);
   1527 
   1528  const animation2 = targetC.animate({ rotate: ['0 0 1 0deg', '0 1 1 100deg'] },
   1529                                     100 * MS_PER_SEC);
   1530 
   1531  await waitForAnimationReadyToRestyle(animation1);
   1532  await waitForAnimationReadyToRestyle(animation2);
   1533  await waitForPaints();
   1534 
   1535  assert_animation_is_running_on_compositor(animation1,
   1536    'rotate animation in the 3d rendering context should be running on the ' +
   1537    'compositor');
   1538  assert_animation_is_running_on_compositor(animation2,
   1539    'rotate animation in the 3d rendering context should be running on the ' +
   1540    'compositor');
   1541 }, 'Transform-like animations in the 3d rendering context should runs on the ' +
   1542   'compositor');
   1543 
   1544 promise_test(async t => {
   1545  const container = addDiv(t, { style: 'transform-style: preserve-3d;' });
   1546  const target = addDiv(t, { style: 'transform-style: preserve-3d;' });
   1547  const innerA = addDiv(t, { style: 'width: 50px; height: 50px;' });
   1548  // The frame of innerB is too large, so this makes its ancenstors and children
   1549  // in the 3d context be not allowed the async animations.
   1550  const innerB = addDiv(t, { style: 'rotate: 0 1 1 100deg; ' +
   1551                                    'transform-style: preserve-3d; ' +
   1552                                    'text-indent: -9999em' });
   1553  const innerB2 = addDiv(t, { style: 'rotate: 0 1 1 45deg;' });
   1554  const innerBText = document.createTextNode("innerB");
   1555  container.appendChild(target);
   1556  target.appendChild(innerA);
   1557  target.appendChild(innerB);
   1558  innerB.appendChild(innerBText);
   1559  innerB.appendChild(innerB2);
   1560 
   1561  const animation1 = target.animate({ rotate: ['0 0 1 0deg', '1 1 1 45deg'] },
   1562                                    100 * MS_PER_SEC);
   1563 
   1564  const animation2 = innerA.animate({ rotate: ['0 0 1 0deg', '0 1 1 100deg'] },
   1565                                    100 * MS_PER_SEC);
   1566 
   1567  const animation3 = innerB2.animate({ rotate: ['0 0 1 0deg', '0 1 1 90deg'] },
   1568                                     100 * MS_PER_SEC);
   1569 
   1570  await waitForAnimationReadyToRestyle(animation1);
   1571  await waitForAnimationReadyToRestyle(animation2);
   1572  await waitForAnimationReadyToRestyle(animation3);
   1573  await waitForPaints();
   1574 
   1575  const isPartialPrerenderEnabled =
   1576    SpecialPowers.getBoolPref('layout.animation.prerender.partial');
   1577 
   1578  if (isPartialPrerenderEnabled) {
   1579    assert_animation_is_running_on_compositor(animation1,
   1580      'rotate animation in the 3d rendering context should be running on ' +
   1581      'the compositor even if one of its inner frames is too large');
   1582    assert_animation_is_running_on_compositor(animation2,
   1583      'rotate animation in the 3d rendering context is still running on ' +
   1584      'the compositor because its display item is created earlier');
   1585    assert_animation_is_running_on_compositor(animation3,
   1586      'rotate animation in the 3d rendering context should be running on ' +
   1587      'the compositor even if one of its parent frames is too large');
   1588  } else {
   1589    assert_animation_is_not_running_on_compositor(animation1,
   1590      'rotate animation in the 3d rendering context should not be running on ' +
   1591      'the compositor because one of its inner frames is too large');
   1592    assert_animation_is_running_on_compositor(animation2,
   1593      'rotate animation in the 3d rendering context is still running on ' +
   1594      'the compositor because its display item is created earlier');
   1595    assert_animation_is_not_running_on_compositor(animation3,
   1596      'rotate animation in the 3d rendering context should not be running on ' +
   1597      'the compositor because one of its parent frames is too large');
   1598  }
   1599  innerBText.remove();
   1600 }, 'Transform-like animations in the 3d rendering context should run on ' +
   1601   'the compositor even if it is the ancestor of ones whose frames are too ' +
   1602   'large or its ancestor is not allowed to run on the compositor');
   1603 
   1604 promise_test(async t => {
   1605  const container = addDiv(t, { style: 'transform-style: preserve-3d;' });
   1606  const target = addDiv(t, { style: 'transform-style: preserve-3d;' });
   1607  // The frame of innerA is too large, so this makes its ancenstors and children
   1608  // in the 3d context be not allowed the async animations.
   1609  const innerA = addDiv(t, { style: 'transform-style: preserve-3d; ' +
   1610                                    'text-indent: -9999em' });
   1611  const innerAText = document.createTextNode("innerA");
   1612  const innerB = addDiv(t, { style: 'width: 50px; height: 50px;' });
   1613  container.appendChild(target);
   1614  target.appendChild(innerA);
   1615  target.appendChild(innerB);
   1616  innerA.appendChild(innerAText);
   1617 
   1618  const animation1 = target.animate({ rotate: ['0 0 1 0deg', '1 1 1 45deg'] },
   1619                                    100 * MS_PER_SEC);
   1620 
   1621  const animation2 = innerA.animate({ rotate: ['0 0 1 0deg', '0 1 1 100deg'] },
   1622                                    100 * MS_PER_SEC);
   1623 
   1624  const animation3 = innerB.animate({ rotate: ['0 0 1 0deg', '0 1 1 90deg'] },
   1625                                    100 * MS_PER_SEC);
   1626 
   1627  await waitForAnimationReadyToRestyle(animation1);
   1628  await waitForAnimationReadyToRestyle(animation2);
   1629  await waitForAnimationReadyToRestyle(animation3);
   1630  await waitForPaints();
   1631 
   1632  const isPartialPrerenderEnabled =
   1633    SpecialPowers.getBoolPref('layout.animation.prerender.partial');
   1634 
   1635  if (isPartialPrerenderEnabled) {
   1636    assert_animation_is_running_on_compositor(animation1,
   1637      'rotate animation in the 3d rendering context should be running on ' +
   1638      'the compositor even if one of its inner frames is too large');
   1639    assert_animation_is_running_on_compositor(animation2,
   1640      'rotate animation in the 3d rendering context should be running on ' +
   1641      'the compositor even if its frame size is too large');
   1642    assert_animation_is_running_on_compositor(animation3,
   1643      'rotate animation in the 3d rendering context should be running on ' +
   1644      'the compositor even if its previous sibling frame is too large');
   1645  } else {
   1646    assert_animation_is_not_running_on_compositor(animation1,
   1647      'rotate animation in the 3d rendering context should not be running on ' +
   1648      'the compositor because one of its inner frames is too large');
   1649    assert_animation_is_not_running_on_compositor(animation2,
   1650      'rotate animation in the 3d rendering context should not be running on ' +
   1651      'the compositor because its frame size is too large');
   1652    assert_animation_is_not_running_on_compositor(animation3,
   1653      'rotate animation in the 3d rendering context should not be running on ' +
   1654      'the compositor because its previous sibling frame is too large');
   1655  }
   1656  innerAText.remove();
   1657 }, 'Transform-like animations in the 3d rendering context should run on ' +
   1658   'the compositor even if its previous sibling frame size is too large');
   1659 
   1660 </script>
   1661 </body>