author | Boris Chiou <boris.chiou@gmail.com> |
Mon, 18 Mar 2019 18:05:04 +0000 | |
changeset 464941 | 9fe743aa6c94f49346902934f40f8f55663bc54d |
parent 464940 | af39dc228d19a34892e32faab8fb105352708099 |
child 464942 | 08126c1f53fe82ab685bd3e75e97ce7bf516a3d9 |
push id | 80792 |
push user | bchiou@mozilla.com |
push date | Tue, 19 Mar 2019 08:04:11 +0000 |
treeherder | autoland@08126c1f53fe [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | hiro, birtles |
bugs | 1425837 |
milestone | 68.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/dom/animation/test/chrome.ini +++ b/dom/animation/test/chrome.ini @@ -1,12 +1,13 @@ [DEFAULT] prefs = dom.animations-api.compositing.enabled=true gfx.omta.background-color=true + layout.css.individual-transform.enabled=true support-files = testcommon.js ../../imptests/testharness.js ../../imptests/testharnessreport.js !/dom/animation/test/chrome/file_animate_xrays.html [chrome/test_animate_xrays.html] # file_animate_xrays.html needs to go in mochitest.ini since it is served
--- a/dom/animation/test/chrome/test_running_on_compositor.html +++ b/dom/animation/test/chrome/test_running_on_compositor.html @@ -1052,10 +1052,95 @@ promise_test(async t => { await waitForPaints(); assert_animation_is_not_running_on_compositor(animation, 'background-color animation should NOT be running on the compositor ' + 'if the pref is disabled'); }, 'background-color animation does not run on the compositor if the pref ' + 'is disabled'); +promise_test(async t => { + const div = addDiv(t); + const animation = div.animate({ translate: ['0px', '100px'] }, + 100 * MS_PER_SEC); + + await waitForAnimationReadyToRestyle(animation); + await waitForPaints(); + + assert_animation_is_running_on_compositor(animation, + 'translate animation should be running on the compositor'); +}, 'translate animation runs on the compositor'); + +promise_test(async t => { + const div = addDiv(t); + const animation = div.animate({ rotate: ['0deg', '45deg'] }, + 100 * MS_PER_SEC); + + await waitForAnimationReadyToRestyle(animation); + await waitForPaints(); + + assert_animation_is_running_on_compositor(animation, + 'rotate animation should be running on the compositor'); +}, 'rotate animation runs on the compositor'); + +promise_test(async t => { + const div = addDiv(t); + const animation = div.animate({ scale: ['1 1', '2 2'] }, + 100 * MS_PER_SEC); + + await waitForAnimationReadyToRestyle(animation); + await waitForPaints(); + + assert_animation_is_running_on_compositor(animation, + 'scale animation should be running on the compositor'); +}, 'scale animation runs on the compositor'); + +promise_test(async t => { + const div = addDiv(t); + const animation = div.animate({ translate: ['0px', '100px'], + rotate: ['0deg', '45deg'], + transform: ['translate(20px)', + 'translate(30px)'] }, + 100 * MS_PER_SEC); + + await waitForAnimationReadyToRestyle(animation); + await waitForPaints(); + + assert_animation_is_running_on_compositor(animation, + 'multiple transform-like properties animation should be running on the ' + + 'compositor'); + + const properties = animation.effect.getProperties(); + properties.forEach(property => { + assert_true(property.runningOnCompositor, + property.property + ' is running on the compositor'); + }); +}, 'Multiple transform-like properties animation runs on the compositor'); + +promise_test(async t => { + const div = addDiv(t); + const animation = div.animate({ translate: ['0px', '100px'], + rotate: ['0deg', '45deg'], + transform: ['translate(20px)', + 'translate(30px)'] }, + 100 * MS_PER_SEC); + + div.style.setProperty('translate', '50px', 'important'); + getComputedStyle(div).translate; + + await waitForAnimationReadyToRestyle(animation); + await waitForPaints(); + + assert_animation_is_not_running_on_compositor(animation, + 'Animation overridden by an !important rule reports that it is ' + + 'NOT running on the compositor'); + + const properties = animation.effect.getProperties(); + properties.forEach(property => { + assert_true(!property.runningOnCompositor, + property.property + ' is not running on the compositor'); + }); +}, 'Multiple transform-like properties animation does not runs on the ' + + 'compositor because one of the transform-like property is overridden ' + + 'by an !important rule'); + </script> </body>
--- a/dom/base/nsDOMWindowUtils.cpp +++ b/dom/base/nsDOMWindowUtils.cpp @@ -3407,17 +3407,20 @@ nsDOMWindowUtils::GetOMTAStyle(Element* if (aProperty.EqualsLiteral("opacity")) { OMTAValue value = GetOMTAValue(frame, DisplayItemType::TYPE_OPACITY, GetWebRenderBridge()); if (value.type() == OMTAValue::Tfloat) { cssValue = new nsROCSSPrimitiveValue; cssValue->SetNumber(value.get_float()); } - } else if (aProperty.EqualsLiteral("transform")) { + } else if (aProperty.EqualsLiteral("transform") || + aProperty.EqualsLiteral("translate") || + aProperty.EqualsLiteral("rotate") || + aProperty.EqualsLiteral("scale")) { OMTAValue value = GetOMTAValue(frame, DisplayItemType::TYPE_TRANSFORM, GetWebRenderBridge()); if (value.type() == OMTAValue::TMatrix4x4) { cssValue = nsComputedDOMStyle::MatrixToCSSValue(value.get_Matrix4x4()); } } else if (aProperty.EqualsLiteral("background-color")) { OMTAValue value = GetOMTAValue( frame, DisplayItemType::TYPE_BACKGROUND_COLOR, GetWebRenderBridge());
--- a/layout/style/test/animation_utils.js +++ b/layout/style/test/animation_utils.js @@ -418,40 +418,52 @@ const ExpectComparisonTo = { // Many callers of this method will pass 'undefined' for // expectedComparisonResult. window.omta_is_approx = function(elem, property, expected, tolerance, runningOn, desc, expectedComparisonResult, pseudo) { // Check input // FIXME: Auto generate this array. - const omtaProperties = [ "transform", "opacity", "background-color" ]; + const omtaProperties = [ "transform", "translate", "rotate", "scale", + "opacity", "background-color" ]; if (!omtaProperties.includes(property)) { ok(false, property + " is not an OMTA property"); return; } var normalize; var compare; var normalizedToString = JSON.stringify; switch (property) { case "transform": + case "translate": + case "rotate": + case "scale": normalize = convertTo3dMatrix; compare = matricesRoughlyEqual; normalizedToString = convert3dMatrixToString; break; case "opacity": normalize = parseFloat; compare = function(a, b, error) { return Math.abs(a - b) <= error; }; break; default: - normalize = function(value) { return value; }; + normalize = value => value; compare = function(a, b, error) { return a == b; }; break; } + if (!!expected.compositorValue) { + const originalNormalize = normalize; + normalize = value => + !!value.compositorValue + ? originalNormalize(value.compositorValue) + : originalNormalize(value); + } + // Get actual values var compositorStr = SpecialPowers.DOMWindowUtils.getOMTAStyle(elem, property, pseudo); var computedStr = window.getComputedStyle(elem, pseudo)[property]; // Prepare expected value var expectedValue = normalize(expected); if (expectedValue === null) { @@ -502,19 +514,50 @@ const ExpectComparisonTo = { if (actualValue === null) { ok(false, desc + ": should return a valid result - got " + actualStr); return; } okOrTodo(compare(expectedValue, actualValue, tolerance), desc + " - got " + actualStr + ", expected " + normalizedToString(expectedValue)); - // For compositor animations do an additional check that they match - // the value calculated on the main thread - if (actualStr === compositorStr) { + // For transform-like properties, if we have multiple transform-like + // properties, the OMTA value and getComputedStyle() must be different, + // so use this flag to skip the following tests. + // FIXME: Putting this property on the expected value is a little bit odd. + // It's not really a product of the expected value, but rather the kind of + // test we're running. That said, the omta_is, omta_todo_is etc. methods are + // already pretty complex and adding another parameter would probably + // complicate things too much so this is fine for now. If we extend these + // functions any more, though, we should probably reconsider this API. + if (expected.usesMultipleProperties) { + return; + } + + if (typeof expected.computed !== 'undefined') { + // For some tests we specify a separate computed value for comparing + // with getComputedStyle. + // + // In particular, we do this for the individual transform functions since + // the form returned from getComputedStyle() reflects the individual + // properties (e.g. 'translate: 100px') while the form we read back from + // the compositor represents the combined result of all the transform + // properties as a single transform matrix (e.g. [0, 0, 0, 0, 100, 0]). + // + // Despite the fact that we can't directly compare the OMTA value against + // the getComputedStyle value in this case, it is still worth checking the + // result of getComputedStyle since it will help to alert us if some + // discrepancy arises between the way we calculate values on the main + // thread and compositor. + okOrTodo(computedStr == expected.computed, + desc + ": Computed style should be equal to " + + expected.computed); + } else if (actualStr === compositorStr) { + // For compositor animations do an additional check that they match + // the value calculated on the main thread var computedValue = normalize(computedStr); if (computedValue === null) { ok(false, desc + ": test framework should parse computed style" + " - got " + computedStr); return; } okOrTodo(compare(computedValue, actualValue, 0.0), desc + ": OMTA style and computed style should be equal" +
--- a/layout/style/test/mochitest.ini +++ b/layout/style/test/mochitest.ini @@ -1,16 +1,17 @@ [DEFAULT] prefs = dom.animations-api.compositing.enabled=true dom.animations-api.core.enabled=true dom.animations-api.getAnimations.enabled=true dom.animations-api.implicit-keyframes.enabled=true dom.animations-api.timelines.enabled=true gfx.omta.background-color=true + layout.css.individual-transform.enabled=true layout.css.step-position-jump.enabled=true support-files = animation_utils.js ccd-quirks.html ccd.sjs ccd-standards.html chrome/bug418986-2.js chrome/match.png
--- a/layout/style/test/test_animations_omta.html +++ b/layout/style/test/test_animations_omta.html @@ -2512,10 +2512,103 @@ if (SpecialPowers.DOMWindowUtils.layerMa "background-color on <a> element after the link is visited"); extraStyle.remove(); done_element(); gDiv = null; }); } +// Normal translate animation. +addAsyncAnimTest(async function() { + new_div("translate: 100px; " + + "transition: translate 10s linear"); + await waitForPaintsFlushed(); + + gDiv.style.translate = "200px"; + await waitForPaintsFlushed(); + + omta_is("translate", { compositorValue: { tx: 100 }, computed: "100px" }, + RunningOn.Compositor, + "translate transition runs on compositor thread"); + + advance_clock(5000); + omta_is("translate", { compositorValue: { tx: 150 }, computed: "150px" }, + RunningOn.Compositor, "translate on compositor at 5s"); + + done_div(); +}); + +// Normal rotate animation. +addAsyncAnimTest(async function() { + new_div("rotate: 0deg; " + + "transition: rotate 10s linear"); + await waitForPaintsFlushed(); + + gDiv.style.rotate = "90deg"; + await waitForPaintsFlushed(); + + omta_is("rotate", { compositorValue: [ 1, 0, 0, 1, 0, 0 ], computed: "0deg"}, + RunningOn.Compositor, "rotate transition runs on compositor thread"); + + advance_clock(5000); + omta_is("rotate", + { compositorValue: [ Math.cos(Math.PI / 4), Math.sin(Math.PI / 4), + -Math.sin(Math.PI / 4), Math.cos(Math.PI / 4), + 0, 0 ], + computed: "45deg" }, RunningOn.Compositor, + "rotate on compositor at 5s"); + + done_div(); +}); + +// Normal scale animation. +addAsyncAnimTest(async function() { + new_div("scale: 1 1; " + + "transition: scale 10s linear"); + await waitForPaintsFlushed(); + + gDiv.style.scale = "2 2"; + await waitForPaintsFlushed(); + + omta_is("scale", { compositorValue: [ 1, 0, 0, 1, 0, 0 ], computed: "1" }, + RunningOn.Compositor, "scale transition runs on compositor thread"); + + advance_clock(5000); + omta_is("scale", + { compositorValue: [ 1.5, 0, 0, 1.5, 0, 0 ], computed: "1.5" }, + RunningOn.Compositor, "scale on compositor at 5s"); + + done_div(); +}); + +// Normal multiple transform-like properties animation. +addAsyncAnimTest(async function() { + new_div("translate: 100px; " + + "scale: 1 1; " + + "transform: translate(200px); " + + "transition: all 10s linear"); + await waitForPaintsFlushed(); + + gDiv.style.translate = "200px"; + gDiv.style.scale = "2 2"; + gDiv.style.transform = "translate(300px)"; + await waitForPaintsFlushed(); + + omta_is("transform", { compositorValue: { tx: 300 }, + usesMultipleProperties: true }, + RunningOn.Compositor, + "transform-like properties transition runs on compositor thread"); + + advance_clock(5000); + omta_is("transform", + // The order is: translate, scale, transform. + // So the translate() in transform should be multiplied by 1.5. + { compositorValue: [ 1.5, 0, 0, 1.5, (150 + 250*1.5), 0 ], + usesMultipleProperties: true }, + RunningOn.Compositor, + "transform-like properties on compositor at 5s"); + + done_div(); +}); + </script> </html>
--- a/layout/style/test/test_transitions_per_property.html +++ b/layout/style/test/test_transitions_per_property.html @@ -2836,21 +2836,21 @@ function test_rotate_transition(prop) { // // We don't test for interpolation of the numbers here since it's quite // complicated and this is tested by the web-platform tests for this property. // Now that we have web-platform tests for animation properties the main // purpose of the tests in this file is to check that transitions run on the // properties we expect them to. div.style.transitionProperty = 'none'; div.style[prop] = '0 1 0 45deg'; - is(cs[prop], '0 1 0 45deg', + is(cs[prop], 'y 45deg', `rotate property ${prop}: computed value before transition`); div.style.transitionProperty = prop; div.style[prop] = '0 1 0 145deg'; - is(cs[prop], '0 1 0 70deg', + is(cs[prop], 'y 70deg', `rotate property ${prop}: interpolation of angles`); check_distance(prop, '0 1 0 45deg', '0 1 0 70deg', '0 1 0 145deg'); } function test_scale_transition(prop) { // One value: <number> test_number_transition(prop); @@ -2878,31 +2878,53 @@ function test_scale_transition(prop) { function test_translate_transition(prop) { // One value: <length-percentage> test_length_transition(prop); test_length_unclamped(prop); test_percent_transition(prop); test_percent_unclamped(prop); // Two values: <length-percentage> <length-percentage> + // Note: Cannot use test_length_percent_pair_transition(prop) and + // test_length_percent_calc_transition(prop) because we don't resolve the + // percentage. test_length_pair_transition(prop); - test_length_percent_pair_transition(prop); - test_length_percent_calc_transition(prop); + + div.style.setProperty("transition-property", "none", ""); + div.style.setProperty(prop, "4px 50%", ""); + is(cs.getPropertyValue(prop), "4px 50%", + `length-valued property ${prop}: computed value before transition`); + div.style.setProperty("transition-property", prop, ""); + div.style.setProperty(prop, "12px 70%", ""); + is(cs.getPropertyValue(prop), "6px 55%", + `length-valued property ${prop}: interpolation of lengths`); + check_distance(prop, "4px 50%", "6px 55%", "12px 70%"); + + div.style.setProperty("transition-property", "none", ""); + div.style.setProperty(prop, "4px 50%", ""); + is(cs.getPropertyValue(prop), "4px 50%", + `length-valued property ${prop}: computed value before transition`); + div.style.setProperty("transition-property", prop, ""); + div.style.setProperty(prop, "20% 20px", ""); + is(cs.getPropertyValue(prop), "calc(5% + 3px) calc(37.5% + 5px)", + `length-valued property ${prop}: interpolation of lengths`); + check_distance(prop, "4px 50%", "calc(5% + 3px) calc(37.5% + 5px)", + "20% 20px"); // We can't use test_length_percent_pair_unclamped here since // it assumes that "0px 0px" is serialized as "0px 0px" but // translate should serialize it as "0px". // Three values: <length-percentage> <length-percentage> <length> div.style.transitionProperty = 'none'; div.style[prop] = '10px 200% 30px'; - is(cs[prop], '10px 20px 30px', + is(cs[prop], '10px 200% 30px', `translate property ${prop}: computed value before transition`); div.style.transitionProperty = prop; div.style[prop] = '50px 600% 70px'; - is(cs[prop], '20px 30px 40px', + is(cs[prop], '20px 300% 40px', `translate property ${prop}: interpolation of three values`); check_distance(prop, '10px 20px 30px', '20px 30px 40px', '50px 60px 70px'); } function test_font_variations_transition(prop) { is(prop, "font-variation-settings", "only designed for one property"); div.style.setProperty("transition-property", "none", "");