Index: LayoutTests/animations/resources/composited-animation-test.js |
diff --git a/LayoutTests/animations/resources/composited-animation-test.js b/LayoutTests/animations/resources/composited-animation-test.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..e7d1830366d024dc7e46730dce3e5aec0c25df0a |
--- /dev/null |
+++ b/LayoutTests/animations/resources/composited-animation-test.js |
@@ -0,0 +1,204 @@ |
+'use strict'; |
+ |
+class CompositedAnimationTestCommon { |
+ constructor(disableThreadedAnimation) { |
+ this.disableThreadedAnimation = disableThreadedAnimation; |
+ this.tests = []; |
+ this.next_sample_id = 1; |
+ |
+ this.createStyles(); |
+ this.createStaticElements(); |
+ } |
+ |
+ createStyles() { |
+ var item = document.createElement('style'); |
+ item.type = 'text/css'; |
+ item.textContent = `.item { |
+ width: 20px; |
+ height: 20px; |
+ position: relative; |
+ background: black; |
+ float: left; |
+ }`; |
+ |
+ var marker = document.createElement('style'); |
+ marker.type = 'text/css'; |
+ marker.textContent = `.marker { |
+ width: 5px; |
+ height: 5px; |
+ display: inline-block; |
+ background: orange; |
+ margin: 15px; |
+ }`; |
+ |
+ document.head.appendChild(item); |
+ document.head.appendChild(marker); |
+ } |
+ |
+ createStaticElements() { |
+ var error = document.createElement("span"); |
+ error.id = 'error'; |
+ error.style.color = 'red'; |
+ document.body.appendChild(error); |
+ |
+ var wrapper = document.createElement("div"); |
+ wrapper.id = 'wrapper'; |
+ document.body.appendChild(wrapper); |
+ } |
+ |
+ createTestElements() { |
+ var testId = 1; |
+ |
+ this.tests.forEach(function(test) { |
+ var testWrapper = document.createElement("div"); |
+ wrapper.appendChild(testWrapper); |
+ |
+ // Create custom styles for this test case. |
+ if (test.data.style) { |
+ var styleForSamples = document.createElement('style'); |
+ styleForSamples.type = 'text/css'; |
+ styleForSamples.textContent = '.test' + testId + ' {' + test.data.style + '}'; |
+ document.head.appendChild(styleForSamples); |
+ } |
+ if (test.data.markerStyle && test.data.markerStyle != '') { |
+ var styleForMarkers = document.createElement('style'); |
+ styleForMarkers.type = 'text/css'; |
+ styleForMarkers.textContent = '.testMarker' + testId + ' {' + test.data.markerStyle + '}'; |
+ document.head.appendChild(styleForMarkers); |
+ } |
+ |
+ test.data.samples.forEach(function(sample) { |
+ var element = document.createElement("div"); |
+ |
+ if (!testWrapper.hasChildNodes()) |
+ element.style.clear = "left"; |
+ |
+ if (test.data.markerStyle == null || test.data.markerStyle != '') { |
+ var content = document.createElement("div"); |
+ content.classList.add('marker', 'testMarker' + testId); |
+ element.appendChild(content); |
+ } |
+ |
+ element.classList.add('item', 'test' + testId); |
+ |
+ var sample_id = this.next_sample_id++; |
+ element.id = sample_id; |
+ |
+ testWrapper.appendChild(element); |
+ |
+ test.instances.push({ |
+ element: element, |
+ animation: null, |
+ id: sample_id |
+ }); |
+ }.bind(this)); |
+ |
+ testId++; |
+ }.bind(this)); |
+ |
+ // Update all lifecycle phases to propagate all the objects to |
+ // the compositor and to clear all the dirty flags. |
+ if (window.internals) |
+ internals.forceCompositingUpdate(document); |
+ } |
+ |
+ startAnimations() { |
+ // We want to achieve desired accuracy for splines using a specific duration. |
+ // TODO(loyso): Duration mustn't affect cc/blink consistency. |
+ // Taken from cubic_bezier.cc: |
+ var kBezierEpsilon = 1e-7; |
+ // Reverse the blink::accuracyForDuration function to calculate duration |
+ // from epsilon: |
+ var duration = 1000 * 1.0 / (kBezierEpsilon * 200.0); |
+ |
+ this.tests.forEach(function(test) { |
+ for (var i = 0; i < test.instances.length; i++) { |
+ var sample = test.data.samples[i]; |
+ var instance = test.instances[i]; |
+ |
+ // Use negative animation delays to specify sampled time for each animation. |
+ instance.animation = instance.element.animate(test.data.keyframes, { |
+ duration: duration, |
+ iterations: Infinity, |
+ delay: -duration * sample.at, |
+ easing: test.data.easing |
+ }); |
+ |
+ if (window.internals && this.disableThreadedAnimation) { |
+ internals.disableCompositedAnimation(instance.animation); |
+ } |
+ } |
+ }.bind(this)); |
+ |
+ if (window.internals) { |
+ internals.pauseAnimations(0); |
+ } |
+ } |
+ |
+ assertAnimationsRunningOnCompositorThread() { |
+ this.tests.forEach(function(test) { |
+ test.instances.forEach(function(instance) { |
+ var composited = internals.isCompositedAnimation(instance.animation); |
+ if (composited != !this.disableThreadedAnimation) |
+ error.textContent += `Animation ${instance.id} ${composited ? 'is' : 'is not'} running on the compositor.`; |
+ }.bind(this)); |
+ }.bind(this)); |
+ } |
+ |
+ layoutAndPaint() { |
+ if (window.testRunner) { |
+ testRunner.waitUntilDone(); |
+ testRunner.layoutAndPaintAsyncThen(function() { |
+ if (window.internals) { |
+ this.assertAnimationsRunningOnCompositorThread(); |
+ } |
+ testRunner.notifyDone(); |
+ }.bind(this)); |
+ } |
+ } |
+ |
+ registerTestData(testData) { |
+ this.tests.push({ |
+ data: testData, |
+ instances: [] |
+ }); |
+ } |
+ |
+ registerTestsData(testsData) { |
+ testsData.forEach(function(test) { |
+ this.registerTestData(test); |
+ }.bind(this)); |
+ } |
+ |
+ run() { |
+ this.createTestElements(); |
+ this.startAnimations(); |
+ this.layoutAndPaint(); |
+ } |
+} |
+ |
+ |
+class CompositedAnimationTest extends CompositedAnimationTestCommon { |
+ constructor() { |
+ var disableThreadedAnimation = false; |
+ super(disableThreadedAnimation) |
+ } |
+} |
+ |
+ |
+class CompositedAnimationTestExpected extends CompositedAnimationTestCommon { |
+ constructor() { |
+ var disableThreadedAnimation = true; |
+ super(disableThreadedAnimation) |
+ } |
+} |
+ |
+ |
+var getLinearSamples = function(n, start, end) { |
+ var arr = []; |
+ var spread = end - start; |
+ for (var i=0; i<=n; i++) { |
+ arr.push(i * spread / n + start); |
+ } |
+ return arr.map(function(t) { return { at: t }}); |
+} |