tor-browser

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

style-change-events.html (7010B)


      1 <!doctype html>
      2 <meta charset=utf-8>
      3 <title>KeyframeEffect interface: style change events</title>
      4 <link rel="help"
      5      href="https://drafts.csswg.org/web-animations-1/#model-liveness">
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <script src="../../testcommon.js"></script>
      9 <body>
     10 <div id="log"></div>
     11 <script>
     12 'use strict';
     13 
     14 // Test that each property defined in the KeyframeEffect interface does not
     15 // produce style change events.
     16 //
     17 // There are two types of tests:
     18 //
     19 //   MakeInEffectTest
     20 //
     21 //     For properties that are able to cause the KeyframeEffect to start
     22 //     affecting the CSS 'opacity' property.
     23 //
     24 //     This function takes either:
     25 //
     26 //     (a) A function that makes the passed-in KeyframeEffect affect the
     27 //         'opacity' property.
     28 //
     29 //     (b) An object with the following format:
     30 //
     31 //         {
     32 //            setup: elem => { /* return Animation */ }
     33 //            test: effect => { /* make |effect| affect 'opacity' */ }
     34 //         }
     35 //
     36 //     If the latter form is used, the setup function should return an Animation
     37 //     whose KeyframeEffect does NOT (yet) affect the 'opacity' property (or is
     38 //     NOT yet in-effect). Otherwise, the transition we use to detect if a style
     39 //     change event has occurred will never have a chance to be triggered (since
     40 //     the animated style will clobber both before-change and after-change
     41 //     style).
     42 //
     43 //  UsePropertyTest
     44 //
     45 //    For properties that cannot cause the KeyframeEffect to start affecting the
     46 //    CSS 'opacity' property.
     47 //
     48 //    The shape of the parameter to the UsePropertyTest is identical to the
     49 //    MakeInEffectTest. The only difference is that the function (or 'test'
     50 //    function of the object format is used) does not need to make the
     51 //    KeyframeEffect affect the CSS 'opacity' property, but simply needs to
     52 //    get/set the property under test.
     53 
     54 const MakeInEffectTest = testFuncOrObj => {
     55  let test, setup;
     56 
     57  if (typeof testFuncOrObj === 'function') {
     58    test = testFuncOrObj;
     59  } else {
     60    test = testFuncOrObj.test;
     61    if (typeof testFuncOrObj.setup === 'function') {
     62      setup = testFuncOrObj.setup;
     63    }
     64  }
     65 
     66  if (!setup) {
     67    setup = elem =>
     68      elem.animate({ color: ['blue', 'green'] }, 100 * MS_PER_SEC);
     69  }
     70 
     71  return { test, setup };
     72 };
     73 
     74 const UsePropertyTest = testFuncOrObj => {
     75  const { test, setup } = MakeInEffectTest(testFuncOrObj);
     76 
     77  let coveringAnimation;
     78  return {
     79    setup: elem => {
     80      coveringAnimation = new Animation(
     81        new KeyframeEffect(elem, { opacity: [0, 1] }, 100 * MS_PER_SEC)
     82      );
     83 
     84      return setup(elem);
     85    },
     86    test: effect => {
     87      test(effect);
     88      coveringAnimation.play();
     89    },
     90  };
     91 };
     92 
     93 const tests = {
     94  getTiming: UsePropertyTest(effect => effect.getTiming()),
     95  getComputedTiming: UsePropertyTest(effect => effect.getComputedTiming()),
     96  updateTiming: MakeInEffectTest({
     97    // Initially put the effect in its before phase (with no fill mode)...
     98    setup: elem =>
     99      elem.animate(
    100        { opacity: [0.5, 1] },
    101        {
    102          duration: 100 * MS_PER_SEC,
    103          delay: 100 * MS_PER_SEC,
    104        }
    105      ),
    106    // ... so that when the delay is removed, it begins to affect the opacity.
    107    test: effect => {
    108      effect.updateTiming({ delay: 0 });
    109    },
    110  }),
    111  get target() {
    112    let targetElem;
    113    return MakeInEffectTest({
    114      setup: (elem, t) => {
    115        targetElem = elem;
    116        const targetB = createDiv(t);
    117        return targetB.animate({ opacity: [0.5, 1] }, 100 * MS_PER_SEC);
    118      },
    119      test: effect => {
    120        effect.target = targetElem;
    121      },
    122    });
    123  },
    124  pseudoElement: MakeInEffectTest({
    125    setup: elem => elem.animate(
    126      {opacity: [0.5, 1]},
    127      {duration: 100 * MS_PER_SEC, pseudoElement: '::before'}
    128    ),
    129    test: effect => {
    130      effect.pseudoElement = null;
    131    },
    132  }),
    133  iterationComposite: UsePropertyTest(effect => {
    134    // Get iterationComposite
    135    effect.iterationComposite;
    136 
    137    // Set iterationComposite
    138    effect.iterationComposite = 'accumulate';
    139  }),
    140  composite: UsePropertyTest(effect => {
    141    // Get composite
    142    effect.composite;
    143 
    144    // Set composite
    145    effect.composite = 'add';
    146  }),
    147  getKeyframes: UsePropertyTest(effect => effect.getKeyframes()),
    148  setKeyframes: MakeInEffectTest(effect =>
    149    effect.setKeyframes({ opacity: [0.5, 1] })
    150  ),
    151  get ['KeyframeEffect constructor']() {
    152    let originalElem;
    153    let animation;
    154    return UsePropertyTest({
    155      setup: elem => {
    156        originalElem = elem;
    157        // Return a dummy animation so the caller has something to wait on
    158        return elem.animate(null);
    159      },
    160      test: () =>
    161        new KeyframeEffect(
    162          originalElem,
    163          { opacity: [0.5, 1] },
    164          100 * MS_PER_SEC
    165        ),
    166    });
    167  },
    168  get ['KeyframeEffect copy constructor']() {
    169    let effectToClone;
    170    return UsePropertyTest({
    171      setup: elem => {
    172        effectToClone = new KeyframeEffect(
    173          elem,
    174          { opacity: [0.5, 1] },
    175          100 * MS_PER_SEC
    176        );
    177        // Return a dummy animation so the caller has something to wait on
    178        return elem.animate(null);
    179      },
    180      test: () => new KeyframeEffect(effectToClone),
    181    });
    182  },
    183 };
    184 
    185 // Check that each enumerable property and the constructors follow the
    186 // expected behavior with regards to triggering style change events.
    187 const properties = [
    188  ...Object.keys(AnimationEffect.prototype),
    189  ...Object.keys(KeyframeEffect.prototype),
    190  'KeyframeEffect constructor',
    191  'KeyframeEffect copy constructor',
    192 ];
    193 
    194 test(() => {
    195  for (const property of Object.keys(tests)) {
    196    assert_in_array(
    197      property,
    198      properties,
    199      `Test property '${property}' should be one of the properties on ` +
    200      ' KeyframeEffect'
    201    );
    202  }
    203 }, 'All property keys are recognized');
    204 
    205 for (const key of properties) {
    206  promise_test(async t => {
    207    assert_own_property(tests, key, `Should have a test for '${key}' property`);
    208    const { setup, test } = tests[key];
    209 
    210    // Setup target element
    211    const div = createDiv(t);
    212    let gotTransition = false;
    213    div.addEventListener('transitionrun', () => {
    214      gotTransition = true;
    215    });
    216 
    217    // Setup animation
    218    const animation = setup(div, t);
    219 
    220    // Setup transition start point
    221    div.style.transition = 'opacity 100s';
    222    getComputedStyle(div).opacity;
    223 
    224    // Update specified style but don't flush
    225    div.style.opacity = '0.5';
    226 
    227    // Trigger the property
    228    test(animation.effect);
    229 
    230    // If the test function produced a style change event it will have triggered
    231    // a transition.
    232 
    233    // Wait for the animation to start and then for at least one animation
    234    // frame to give the transitionrun event a chance to be dispatched.
    235    await animation.ready;
    236    await waitForAnimationFrames(1);
    237 
    238    assert_false(gotTransition, 'A transition should NOT have been triggered');
    239  }, `KeyframeEffect.${key} does NOT trigger a style change event`);
    240 }
    241 </script>
    242 </body>