layout/style/test/test_animations_omta.html

Thu, 15 Jan 2015 21:03:48 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 21:03:48 +0100
branch
TOR_BUG_9701
changeset 11
deefc01c0e14
permissions
-rw-r--r--

Integrate friendly tips from Tor colleagues to make (or not) 4.5 alpha 3;
This includes removal of overloaded (but unused) methods, and addition of
a overlooked call to DataStruct::SetData(nsISupports, uint32_t, bool.)

     1 <!DOCTYPE HTML>
     2 <html>
     3 <!--
     4 https://bugzilla.mozilla.org/show_bug.cgi?id=964646
     5 -->
     6 <!--
     8  ========= PLEASE KEEP THIS IN SYNC WITH test_animations.html =========
    10  This test mimicks the content of test_animations.html but performs tests
    11  specific to animations that run on the compositor thread since they require
    12  special (asynchronous) handling. Furthermore, these tests check that
    13  animations that are expected to run on the compositor thread, are actually
    14  doing so.
    16  If you are making changes to this file or to test_animations.html, please
    17  try to keep them consistent where appropriate.
    19 -->
    20 <head>
    21   <meta charset="utf-8">
    22   <title>Test for css3-animations running on the compositor thread (Bug
    23          964646)</title>
    24   <script type="application/javascript"
    25     src="/tests/SimpleTest/SimpleTest.js"></script>
    26   <script type="application/javascript"
    27     src="/tests/SimpleTest/paint_listener.js"></script>
    28   <script type="application/javascript" src="animation_utils.js"></script>
    29   <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
    30   <style type="text/css">
    31     @keyframes transform-anim {
    32       to {
    33         transform: translate(100px);
    34       }
    35     }
    36     @keyframes anim1 {
    37        0% { transform: translate(0px) }
    38        50% { transform: translate(80px) }
    39        100% { transform: translate(100px) }
    40     }
    41     @keyframes anim2 {
    42       from { opacity: 0 } to { opacity: 1 }
    43     }
    44     @keyframes anim3 {
    45       from { opacity: 0 } to { opacity: 1 }
    46     }
    48     @keyframes kf1 {
    49       50% { transform: translate(50px) }
    50       to { transform: translate(150px) }
    51     }
    52     @keyframes kf2 {
    53       from { transform: translate(150px) }
    54       50% { transform: translate(50px) }
    55     }
    56     @keyframes kf3 {
    57       25% { transform: translate(100px) }
    58     }
    59     @keyframes kf4 {
    60       to, from { display: inline; transform: translate(37px) }
    61     }
    62     @keyframes kf_cascade1 {
    63       from { transform: translate(50px) }
    64       50%, from { transform: translate(30px) }      /* wins: 0% */
    65       75%, 85%, 50% { transform: translate(20px) }  /* wins: 75%, 50% */
    66       100%, 85% { transform: translate(70px) }      /* wins: 100% */
    67       85.1% { transform: translate(60px) }          /* wins: 85.1% */
    68       85% { transform: translate(30px) }            /* wins: 85% */
    69     }
    70     @keyframes kf_cascade2 { from, to { opacity: 0.3 } }
    71     @keyframes kf_cascade2 { from, to { transform: translate(50px) } }
    72     @keyframes kf_cascade2 { from, to { transform: translate(100px) } }
    74     .target {
    75       /* The animation target needs geometry in order to qualify for OMTA */
    76       width: 100px;
    77       height: 100px;
    78       background-color: white;
    79     }
    80   </style>
    81 </head>
    82 <body>
    83 <a target="_blank"
    84   href="https://bugzilla.mozilla.org/show_bug.cgi?id=964646">Mozilla Bug
    85   964646</a>
    86 <div id="display"></div>
    87 <pre id="test">
    88 <script type="application/javascript">
    89 "use strict";
    91 /** Test for css3-animations running on the compositor thread (Bug 964646) **/
    93 // Global state
    94 var gAsyncTests     = [],
    95     gDisplay        = document.getElementById("display"),
    96     gDiv            = null,
    97     gEventsReceived = [];
    99 SimpleTest.waitForExplicitFinish();
   100 runOMTATest(function() {
   101   // The async test runner returns a Promise that is resolved when the
   102   // test is finished so we can chain them together
   103   gAsyncTests.reduce(function(sequence, test) {
   104     return sequence.then(function() { return runAsyncTest(test); });
   105   }, Promise.resolve() /* the start of the sequence */)
   106   // Final step in the sequence
   107   .then(function() {
   108     SimpleTest.finish();
   109   });
   110 }, SimpleTest.finish);
   112 // Takes a generator function that represents a test case. Each point in the
   113 // test case that waits asynchronously for some result yields a Promise that is
   114 // resolved when the asychronous action has completed. By chaining these
   115 // intermediate results together we run the test to completion.
   116 //
   117 // This method itself returns a Promise that is resolved when the generator
   118 // function has completed.
   119 //
   120 // This arrangement is based on add_task() which is currently only available
   121 // in mochitest-chrome (bug 872229). Once add_task is available in
   122 // mochitest-plain we can remove this function and use add_task instead.
   123 function runAsyncTest(test) {
   124   var generator;
   126   function step(arg) {
   127     var next;
   128     try {
   129       next = generator.next(arg);
   130     } catch (e) {
   131       return Promise.reject(e);
   132     }
   133     if (next.done) {
   134       return Promise.resolve(next.value);
   135     } else {
   136       return Promise.resolve(next.value)
   137              .then(step, function(err) { throw err; });
   138     }
   139   }
   141   // Put refresh driver under test control
   142   advance_clock(0);
   144   // Run test
   145   generator = test();
   146   return step()
   147   .catch(function(err) {
   148     ok(false, err.message);
   149     // Clear up the test div in case we aborted the test before doing clean-up
   150     if (gDiv) {
   151       done_div();
   152     }
   153   }).then(function() {
   154     // Restore clock
   155     SpecialPowers.DOMWindowUtils.restoreNormalRefresh();
   156   });
   157 }
   159 function addAsyncTest(generator) {
   160   gAsyncTests.push(generator);
   161 }
   163 //----------------------------------------------------------------------
   164 //
   165 // Test cases
   166 //
   167 //----------------------------------------------------------------------
   169 // This test is not in test_animations.html but is here to test that
   170 // transform animations are actually run on the compositor thread as expected.
   171 addAsyncTest(function *() {
   172   new_div("animation: transform-anim linear 300s");
   174   yield waitForPaints();
   176   advance_clock(200000);
   177   omta_is("transform", { tx: 100 * 2 / 3 }, RunningOn.Compositor,
   178           "OMTA animation is animating as expected");
   179   done_div();
   180 });
   182 function *testFillMode(fillMode, fillsBackwards, fillsForwards)
   183 {
   184   var style = "transform: translate(30px); animation: 10s 3s anim1 linear";
   185   var desc;
   186   if (fillMode.length > 0) {
   187     style += " " + fillMode;
   188     desc = "fill mode " + fillMode + ": ";
   189   } else {
   190     desc = "default fill mode: ";
   191   }
   192   new_div(style);
   193   listen();
   195   // Currently backwards fill is not performed on the compositor thread but we
   196   // should wait for paints so we can test that transform values are *not* being
   197   // set on the compositor thread.
   198   yield waitForPaints();
   200   if (fillsBackwards)
   201     omta_is("transform", { tx: 0 }, RunningOn.MainThread,
   202             desc + "does affect value during delay (0s)");
   203   else
   204     omta_is("transform", { tx: 30 }, RunningOn.MainThread,
   205             desc + "doesn't affect value during delay (0s)");
   207   advance_clock(2000);
   208   if (fillsBackwards)
   209     omta_is("transform", { tx: 0 }, RunningOn.MainThead,
   210             desc + "does affect value during delay (0s)");
   211   else
   212     omta_is("transform", { tx: 30 }, RunningOn.MainThread,
   213             desc + "does affect value during delay (0s)");
   215   check_events([], "before start in testFillMode");
   216   advance_clock(1000);
   217   check_events([{ type: "animationstart", target: gDiv,
   218                   bubbles: true, cancelable: false,
   219                   animationName: "anim1", elapsedTime: 0.0,
   220                   pseudoElement: "" }],
   221                "right after start in testFillMode");
   223   // If we have a backwards fill then at the start of the animation we will end
   224   // up applying the same value as the fill value. Various optimizations in
   225   // RestyleManager may filter out this meaning that the animation doesn't get
   226   // added to the compositor thread until the first time the value changes.
   227   //
   228   // As a result we look for this first sample on either the compositor or the
   229   // computed style
   230   yield waitForPaints();
   231   omta_is("transform", { tx: 0 }, RunningOn.Either,
   232           desc + "affects value at start of animation");
   233   advance_clock(125);
   234   // We might not add the animation to compositor until the second sample (due
   235   // to the optimizations mentioned above) so we should wait for paints before
   236   // proceeding
   237   yield waitForPaints();
   238   omta_is("transform", { tx: 2 }, RunningOn.Compositor,
   239           desc + "affects value during animation");
   240   advance_clock(2375);
   241   omta_is("transform", { tx: 40 }, RunningOn.Compositor,
   242           desc + "affects value during animation");
   243   advance_clock(2500);
   244   omta_is("transform", { tx: 80 }, RunningOn.Compositor,
   245           desc + "affects value during animation");
   246   advance_clock(2500);
   247   omta_is("transform", { tx: 90 }, RunningOn.Compositor,
   248           desc + "affects value during animation");
   249   advance_clock(2375);
   250   omta_is("transform", { tx: 99.5 }, RunningOn.Compositor,
   251           desc + "affects value during animation");
   252   check_events([], "before end in testFillMode");
   253   advance_clock(125);
   254   check_events([{ type: "animationend", target: gDiv,
   255                   bubbles: true, cancelable: false,
   256                   animationName: "anim1", elapsedTime: 10.0,
   257                   pseudoElement: "" }],
   258                "right after end in testFillMode");
   260   // Currently the compositor will apply a forwards fill until it gets told by
   261   // the main thread to clear the animation. As a result we should wait for
   262   // paints to be flushed before checking that the animated value does *not*
   263   // appear on the compositor thread.
   264   yield waitForPaints();
   265   if (fillsForwards)
   266     omta_is("transform", { tx: 100 }, RunningOn.MainThread,
   267             desc + "affects value at end of animation");
   268   advance_clock(10);
   269   if (fillsForwards)
   270     omta_is("transform", { tx: 100 }, RunningOn.MainThread,
   271             desc + "affects value after animation");
   272   else
   273     omta_is("transform", { tx: 30 }, RunningOn.MainThread,
   274             desc + "does not affect value after animation");
   276   done_div();
   277 }
   279 addAsyncTest(function() { return testFillMode("", false, false); });
   280 addAsyncTest(function() { return testFillMode("none", false, false); });
   281 addAsyncTest(function() { return testFillMode("forwards", false, true); });
   282 addAsyncTest(function() { return testFillMode("backwards", true, false); });
   283 addAsyncTest(function() { return testFillMode("both", true, true); });
   285 // Test that animations continue running when the animation name
   286 // list is changed.
   287 //
   288 // test_animations.html combines all these tests into one block but this is
   289 // difficult for OMTA because currently there are only two properties to which
   290 // we apply OMTA. Instead we break the test down into a few independent pieces
   291 // in order to exercise the same functionality.
   293 // Append to list
   294 addAsyncTest(function *() {
   295   new_div("animation: anim1 linear 10s");
   296   yield waitForPaints();
   297     omta_is("transform", { tx: 0 }, RunningOn.Either,
   298             "just anim1, translate at start");
   299   advance_clock(1000);
   300     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
   301             "just anim1, translate at 1s");
   302   // append anim2
   303   gDiv.style.animation = "anim1 linear 10s, anim2 linear 10s";
   304   yield waitForPaintsFlushed();
   305     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
   306             "anim1 + anim2, translate at 1s");
   307     omta_is("opacity", 0, RunningOn.Compositor,
   308             "anim1 + anim2, opacity at 1s");
   309   advance_clock(1000);
   310     omta_is("transform", { tx: 32 }, RunningOn.Compositor,
   311             "anim1 + anim2, translate at 2s");
   312     omta_is("opacity", 0.1, RunningOn.Compositor,
   313             "anim1 + anim2, opacity at 2s");
   314   done_div();
   315 });
   317 // Prepend to list; delete from list
   318 addAsyncTest(function *() {
   319   new_div("animation: anim1 linear 10s");
   320   yield waitForPaints();
   321     omta_is("transform", { tx: 0 }, RunningOn.Either,
   322             "just anim1, translate at start");
   323   advance_clock(1000);
   324     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
   325                      "just anim1, translate at 1s");
   326   // prepend anim2
   327   gDiv.style.animation = "anim2 linear 10s, anim1 linear 10s";
   328   yield waitForPaintsFlushed();
   329     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
   330             "anim2 + anim1, translate at 1s");
   331     omta_is("opacity", 0, RunningOn.Compositor,
   332             "anim2 + anim1, opacity at 1s");
   333   advance_clock(1000);
   334     omta_is("transform", { tx: 32 }, RunningOn.Compositor,
   335             "anim2 + anim1, translate at 2s");
   336     omta_is("opacity", 0.1, RunningOn.Compositor,
   337             "anim2 + anim1, opacity at 2s");
   338   // remove anim2 from list
   339   gDiv.style.animation = "anim1 linear 10s";
   340   yield waitForPaintsFlushed();
   341     omta_is("transform", { tx: 32 }, RunningOn.Compositor,
   342             "just anim1, translate at 2s");
   343     omta_is("opacity", 1, RunningOn.MainThread, "just anim1, opacity at 2s");
   344   advance_clock(1000);
   345     omta_is("transform", { tx: 48 }, RunningOn.Compositor,
   346             "just anim1, translate at 3s");
   347     omta_is("opacity", 1, RunningOn.MainThread, "just anim1, opacity at 3s");
   348   done_div();
   349 });
   351 // Swap elements
   352 addAsyncTest(function *() {
   353   new_div("animation: anim1 linear 10s, anim2 linear 10s");
   354   yield waitForPaints();
   355     omta_is("transform", { tx: 0 }, RunningOn.Either,
   356             "anim1 + anim2, translate at start");
   357     omta_is("opacity", 0, RunningOn.Compositor,
   358             "anim1 + anim2, opacity at start");
   359   advance_clock(1000);
   360     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
   361             "anim1 + anim2, translate at 1s");
   362     omta_is("opacity", 0.1, RunningOn.Compositor,
   363             "anim1 + anim2, opacity at 1s");
   364   // swap anim1 and anim2, change duration of anim2
   365   gDiv.style.animation = "anim2 linear 5s, anim1 linear 10s";
   366   yield waitForPaintsFlushed();
   367     omta_is("transform", { tx: 16 }, RunningOn.Compositor,
   368             "anim2 + anim1, translate at 1s");
   369     omta_is("opacity", 0.2, RunningOn.Compositor,
   370             "anim2 + anim1, opacity at 1s");
   371   advance_clock(1000);
   372     omta_is("transform", { tx: 32 }, RunningOn.Compositor,
   373             "anim2 + anim1, translate at 2s");
   374     omta_is("opacity", 0.4, RunningOn.Compositor,
   375             "anim2 + anim1, opacity at 2s");
   376   // list anim2 twice, last duration wins, original start time still applies
   377   gDiv.style.animation = "anim2 linear 5s, anim1 linear 10s, anim2 linear 20s";
   378   yield waitForPaintsFlushed();
   379     omta_is("transform", { tx: 32 }, RunningOn.Compositor,
   380             "anim2 + anim1 + anim2, translate at 2s");
   381     // Bug 980769
   382     todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.1",
   383             "anim2 + anim1 + anim2, opacity at 2s");
   384   // drop one of the anim2, and list anim3 as well, which animates
   385   // the same property as anim2
   386   gDiv.style.animation = "anim1 linear 10s, anim2 linear 20s, anim3 linear 10s";
   387   yield waitForPaintsFlushed();
   388     omta_is("transform", { tx: 32 }, RunningOn.Compositor,
   389             "anim1 + anim2 + anim3, translate at 2s");
   390     // Bug 980769
   391     todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0",
   392             "anim1 + anim2 + anim3, opacity at 2s");
   393   advance_clock(1000);
   394     omta_is("transform", { tx: 48 }, RunningOn.Compositor,
   395             "anim1 + anim2 + anim3, translate at 3s");
   396     // Bug 980769
   397     todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.1",
   398             "anim1 + anim2 + anim3, opacity at 3s");
   399   // now swap the anim3 and anim2 order
   400   gDiv.style.animation = "anim1 linear 10s, anim3 linear 10s, anim2 linear 20s";
   401   yield waitForPaintsFlushed();
   402     omta_is("transform", { tx: 48 }, RunningOn.Compositor,
   403             "anim1 + anim3 + anim2, translate at 3s");
   404     // Bug 980769
   405     todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.15",
   406             "anim1 + anim3 + anim2, opacity at 3s");
   407   advance_clock(2000); // (unlike test_animations.html, we seek 2s forwards here
   408                        // since at 4s anim2 and anim3 produce the same result so
   409                        // we can't tell which won.)
   410     omta_is("transform", { tx: 80 }, RunningOn.Compositor,
   411             "anim1 + anim3 + anim2, translate at 5s");
   412     // Bug 980769
   413     todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.25",
   414             "anim1 + anim3 + anim2, opacity at 5s");
   415   // swap anim3 and anim2 back
   416   gDiv.style.animation = "anim1 linear 10s, anim2 linear 20s, anim3 linear 10s";
   417   yield waitForPaintsFlushed();
   418     omta_is("transform", { tx: 80 }, RunningOn.Compositor,
   419             "anim1 + anim2 + anim3, translate at 5s");
   420     // Bug 980769
   421     todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.3",
   422             "anim1 + anim2 + anim3, opacity at 5s");
   423   // seek past end of anim1
   424   advance_clock(5100);
   425   yield waitForPaints();
   426     omta_is("transform", { tx: 0 }, RunningOn.MainThread,
   427             "anim1 + anim2 + anim3, translate at 10.1s");
   428   // Change the animation fill mode on the completed animation.
   429   gDiv.style.animation =
   430     "anim1 linear 10s forwards, anim2 linear 20s, anim3 linear 10s";
   431   yield waitForPaintsFlushed();
   432     omta_is("transform", { tx: 100 }, RunningOn.MainThread,
   433             "anim1 + anim2 + anim3, translate at 10.1s with fill mode");
   434   advance_clock(900);
   435     omta_is("transform", { tx: 100 }, RunningOn.MainThread,
   436             "anim1 + anim2 + anim3, translate at 11s with fill mode");
   437   // Change the animation duration on the completed animation, so it is
   438   // no longer completed.
   439   // XXX Not sure about this---there seems to be a bug in test_animations.html
   440   // in that it drops the fill mode but the test comment says it has a fill mode
   441   gDiv.style.animation = "anim1 linear 20s, anim2 linear 20s, anim3 linear 10s";
   442   yield waitForPaintsFlushed();
   443     omta_is("transform", { tx: 82 }, RunningOn.Compositor,
   444             "anim1 + anim2 + anim3, translate at 11s with fill mode");
   445     // Bug 980769 - We should get 0.9 but instead 
   446     todo_is(SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, "opacity"), "0.9",
   447             "anim1 + anim2 + anim3, opacity at 11s");
   448   done_div();
   449 });
   451 /*
   452  * css3-animations:  3. Keyframes
   453  * http://dev.w3.org/csswg/css3-animations/#keyframes
   454  */
   456 // Test the rules on keyframes that lack a 0% or 100% rule:
   457 // (simultaneously, test that reverse animations have their keyframes
   458 // run backwards)
   460 addAsyncTest(function *() {
   461   // 100px at 0%, 50px at 50%, 150px at 100%
   462   new_div("transform: translate(100px); " +
   463           "animation: kf1 ease 1s alternate infinite");
   464   advance_clock(0);
   465   yield waitForPaints();
   466   omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-0% at 0.0s");
   467   advance_clock(100);
   468   omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.2) },
   469                  RunningOn.Compositor, 0.01, "no-0% at 0.1s");
   470   advance_clock(200);
   471   omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.6) },
   472                  RunningOn.Compositor, 0.01, "no-0% at 0.3s");
   473   advance_clock(200);
   474   omta_is("transform", { tx: 50 }, RunningOn.Compositor, "no-0% at 0.5s");
   475   advance_clock(200);
   476   omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.4) },
   477                  RunningOn.Compositor, 0.01, "no-0% at 0.7s");
   478   advance_clock(200);
   479   omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.8) },
   480                  RunningOn.Compositor, 0.01, "no-0% at 0.9s");
   481   advance_clock(100);
   482   omta_is("transform", { tx: 150 }, RunningOn.Compositor, "no-0% at 1.0s");
   483   advance_clock(100);
   484   omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.8) },
   485                  RunningOn.Compositor, 0.01, "no-0% at 1.1s");
   486   advance_clock(300);
   487   omta_is_approx("transform", { tx: 50 + 100 * gTF.ease(0.2) },
   488                  RunningOn.Compositor, 0.01, "no-0% at 1.4s");
   489   advance_clock(300);
   490   omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.6) },
   491                  RunningOn.Compositor, 0.01, "no-0% at 1.7s");
   492   advance_clock(200);
   493   omta_is_approx("transform", { tx: 100 - 50 * gTF.ease(0.2) },
   494                  RunningOn.Compositor, 0.01, "no-0% at 1.9s");
   495   advance_clock(100);
   496   omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-0% at 2.0s");
   497   done_div();
   499   // 150px at 0%, 50px at 50%, 100px at 100%
   500   new_div("transform: translate(100px); " +
   501           "animation: kf2 ease-in 1s alternate infinite");
   502   yield waitForPaints();
   503   omta_is("transform", { tx: 150 }, RunningOn.Compositor, "no-100% at 0.0s");
   504   advance_clock(100);
   505   omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.2) },
   506                  RunningOn.Compositor, 0.01, "no-100% at 0.1s");
   507   advance_clock(200);
   508   omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.6) },
   509                  RunningOn.Compositor, 0.01, "no-100% at 0.3s");
   510   advance_clock(200);
   511   omta_is("transform", { tx: 50 }, RunningOn.Compositor, "no-100% at 0.5s");
   512   advance_clock(200);
   513   omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.4) },
   514                  RunningOn.Compositor, 0.01, "no-100% at 0.7s");
   515   advance_clock(200);
   516   omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.8) },
   517                  RunningOn.Compositor, 0.01, "no-100% at 0.9s");
   518   advance_clock(100);
   519   omta_is("transform", { tx: 100 }, RunningOn.Compositor, "no-100% at 1.0s");
   520   advance_clock(100);
   521   omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.8) },
   522                  RunningOn.Compositor, 0.01, "no-100% at 1.1s");
   523   advance_clock(300);
   524   omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_in(0.2) },
   525                  RunningOn.Compositor, 0.01, "no-100% at 1.4s");
   526   advance_clock(300);
   527   omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.6) },
   528                  RunningOn.Compositor, 0.01, "no-100% at 1.7s");
   529   advance_clock(200);
   530   omta_is_approx("transform", { tx: 150 - 100 * gTF.ease_in(0.2) },
   531                  RunningOn.Compositor, 0.01, "no-100% at 1.9s");
   532   advance_clock(100);
   533   omta_is("transform", { tx: 150 }, RunningOn.Compositor, "no-100% at 2.0s");
   534   done_div();
   536   // 50px at 0%, 100px at 25%, 50px at 100%
   537   new_div("transform: translate(50px); " +
   538           "animation: kf3 ease-out 1s alternate infinite");
   539   yield waitForPaints();
   540   omta_is("transform", { tx: 50 }, RunningOn.Compositor,
   541           "no-0%-no-100% at 0.0s");
   542   advance_clock(50);
   543   omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_out(0.2) },
   544                  RunningOn.Compositor, 0.01, "no-0%-no-100% at 0.05s");
   545   advance_clock(100);
   546   omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_out(0.6) },
   547                  RunningOn.Compositor, 0.01, "no-0%-no-100% at 0.15s");
   548   advance_clock(100);
   549   omta_is("transform", { tx: "100px" }, RunningOn.Compositor,
   550           "no-0%-no-100% at 0.25s");
   551   advance_clock(300);
   552   omta_is_approx("transform", { tx: 100 - 50 * gTF.ease_out(0.4) },
   553                  RunningOn.Compositor, 0.01, "no-0%-no-100% at 0.55s");
   554   advance_clock(300);
   555   omta_is_approx("transform", { tx: 100 - 50 * gTF.ease_out(0.8) },
   556                  RunningOn.Compositor, 0.01, "no-0%-no-100% at 0.85s");
   557   advance_clock(150);
   558   omta_is("transform", { tx: 50 }, RunningOn.Compositor,
   559           "no-0%-no-100% at 1.0s");
   560   advance_clock(150);
   561   omta_is_approx("transform", { tx: 100 - 50 * gTF.ease_out(0.8) },
   562                  RunningOn.Compositor, 0.01, "no-0%-no-100% at 1.15s");
   563   advance_clock(450);
   564   omta_is_approx("transform", { tx: 100 - 50 * gTF.ease_out(0.2) },
   565                  RunningOn.Compositor, 0.01, "no-0%-no-100% at 1.6s");
   566   advance_clock(250);
   567   omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_out(0.6) },
   568                  RunningOn.Compositor, 0.01, "no-0%-no-100% at 1.85s");
   569   advance_clock(100);
   570   omta_is_approx("transform", { tx: 50 + 50 * gTF.ease_out(0.2) },
   571                  RunningOn.Compositor, 0.01, "no-0%-no-100% at 1.95s");
   572   advance_clock(50);
   573   omta_is("transform", { tx: 50 }, RunningOn.Compositor,
   574           "no-0%-no-100% at 2.0s");
   575   done_div();
   577   // Test that non-animatable properties are ignored.
   578   // Simultaneously, test that the block is still honored, and that
   579   // we still override the value when two consecutive keyframes have
   580   // the same value.
   581   new_div("animation: kf4 ease 10s");
   582   yield waitForPaints();
   583   var cs = window.getComputedStyle(gDiv);
   584   is(cs.display, "block",
   585      "non-animatable properties should be ignored (linear, 0s)");
   586   omta_is("transform", { tx: 37 }, RunningOn.Compositor,
   587           "animatable properties should still apply (linear, 0s)");
   588   advance_clock(1000);
   589   is(cs.display, "block",
   590      "non-animatable properties should be ignored (linear, 1s)");
   591   omta_is("transform", { tx: 37 }, RunningOn.Compositor,
   592           "animatable properties should still apply (linear, 1s)");
   593   done_div();
   594   new_div("animation: kf4 step-start 10s");
   595   yield waitForPaints();
   596   cs = window.getComputedStyle(gDiv);
   597   is(cs.display, "block",
   598      "non-animatable properties should be ignored (step-start, 0s)");
   599   omta_is("transform", { tx: 37 }, RunningOn.Compositor,
   600           "animatable properties should still apply (step-start, 0s)");
   601   advance_clock(1000);
   602   is(cs.display, "block",
   603      "non-animatable properties should be ignored (step-start, 1s)");
   604   omta_is("transform", { tx: 37 }, RunningOn.Compositor,
   605           "animatable properties should still apply (step-start, 1s)");
   606   done_div();
   608   // Test cascading of the keyframes within an @keyframes rule.
   609   new_div("animation: kf_cascade1 linear 10s");
   610   yield waitForPaints();
   611   //    0%: 30px
   612   //   50%: 20px
   613   //   75%: 20px
   614   //   85%: 30px
   615   // 85.1%: 60px
   616   //  100%: 70px
   617   omta_is("transform", { tx: 30 }, RunningOn.Compositor, "kf_cascade1 at 0s");
   618   advance_clock(2500);
   619   omta_is("transform", { tx: 25 }, RunningOn.Compositor, "kf_cascade1 at 2.5s");
   620   advance_clock(2500);
   621   omta_is("transform", { tx: 20 }, RunningOn.Compositor, "kf_cascade1 at 5s");
   622   advance_clock(2000);
   623   omta_is("transform", { tx: 20 }, RunningOn.Compositor, "kf_cascade1 at 7s");
   624   advance_clock(500);
   625   omta_is("transform", { tx: 20 }, RunningOn.Compositor, "kf_cascade1 at 7.5s");
   626   advance_clock(500);
   627   omta_is("transform", { tx: 25 }, RunningOn.Compositor, "kf_cascade1 at 8s");
   628   advance_clock(500);
   629   omta_is("transform", { tx: 30 }, RunningOn.Compositor, "kf_cascade1 at 8.5s");
   630   advance_clock(10);
   631   // For some reason we get an error of 0.0003 for this test only
   632   omta_is_approx("transform", { tx: 60 }, RunningOn.Compositor, 0.001,
   633                  "kf_cascade1 at 8.51s");
   634   advance_clock(745);
   635   omta_is("transform", { tx: 65 }, RunningOn.Compositor,
   636           "kf_cascade1 at 9.2505s");
   637   done_div();
   639   // Test cascading of the @keyframes rules themselves.
   640   new_div("animation: kf_cascade2 linear 10s");
   641   yield waitForPaints();
   642   omta_is("opacity", 1, RunningOn.MainThread,
   643           "last @keyframes rule with transform should win");
   644   omta_is("transform", { tx: 100 }, RunningOn.Compositor,
   645           "last @keyframes rule with transform should win");
   646   done_div();
   647 });
   649 //----------------------------------------------------------------------
   650 //
   651 // Helper functions from test_animations.html
   652 //
   653 //----------------------------------------------------------------------
   655 function new_div(style) {
   656   if (gDiv !== null) {
   657     ok(false, "test author forgot to call done_div");
   658   }
   659   if (typeof(style) != "string") {
   660     ok(false, "test author forgot to pass style argument");
   661   }
   662   gDiv = document.createElement("div");
   663   gDiv.classList.add("target");
   664   gDiv.setAttribute("style", style);
   665   gDisplay.appendChild(gDiv);
   666   gDiv.clientTop;
   667 }
   669 function done_div() {
   670   if (gDiv === null) {
   671     ok(false, "test author forgot to call new_div");
   672   }
   673   gDisplay.removeChild(gDiv);
   674   gDiv = null;
   675 }
   677 function listen() {
   678   gEventsReceived = [];
   679   function listener(event) {
   680     gEventsReceived.push(event);
   681   }
   682   gDiv.addEventListener("animationstart", listener, false);
   683   gDiv.addEventListener("animationiteration", listener, false);
   684   gDiv.addEventListener("animationend", listener, false);
   685 }
   687 function check_events(events_expected, desc) {
   688   // This function checks that the list of events_expected matches
   689   // the received events -- but it only checks the properties that
   690   // are present on events_expected.
   691   is(gEventsReceived.length, events_expected.length,
   692      "number of events received for " + desc);
   693   for (var i = 0,
   694        i_end = Math.min(events_expected.length, gEventsReceived.length);
   695        i != i_end; ++i) {
   696     var exp = events_expected[i];
   697     var rec = gEventsReceived[i];
   698     for (var prop in exp) {
   699       if (prop == "elapsedTime") {
   700         // Allow floating point error.
   701         ok(Math.abs(rec.elapsedTime - exp.elapsedTime) < 0.000002,
   702            "events[" + i + "]." + prop + " for " + desc +
   703            " received=" + rec.elapsedTime + " expected=" + exp.elapsedTime);
   704       } else {
   705         is(rec[prop], exp[prop], "events[" + i + "]." + prop + " for " + desc);
   706       }
   707     }
   708   }
   709   for (i = events_expected.length; i < gEventsReceived.length; ++i) {
   710     ok(false, "unexpected " + gEventsReceived[i].type + " event for " + desc);
   711   }
   712   gEventsReceived = [];
   713 }
   715 function advance_clock(milliseconds) {
   716   SpecialPowers.DOMWindowUtils.advanceTimeAndRefresh(milliseconds);
   717 }
   719 //----------------------------------------------------------------------
   720 //
   721 // Helper functions for querying the compositor thread
   722 //
   723 //----------------------------------------------------------------------
   725 // Returns a Promise that resolves once all paints have completed
   726 function waitForPaints() {
   727   return new Promise(function(resolve, reject) {
   728     waitForAllPaints(resolve);
   729   });
   730 }
   732 // As with waitForPaints but also flushes pending style changes before waiting
   733 function waitForPaintsFlushed() {
   734   return new Promise(function(resolve, reject) {
   735     waitForAllPaintsFlushed(resolve);
   736   });
   737 }
   739 //----------------------------------------------------------------------
   740 //
   741 // Helper functions for working with animated values
   742 //
   743 //----------------------------------------------------------------------
   745 const RunningOn = {
   746   MainThread: 0,
   747   Compositor: 1,
   748   Either: 2
   749 };
   751 function omta_is(property, expected, runningOn, desc) {
   752   return omta_is_approx(property, expected, runningOn, 0, desc);
   753 }
   755 function omta_is_approx(property, expected, runningOn, tolerance, desc) {
   756   // Check input
   757   const omtaProperties = [ "transform", "opacity" ];
   758   if (omtaProperties.indexOf(property) === -1) {
   759     ok(false, property + " is not an OMTA property");
   760     return;
   761   }
   762   var isTransform = property == "transform";
   763   var normalize = isTransform ? convertTo3dMatrix : parseFloat;
   764   var compare = isTransform ?
   765                 matricesRoughlyEqual :
   766                 function(a, b, error) { return Math.abs(a - b) <= error; };
   767   var normalizedToString = isTransform ?
   768                            convert3dMatrixToString :
   769                            JSON.stringify;
   771   // Get actual values
   772   var compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(gDiv, property);
   773   var computedStr   = window.getComputedStyle(gDiv)[property];
   775   // Prepare expected value
   776   var expectedValue = normalize(expected);
   777   if (expectedValue === null) {
   778     ok(false, desc + ": test author should provide a valid 'expected' value" +
   779               " - got " + expected.toString());
   780     return;
   781   }
   783   // Check expected value appears in the right place
   784   var actualStr;
   785   switch (runningOn) {
   786     case RunningOn.Either:
   787       runningOn = compositorStr !== "" ?
   788                   RunningOn.Compositor :
   789                   RunningOn.MainThread;
   790       actualStr = compositorStr !== "" ? compositorStr : computedStr;
   791       break;
   793     case RunningOn.Compositor:
   794       if (compositorStr === "") {
   795         ok(false, desc + ": should be animating on compositor");
   796         return;
   797       }
   798       actualStr = compositorStr;
   799       break;
   801     default:
   802       if (compositorStr !== "") {
   803         ok(false, desc + ": should NOT be animating on compositor");
   804         return;
   805       }
   806       actualStr = computedStr;
   807       break;
   808   }
   810   // Compare animated value with expected
   811   var actualValue = normalize(actualStr);
   812   if (actualValue === null) {
   813     ok(false, desc + ": should return a valid result - got " + actualStr);
   814     return;
   815   }
   816   ok(compare(expectedValue, actualValue, tolerance),
   817      desc + " - got " + actualStr + ", expected " +
   818      normalizedToString(expectedValue));
   820   // For compositor animations do an additional check that they match
   821   // the value calculated on the main thread
   822   if (runningOn === RunningOn.Compositor) {
   823     var computedValue = normalize(computedStr);
   824     if (computedValue === null) {
   825       ok(false, desc + ": test framework should parse computed style" +
   826                 " - got " + computedStr);
   827       return;
   828     }
   829     ok(compare(computedValue, actualValue, 0),
   830        desc + ": OMTA style and computed style should be equal" +
   831        " - OMTA " + actualStr + ", computed " + computedStr);
   832   }
   833 }
   835 function matricesRoughlyEqual(a, b, tolerance) {
   836   tolerance = tolerance || 0.0001;
   837   for (var i = 0; i < 4; i++) {
   838     for (var j = 0; j < 4; j++) {
   839       if (Math.abs(a[i][j] - b[i][j]) > tolerance)
   840         return false;
   841     }
   842   }
   843   return true;
   844 }
   846 // Converts something representing an transform into a 3d matrix in column-major
   847 // order.
   848 // The following are supported:
   849 //  "matrix(...)"
   850 //  "matrix3d(...)"
   851 //  [ 1, 0, 0, ... ]
   852 //  { a: 1, ty: 23 } etc.
   853 function convertTo3dMatrix(matrixLike) {
   854   if (typeof(matrixLike) == "string") {
   855     return convertStringTo3dMatrix(matrixLike);
   856   } else if (Array.isArray(matrixLike)) {
   857     return convertArrayTo3dMatrix(matrixLike);
   858   } else if (typeof(matrixLike) == "object") {
   859     return convertObjectTo3dMatrix(matrixLike);
   860   } else {
   861     return null;
   862   }
   863 }
   865 // Converts strings of the format "matrix(...)" and "matrix3d(...)" to a 3d
   866 // matrix
   867 function convertStringTo3dMatrix(str) {
   868   if (str == "none")
   869     return convertArrayTo3dMatrix([1, 0, 0, 1, 0, 0]);
   870   var result = str.match("^matrix(3d)?\\(");
   871   if (result === null)
   872     return null;
   874   return convertArrayTo3dMatrix(
   875       str.substring(result[0].length, str.length-1)
   876          .split(",")
   877          .map(function(component) {
   878            return Number(component);
   879          })
   880     );
   881 }
   883 // Takes an array of numbers of length 6 (2d matrix) or 16 (3d matrix)
   884 // representing a matrix specified in column-major order and returns a 3d matrix
   885 // represented as an array of arrays
   886 function convertArrayTo3dMatrix(array) {
   887   if (array.length == 6) {
   888     return convertObjectTo3dMatrix(
   889       { a: array[0], b: array[1],
   890         c: array[2], d: array[3],
   891         e: array[4], f: array[5] } );
   892   } else if (array.length == 16) {
   893     return [
   894       array.slice(0, 3),
   895       array.slice(4, 7),
   896       array.slice(8, 11),
   897       array.slice(12, 15)
   898     ];
   899   } else {
   900     return null;
   901   }
   902 }
   904 // Takes an object of the form { a: 1.1, e: 23 } and builds up a 3d matrix
   905 // with unspecified values filled in with identity values.
   906 function convertObjectTo3dMatrix(obj) {
   907   return [
   908     [
   909       obj.a || obj.sx || obj.m11 || 1,
   910       obj.b || obj.m12 || 0,
   911       obj.m13 || 0,
   912       obj.m14 || 0
   913     ], [
   914       obj.c || obj.m21 || 0,
   915       obj.d || obj.sy || obj.m22 || 1,
   916       obj.m23 || 0,
   917       obj.m24 || 0
   918     ], [
   919       obj.m31 || 0,
   920       obj.m32 || 0,
   921       obj.sz || obj.m33 || 1,
   922       obj.m34 || 0
   923     ], [
   924       obj.e || obj.tx || obj.m41 || 0,
   925       obj.f || obj.ty || obj.m42 || 0,
   926       obj.tz || obj.m43 || 0,
   927       obj.m44 || 1
   928     ]
   929   ];
   930 }
   932 function convert3dMatrixToString(matrix) {
   933   if (is2d(matrix)) {
   934     return "matrix(" +
   935            [ matrix[0][0], matrix[0][1],
   936              matrix[1][0], matrix[1][1],
   937              matrix[3][0], matrix[3][1] ].join(", ") + ")";
   938   } else {
   939     return "matrix3d(" +
   940             matrix.reduce(function(outer, inner) {
   941                 return outer.concat(inner);
   942             }).join(", ") + ")";
   943   }
   944 }
   946 function is2d(matrix) {
   947   return matrix[0][2] === 0 && matrix[0][3] === 0 &&
   948          matrix[1][2] === 0 && matrix[1][3] === 0 &&
   949          matrix[2][0] === 0 && matrix[2][1] === 0 &&
   950          matrix[2][2] === 1 && matrix[2][3] === 0 &&
   951          matrix[3][2] === 0 && matrix[3][3] === 1;
   952 }
   953 </script>
   954 </html>

mercurial