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