2009-08-03 Simon Fraser <simon.fraser@apple.com>
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Aug 2009 19:40:12 +0000 (19:40 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Aug 2009 19:40:12 +0000 (19:40 +0000)
        Reviewed by Dan Bernstein.

        Make the DRT APIs to pause transitions and animations work for accelerated animations
        <https://bugs.webkit.org/show_bug.cgi?id=27627>
        <rdar://problem/6442932>

        Fix the 'pauseAnimationAtTimeOnElementWithId' and 'pauseTransitionAtTimeOnElementWithId' APIs
        available to LayoutTests to work with accelerated animations and transitions. Done by sending
        the pause time down to the GraphicsLayer, and using it to stop time on the layer.

        I also added an assertion to check that the animation has actually started when we
        try to pause it, to check that the pause time we compute is valid. This revealed a number
        of tests that tried to pause before animations started, required some amount of test fixing.

        * page/animation/AnimationBase.cpp:
        (WebCore::AnimationBase::freezeAtTime):
        * page/animation/AnimationBase.h:
        * page/animation/CompositeAnimation.cpp:
        (WebCore::CompositeAnimation::pauseAnimationAtTime):
        (WebCore::CompositeAnimation::pauseTransitionAtTime):
        * platform/graphics/GraphicsLayer.cpp:
        (WebCore::GraphicsLayer::suspendAnimations):
        * platform/graphics/GraphicsLayer.h:
        * platform/graphics/mac/GraphicsLayerCA.h:
        * platform/graphics/mac/GraphicsLayerCA.mm:
        (WebCore::GraphicsLayerCA::suspendAnimations):
        * rendering/RenderLayerBacking.cpp:
        (WebCore::RenderLayerBacking::suspendAnimations):
        * rendering/RenderLayerBacking.h:

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@46727 268f45cc-cd09-0410-ab3c-d52691b4dbfc

20 files changed:
LayoutTests/ChangeLog
LayoutTests/animations/animation-drt-api-multiple-keyframes.html
LayoutTests/animations/animation-drt-api.html
LayoutTests/animations/animation-hit-test-transform.html
LayoutTests/animations/animation-hit-test.html
LayoutTests/animations/animation-test-helpers.js
LayoutTests/transitions/transition-drt-api-delay.html
LayoutTests/transitions/transition-hit-test-transform.html
LayoutTests/transitions/transition-shorthand-delay.html
LayoutTests/transitions/transition-test-helpers.js
WebCore/ChangeLog
WebCore/page/animation/AnimationBase.cpp
WebCore/page/animation/AnimationBase.h
WebCore/page/animation/CompositeAnimation.cpp
WebCore/platform/graphics/GraphicsLayer.cpp
WebCore/platform/graphics/GraphicsLayer.h
WebCore/platform/graphics/mac/GraphicsLayerCA.h
WebCore/platform/graphics/mac/GraphicsLayerCA.mm
WebCore/rendering/RenderLayerBacking.cpp
WebCore/rendering/RenderLayerBacking.h

index fa711c5..fbbd731 100644 (file)
@@ -1,3 +1,44 @@
+2009-08-03  Simon Fraser  <simon.fraser@apple.com>
+
+        Reviewed by Dan Bernstein.
+
+        Make the DRT APIs to pause transitions and animations work for accelerated animations
+        <https://bugs.webkit.org/show_bug.cgi?id=27627>
+        <rdar://problem/6442932>
+
+        Fix the 'pauseAnimationAtTimeOnElementWithId' and 'pauseTransitionAtTimeOnElementWithId' APIs
+        available to LayoutTests to work with accelerated animations and transitions. Done by sending
+        the pause time down to the GraphicsLayer, and using it to stop time on the layer.
+        
+        I also added an assertion to check that the animation has actually started when we
+        try to pause it, to check that the pause time we compute is valid. This revealed a number
+        of tests that tried to pause before animations started, required some amount of test fixing.
+
+        * animations/animation-drt-api-multiple-keyframes.html:
+        * animations/animation-drt-api.html:
+        * animations/animation-hit-test-transform.html:
+        * animations/animation-hit-test.html:
+        * animations/animation-test-helpers.js:
+        (isCloseEnough):
+        (matrixStringToArray):
+        (checkExpectedValue):
+        (endTest):
+        (startTest):
+        (runAnimationTest.else):
+        (runAnimationTest):
+        (waitForAnimationToStart):
+        * transitions/transition-drt-api-delay.html:
+        * transitions/transition-hit-test-transform.html:
+        * transitions/transition-shorthand-delay.html:
+        * transitions/transition-test-helpers.js:
+        (isCloseEnough):
+        (checkExpectedValue):
+        (endTest):
+        (runTest):
+        (waitForAnimationStart):
+        (startTest):
+        (runTransitionTest):
+
 2009-08-03  Chris Fleizach  <cfleizach@apple.com>
 
         Reviewed by Darin Adler.
index cc170b6..023e11f 100644 (file)
       60% { left: 15px; }
       to { left: 20px; }
     }
-         
-     </style>
+   </style>
+
+   <script src="animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript" charset="utf-8">
-    function startTest() {
+    if (window.layoutTestController)
+      layoutTestController.waitUntilDone();
+   
+    function animationStarted()
+    {
       if (window.layoutTestController) {
         if (!layoutTestController.pauseAnimationAtTimeOnElementWithId("anim", 3, "box"))
           throw("Animation is not running");
+
+        layoutTestController.notifyDone();
       }
     }
+
+    function startTest()
+    {
+      waitForAnimationToStart(document.getElementById('box'), animationStarted);
+    }
    </script>
 </head>
 <body onload="startTest()">
index 06e06ec..8872ae2 100644 (file)
       }
     }
          
-     </style>
-   <script type="text/javascript" charset="utf-8">
-    function startTest() {
+  </style>
+
+  <script src="animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
+  <script type="text/javascript" charset="utf-8">
+    if (window.layoutTestController)
+      layoutTestController.waitUntilDone();
+
+    function animationStarted()
+    {
       if (window.layoutTestController) {
         if (!layoutTestController.pauseAnimationAtTimeOnElementWithId("bounce", 1.0, "target"))
           throw("Animation is not running");
+        layoutTestController.notifyDone();
       }
     }
+
+    function startTest()
+    {
+      waitForAnimationToStart(document.getElementById('target'), animationStarted);
+    }
    </script>
 </head>
 <body onload="startTest()">
index d806356..c96ff4d 100644 (file)
@@ -27,7 +27,9 @@
         background-color: yellow;
         position:absolute;
     }
-     </style>
+   </style>
+
+   <script src="animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript" charset="utf-8">
         function checkResult(pos, isIn)
         {
@@ -71,7 +73,7 @@
             }
       
             document.getElementById("target").style.webkitAnimationName = "anim";
-            window.setTimeout(doTest, 0);
+            waitForAnimationToStart(document.getElementById('target'), doTest);
         }
    </script>
 </head>
index 6e0c56a..fae0395 100644 (file)
@@ -27,7 +27,9 @@
         background-color: yellow;
         position:absolute;
     }
-     </style>
+    </style>
+
+   <script src="animation-test-helpers.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript" charset="utf-8">
         function checkResult(pos, isIn)
         {
@@ -71,7 +73,7 @@
             }
       
             document.getElementById("target").style.webkitAnimationName = "anim";
-            window.setTimeout(doTest, 0);
+            waitForAnimationToStart(document.getElementById('target'), doTest);
         }
    </script>
 </head>
index 6fe1812..ab606aa 100644 (file)
@@ -29,169 +29,173 @@ Function parameters:
     If the CSS property name is "webkitTransform.N", expected value must be a number corresponding to the Nth element of the matrix
 
 */
-function runAnimationTest(expected, callback, event, disablePauseAnimationAPI)
+
+function isCloseEnough(actual, desired, tolerance)
 {
-    var result = "";
-    var hasPauseAnimationAPI = ('layoutTestController' in window) && ('pauseAnimationAtTimeOnElementWithId' in layoutTestController);
-    if (disablePauseAnimationAPI)
-        hasPauseAnimationAPI = false;
+    var diff = Math.abs(actual - desired);
+    return diff <= tolerance;
+}
 
-    function isCloseEnough(actual, desired, tolerance)
-    {
-        var diff = Math.abs(actual - desired);
-        return diff <= tolerance;
+function matrixStringToArray(s)
+{
+    var m = s.split("(");
+    m = m[1].split(")");
+    return m[0].split(",");
+}
+
+function checkExpectedValue(expected, index)
+{
+    var animationName = expected[index][0];
+    var time = expected[index][1];
+    var elementId = expected[index][2];
+    var property = expected[index][3];
+    var expectedValue = expected[index][4];
+    var tolerance = expected[index][5];
+
+    // Check for a pair of element Ids
+    var compareElements = false;
+    var elementId2;
+    if (typeof elementId != "string") {
+        if (elementId.length != 2)
+            return;
+            
+        elementId2 = elementId[1];
+        elementId = elementId[0];
+        compareElements = true;
+    }
+
+    if (animationName && hasPauseAnimationAPI && !layoutTestController.pauseAnimationAtTimeOnElementWithId(animationName, time, elementId)) {
+        result += "FAIL - animation \"" + animationName + "\" is not running" + "<br>";
+        return;
     }
     
-    function matrixStringToArray(s)
-    {
-        var m = s.split("(");
-        m = m[1].split(")");
-        return m[0].split(",");
+    if (compareElements && animationName && hasPauseAnimationAPI && !layoutTestController.pauseAnimationAtTimeOnElementWithId(animationName, time, elementId2)) {
+        result += "FAIL - animation \"" + animationName + "\" is not running" + "<br>";
+        return;
     }
     
-    function checkExpectedValue(expected, index)
-    {
-        var animationName = expected[index][0];
-        var time = expected[index][1];
-        var elementId = expected[index][2];
-        var property = expected[index][3];
-        var expectedValue = expected[index][4];
-        var tolerance = expected[index][5];
-
-        // Check for a pair of element Ids
-        var compareElements = false;
-        var elementId2;
-        if (typeof elementId != "string") {
-            if (elementId.length != 2)
-                return;
-                
-            elementId2 = elementId[1];
-            elementId = elementId[0];
-            compareElements = true;
-        }
-
-        if (animationName && hasPauseAnimationAPI && !layoutTestController.pauseAnimationAtTimeOnElementWithId(animationName, time, elementId)) {
-            result += "FAIL - animation \"" + animationName + "\" is not running" + "<br>";
-            return;
-        }
-        
-        if (compareElements && animationName && hasPauseAnimationAPI && !layoutTestController.pauseAnimationAtTimeOnElementWithId(animationName, time, elementId2)) {
-            result += "FAIL - animation \"" + animationName + "\" is not running" + "<br>";
-            return;
-        }
-        
-        var computedValue, computedValue2;
-        var pass = true;
-        if (!property.indexOf("webkitTransform")) {
-            computedValue = window.getComputedStyle(document.getElementById(elementId)).webkitTransform;
-            if (compareElements) {
-                computedValue2 = window.getComputedStyle(document.getElementById(elementId2)).webkitTransform;
-                var m1 = matrixStringToArray(computedValue);
-                var m2 = matrixStringToArray(computedValue2);
-                
-                // for now we assume that both matrices are either both 2D or both 3D
-                var count = (computedValue.substring(0, 7) == "matrix3d") ? 16 : 6;
-                for (var i = 0; i < count; ++i) {
-                    pass = isCloseEnough(parseFloat(m1[i]), m2[i], tolerance);
+    var computedValue, computedValue2;
+    var pass = true;
+    if (!property.indexOf("webkitTransform")) {
+        computedValue = window.getComputedStyle(document.getElementById(elementId)).webkitTransform;
+        if (compareElements) {
+            computedValue2 = window.getComputedStyle(document.getElementById(elementId2)).webkitTransform;
+            var m1 = matrixStringToArray(computedValue);
+            var m2 = matrixStringToArray(computedValue2);
+            
+            // for now we assume that both matrices are either both 2D or both 3D
+            var count = (computedValue.substring(0, 7) == "matrix3d") ? 16 : 6;
+            for (var i = 0; i < count; ++i) {
+                pass = isCloseEnough(parseFloat(m1[i]), m2[i], tolerance);
+                if (!pass)
+                    break;
+            }                
+        } else {
+            if (typeof expectedValue == "string")
+                pass = (computedValue == expectedValue);
+            else if (typeof expectedValue == "number") {
+                var m = matrixStringToArray(computedValue);
+                pass = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance);
+            } else {
+                var m = matrixStringToArray(computedValue);
+                for (i = 0; i < expectedValue.length; ++i) {
+                    pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
                     if (!pass)
                         break;
-                }                
-            } else {
-                if (typeof expectedValue == "string")
-                    pass = (computedValue == expectedValue);
-                else if (typeof expectedValue == "number") {
-                    var m = matrixStringToArray(computedValue);
-                    pass = isCloseEnough(parseFloat(m[parseInt(property.substring(16))]), expectedValue, tolerance);
-                } else {
-                    var m = matrixStringToArray(computedValue);
-                    for (i = 0; i < expectedValue.length; ++i) {
-                        pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
-                        if (!pass)
-                            break;
-                    }
                 }
             }
-        } else if (property == "lineHeight") {
-            computedValue = parseInt(window.getComputedStyle(document.getElementById(elementId)).lineHeight);
-            if (compareElements) {
-                computedValue2 = parseInt(window.getComputedStyle(document.getElementById(elementId2)).lineHeight);
-                pass = isCloseEnough(computedValue, computedValue2, tolerance);
-            }
-            else
-                pass = isCloseEnough(computedValue, expectedValue, tolerance);
-        } else {
-            var computedStyle = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property);
-            computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
-            if (compareElements) {
-                var computedStyle2 = window.getComputedStyle(document.getElementById(elementId2)).getPropertyCSSValue(property);
-                computedValue2 = computedStyle2.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
-                pass = isCloseEnough(computedValue, computedValue2, tolerance);
-            }
-            else
-                pass = isCloseEnough(computedValue, expectedValue, tolerance);
         }
-
+    } else if (property == "lineHeight") {
+        computedValue = parseInt(window.getComputedStyle(document.getElementById(elementId)).lineHeight);
         if (compareElements) {
-            if (pass)
-                result += "PASS - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + "\" elements at " + time + "s are close enough to each other" + "<br>";
-            else
-                result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + "\" elements at " + time + "s saw: \"" + computedValue + "\" and \"" + computedValue2 + "\" which are not close enough to each other" + "<br>";
-        } else {
-            if (pass)
-                result += "PASS - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s saw something close to: " + expectedValue + "<br>";
-            else
-                result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
+            computedValue2 = parseInt(window.getComputedStyle(document.getElementById(elementId2)).lineHeight);
+            pass = isCloseEnough(computedValue, computedValue2, tolerance);
+        }
+        else
+            pass = isCloseEnough(computedValue, expectedValue, tolerance);
+    } else {
+        var computedStyle = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property);
+        computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+        if (compareElements) {
+            var computedStyle2 = window.getComputedStyle(document.getElementById(elementId2)).getPropertyCSSValue(property);
+            computedValue2 = computedStyle2.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+            pass = isCloseEnough(computedValue, computedValue2, tolerance);
         }
+        else
+            pass = isCloseEnough(computedValue, expectedValue, tolerance);
     }
 
-    function endTest()
-    {
-        document.getElementById('result').innerHTML = result;
-
-        if (window.layoutTestController)
-            layoutTestController.notifyDone();
+    if (compareElements) {
+        if (pass)
+            result += "PASS - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + "\" elements at " + time + "s are close enough to each other" + "<br>";
+        else
+            result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" and \"" + elementId2 + "\" elements at " + time + "s saw: \"" + computedValue + "\" and \"" + computedValue2 + "\" which are not close enough to each other" + "<br>";
+    } else {
+        if (pass)
+            result += "PASS - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s saw something close to: " + expectedValue + "<br>";
+        else
+            result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
     }
+}
 
-    function checkExpectedValueCallback(expected, index)
-    {
-        return function() { checkExpectedValue(expected, index); };
-    }
+function endTest()
+{
+    document.getElementById('result').innerHTML = result;
 
-    var testStarted = false;
-    function startTest(expected, callback)
-    {
-        if (testStarted) return;
-        testStarted = true;
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+}
 
-        if (callback)
-            callback();
+function checkExpectedValueCallback(expected, index)
+{
+    return function() { checkExpectedValue(expected, index); };
+}
 
-        var maxTime = 0;
+var testStarted = false;
+function startTest(expected, callback)
+{
+    if (testStarted) return;
+    testStarted = true;
 
-        for (var i = 0; i < expected.length; ++i) {
-            var animationName = expected[i][0];
-            var time = expected[i][1];
+    if (callback)
+        callback();
 
-            // We can only use the animation fast-forward mechanism if there's an animation name
-            // and DRT implements pauseAnimationAtTimeOnElementWithId()
-            if (animationName && hasPauseAnimationAPI)
-                checkExpectedValue(expected, i);
-            else {
-                if (time > maxTime)
-                    maxTime = time;
+    var maxTime = 0;
 
-                window.setTimeout(checkExpectedValueCallback(expected, i), time * 1000);
-            }
-        }
+    for (var i = 0; i < expected.length; ++i) {
+        var animationName = expected[i][0];
+        var time = expected[i][1];
 
-        if (maxTime > 0)
-            window.setTimeout(endTest, maxTime * 1000 + 50);
-        else
-            endTest();
+        // We can only use the animation fast-forward mechanism if there's an animation name
+        // and DRT implements pauseAnimationAtTimeOnElementWithId()
+        if (animationName && hasPauseAnimationAPI)
+            checkExpectedValue(expected, i);
+        else {
+            if (time > maxTime)
+                maxTime = time;
+
+            window.setTimeout(checkExpectedValueCallback(expected, i), time * 1000);
+        }
     }
-    
+
+    if (maxTime > 0)
+        window.setTimeout(endTest, maxTime * 1000 + 50);
+    else
+        endTest();
+}
+
+var result = "";
+var hasPauseAnimationAPI;
+
+function runAnimationTest(expected, callback, event, disablePauseAnimationAPI, doPixelTest)
+{
+    hasPauseAnimationAPI = ('layoutTestController' in window) && ('pauseAnimationAtTimeOnElementWithId' in layoutTestController);
+    if (disablePauseAnimationAPI)
+        hasPauseAnimationAPI = false;
+
     if (window.layoutTestController) {
-        layoutTestController.dumpAsText();
+        if (!doPixelTest)
+            layoutTestController.dumpAsText();
         layoutTestController.waitUntilDone();
     }
     
@@ -200,8 +204,16 @@ function runAnimationTest(expected, callback, event, disablePauseAnimationAPI)
     
     var target = document;
     if (event == undefined)
-        event = "webkitAnimationStart";
+        waitForAnimationToStart(target, function() { startTest(expected, callback); });
     else if (event == "load")
-        target = window;
-    target.addEventListener(event, function() { startTest(expected, callback); }, false);
+        window.addEventListener(event, function() {
+            startTest(expected, callback);
+        }, false);
+}
+
+function waitForAnimationToStart(element, callback)
+{
+    element.addEventListener('webkitAnimationStart', function() {
+        window.setTimeout(callback, 0); // delay to give hardware animations a chance to start
+    }, false);
 }
index 4bbc8db..589f599 100644 (file)
@@ -20,7 +20,8 @@
     .moved {
       left: 200px;
     }
-     </style>
+   </style>
+   <script src="transition-test-helpers.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript" charset="utf-8">
     function endTest() {
       if (window.layoutTestController) {
@@ -42,7 +43,7 @@
       }
       
       document.getElementById("target").className = "moved";
-      window.setTimeout(endTest, 0);
+      waitForAnimationStart(endTest, 1);
     }
    </script>
 </head>
index 12b7998..819181e 100644 (file)
@@ -26,6 +26,7 @@
         position:absolute;
     }
      </style>
+   <script src="transition-test-helpers.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript" charset="utf-8">
         function checkResult(pos, isIn)
         {
@@ -69,7 +70,7 @@
             }
       
             document.getElementById("target").style.webkitTransform = "translateX(300px)";
-            window.setTimeout(doTest, 0);
+            waitForAnimationStart(doTest);
         }
    </script>
 </head>
index b11a08c..58d5cb8 100644 (file)
@@ -21,7 +21,8 @@
     .moved {
       left: 200px;
     }
-     </style>
+   </style>
+   <script src="transition-test-helpers.js" type="text/javascript" charset="utf-8"></script>
    <script type="text/javascript" charset="utf-8">
     function endTest() {
       if (window.layoutTestController) {
@@ -43,7 +44,7 @@
       }
       
       document.getElementById("target").className = "moved";
-      window.setTimeout(endTest, 0);
+      waitForAnimationStart(endTest, 1);
     }
    </script>
 </head>
index 5caa1b5..aa679ba 100644 (file)
@@ -26,150 +26,165 @@ function roundNumber(num, decimalPlaces)
   return Math.round(num * Math.pow(10, decimalPlaces)) / Math.pow(10, decimalPlaces);
 }
 
-function runTransitionTest(expected, callback, usePauseAPI)
+function isCloseEnough(actual, desired, tolerance)
 {
-    var result = "";
-    var hasPauseTransitionAPI = ('layoutTestController' in window) && ('pauseTransitionAtTimeOnElementWithId' in layoutTestController);
-    
-    function isCloseEnough(actual, desired, tolerance)
-    {
-        var diff = Math.abs(actual - desired);
-        return diff <= tolerance;
-    }
+    var diff = Math.abs(actual - desired);
+    return diff <= tolerance;
+}
 
-    function checkExpectedValue(expected, index)
-    {
-        var time = expected[index][0];
-        var elementId = expected[index][1];
-        var property = expected[index][2];
-        var expectedValue = expected[index][3];
-        var tolerance = expected[index][4];
-
-        var computedValue;
-        var pass = false;
-        var transformRegExp = /^-webkit-transform(\.\d+)?$/;
-        if (transformRegExp.test(property)) {
-            computedValue = window.getComputedStyle(document.getElementById(elementId)).webkitTransform;
-            if (typeof expectedValue == "string")
-                pass = (computedValue == expectedValue);
-            else if (typeof expectedValue == "number") {
-                var m = computedValue.split("(");
-                var m = m[1].split(",");
-                pass = isCloseEnough(parseFloat(m[parseInt(property.substring(18))]), expectedValue, tolerance);
-            } else {
-                var m = computedValue.split("(");
-                var m = m[1].split(",");
-                for (i = 0; i < expectedValue.length; ++i) {
-                    pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
-                    if (!pass)
-                        break;
-                }
-            }
-        } else if (property == "lineHeight") {
-            computedValue = parseInt(window.getComputedStyle(document.getElementById(elementId)).lineHeight);
-            pass = isCloseEnough(computedValue, expectedValue, tolerance);
+function checkExpectedValue(expected, index)
+{
+    var time = expected[index][0];
+    var elementId = expected[index][1];
+    var property = expected[index][2];
+    var expectedValue = expected[index][3];
+    var tolerance = expected[index][4];
+
+    var computedValue;
+    var pass = false;
+    var transformRegExp = /^-webkit-transform(\.\d+)?$/;
+    if (transformRegExp.test(property)) {
+        computedValue = window.getComputedStyle(document.getElementById(elementId)).webkitTransform;
+        if (typeof expectedValue == "string")
+            pass = (computedValue == expectedValue);
+        else if (typeof expectedValue == "number") {
+            var m = computedValue.split("(");
+            var m = m[1].split(",");
+            pass = isCloseEnough(parseFloat(m[parseInt(property.substring(18))]), expectedValue, tolerance);
         } else {
-            var computedStyle = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property);
-            if (computedStyle.cssValueType == CSSValue.CSS_VALUE_LIST) {
-                // For now, assume that value lists are simple lists of number values (e.g. transform-origin)
-                var values = [];
-                for (var i = 0; i < computedStyle.length; ++i) {
-                    values.push(computedStyle[i].getFloatValue(CSSPrimitiveValue.CSS_NUMBER));
-                }
-                computedValue = values.join(',');
-                pass = true;
-                for (var i = 0; i < values.length; ++i)
-                    pass &= isCloseEnough(values[i], expectedValue[i], tolerance);
-            } else if (computedStyle.cssValueType == CSSValue.CSS_PRIMITIVE_VALUE) {
-                switch (computedStyle.primitiveType) {
-                    case CSSPrimitiveValue.CSS_STRING:
-                        computedValue = computedStyle.getStringValue();
-                        pass = computedValue == expectedValue;
-                        break;
-                    case CSSPrimitiveValue.CSS_RGBCOLOR:
-                        var rgbColor = computedStyle.getRGBColorValue();
-                        computedValue = [rgbColor.red.getFloatValue(CSSPrimitiveValue.CSS_NUMBER),
-                                         rgbColor.green.getFloatValue(CSSPrimitiveValue.CSS_NUMBER),
-                                         rgbColor.blue.getFloatValue(CSSPrimitiveValue.CSS_NUMBER)]; // alpha is not exposed to JS
-                        pass = true;
-                        for (var i = 0; i < 3; ++i)
-                            pass &= isCloseEnough(computedValue[i], expectedValue[i], tolerance);
-                        break;
-                    case CSSPrimitiveValue.CSS_RECT:
-                        computedValue = computedStyle.getRectValue();
-                        pass = computedValue == expectedValue;
-                        break;
-                    default:
-                        computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
-                        pass = isCloseEnough(computedValue, expectedValue, tolerance);
-                }
+            var m = computedValue.split("(");
+            var m = m[1].split(",");
+            for (i = 0; i < expectedValue.length; ++i) {
+                pass = isCloseEnough(parseFloat(m[i]), expectedValue[i], tolerance);
+                if (!pass)
+                    break;
+            }
+        }
+    } else if (property == "lineHeight") {
+        computedValue = parseInt(window.getComputedStyle(document.getElementById(elementId)).lineHeight);
+        pass = isCloseEnough(computedValue, expectedValue, tolerance);
+    } else {
+        var computedStyle = window.getComputedStyle(document.getElementById(elementId)).getPropertyCSSValue(property);
+        if (computedStyle.cssValueType == CSSValue.CSS_VALUE_LIST) {
+            // For now, assume that value lists are simple lists of number values (e.g. transform-origin)
+            var values = [];
+            for (var i = 0; i < computedStyle.length; ++i) {
+                values.push(computedStyle[i].getFloatValue(CSSPrimitiveValue.CSS_NUMBER));
+            }
+            computedValue = values.join(',');
+            pass = true;
+            for (var i = 0; i < values.length; ++i)
+                pass &= isCloseEnough(values[i], expectedValue[i], tolerance);
+        } else if (computedStyle.cssValueType == CSSValue.CSS_PRIMITIVE_VALUE) {
+            switch (computedStyle.primitiveType) {
+                case CSSPrimitiveValue.CSS_STRING:
+                    computedValue = computedStyle.getStringValue();
+                    pass = computedValue == expectedValue;
+                    break;
+                case CSSPrimitiveValue.CSS_RGBCOLOR:
+                    var rgbColor = computedStyle.getRGBColorValue();
+                    computedValue = [rgbColor.red.getFloatValue(CSSPrimitiveValue.CSS_NUMBER),
+                                     rgbColor.green.getFloatValue(CSSPrimitiveValue.CSS_NUMBER),
+                                     rgbColor.blue.getFloatValue(CSSPrimitiveValue.CSS_NUMBER)]; // alpha is not exposed to JS
+                    pass = true;
+                    for (var i = 0; i < 3; ++i)
+                        pass &= isCloseEnough(computedValue[i], expectedValue[i], tolerance);
+                    break;
+                case CSSPrimitiveValue.CSS_RECT:
+                    computedValue = computedStyle.getRectValue();
+                    pass = computedValue == expectedValue;
+                    break;
+                default:
+                    computedValue = computedStyle.getFloatValue(CSSPrimitiveValue.CSS_NUMBER);
+                    pass = isCloseEnough(computedValue, expectedValue, tolerance);
             }
         }
-
-        if (pass)
-            result += "PASS - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s saw something close to: " + expectedValue + "<br>";
-        else
-            result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
     }
 
-    function endTest()
-    {
-        document.getElementById('result').innerHTML = result;
+    if (pass)
+        result += "PASS - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s saw something close to: " + expectedValue + "<br>";
+    else
+        result += "FAIL - \"" + property + "\" property for \"" + elementId + "\" element at " + time + "s expected: " + expectedValue + " but saw: " + computedValue + "<br>";
+}
 
-        if (window.layoutTestController)
-            layoutTestController.notifyDone();
-    }
+function endTest()
+{
+    document.getElementById('result').innerHTML = result;
 
-    function checkExpectedValueCallback(expected, index)
-    {
-        return function() { checkExpectedValue(expected, index); };
-    }
+    if (window.layoutTestController)
+        layoutTestController.notifyDone();
+}
 
-    function runTest(expected)
-    {
-        var maxTime = 0;
+function checkExpectedValueCallback(expected, index)
+{
+    return function() { checkExpectedValue(expected, index); };
+}
 
-        for (var i = 0; i < expected.length; ++i) {
-            var time = expected[i][0];
-            var elementId = expected[i][1];
-            var property = expected[i][2];
-            if (!property.indexOf("-webkit-transform"))
+function runTest(expected, usePauseAPI)
+{
+    var maxTime = 0;
+    for (var i = 0; i < expected.length; ++i) {
+        var time = expected[i][0];
+        var elementId = expected[i][1];
+        var property = expected[i][2];
+        if (!property.indexOf("-webkit-transform"))
             property = "-webkit-transform";
 
-            // We can only use the transition fast-forward mechanism if DRT implements pauseTransitionAtTimeOnElementWithId()
-            if (hasPauseTransitionAPI && usePauseAPI) {
-                layoutTestController.pauseTransitionAtTimeOnElementWithId(property, time, elementId);
-                checkExpectedValue(expected, i);
-            }
-            else {
-                if (time > maxTime)
-                    maxTime = time;
+        // We can only use the transition fast-forward mechanism if DRT implements pauseTransitionAtTimeOnElementWithId()
+        if (hasPauseTransitionAPI && usePauseAPI) {
+            layoutTestController.pauseTransitionAtTimeOnElementWithId(property, time, elementId);
+            checkExpectedValue(expected, i);
+        } else {
+            if (time > maxTime)
+                maxTime = time;
 
-                window.setTimeout(checkExpectedValueCallback(expected, i), time * 1000);
-            }
+            window.setTimeout(checkExpectedValueCallback(expected, i), time * 1000);
         }
-
-        if (maxTime > 0)
-            window.setTimeout(endTest, maxTime * 1000 + 50);
-        else
-            endTest();
     }
-    
-    function startTest(expected, callback)
-    {
-        if (callback)
+
+    if (maxTime > 0)
+        window.setTimeout(endTest, maxTime * 1000 + 50);
+    else
+        endTest();
+}
+
+function waitForAnimationStart(callback, delay)
+{
+    var delayTimeout = delay ? 1000 * delay + 10 : 0;
+    // Why the two setTimeouts? Well, for hardware animations we need to ensure that the hardware animation
+    // has started before we try to pause it, and timers fire before animations get committed in the runloop.
+    window.setTimeout(function() {
+        window.setTimeout(function() {
             callback();
+        }, 0);
+    }, delayTimeout);
+}
 
-        window.setTimeout(function() { runTest(expected); }, 0);
-    }
+function startTest(expected, usePauseAPI, callback)
+{
+    if (callback)
+        callback();
+
+    waitForAnimationStart(function() {
+        runTest(expected, usePauseAPI);
+    });
+}
+
+var result = "";
+var hasPauseTransitionAPI;
+
+function runTransitionTest(expected, callback, usePauseAPI, doPixelTest)
+{
+    hasPauseTransitionAPI = ('layoutTestController' in window) && ('pauseTransitionAtTimeOnElementWithId' in layoutTestController);
     
     if (window.layoutTestController) {
-        layoutTestController.dumpAsText();
+        if (!doPixelTest)
+            layoutTestController.dumpAsText();
         layoutTestController.waitUntilDone();
     }
     
     if (!expected)
         throw("Expected results are missing!");
     
-    window.addEventListener("load", function() { startTest(expected, callback); }, false);
+    window.addEventListener("load", function() { startTest(expected, usePauseAPI, callback); }, false);
 }
index 2da8e28..668491d 100644 (file)
@@ -1,3 +1,35 @@
+2009-08-03  Simon Fraser  <simon.fraser@apple.com>
+
+        Reviewed by Dan Bernstein.
+
+        Make the DRT APIs to pause transitions and animations work for accelerated animations
+        <https://bugs.webkit.org/show_bug.cgi?id=27627>
+        <rdar://problem/6442932>
+
+        Fix the 'pauseAnimationAtTimeOnElementWithId' and 'pauseTransitionAtTimeOnElementWithId' APIs
+        available to LayoutTests to work with accelerated animations and transitions. Done by sending
+        the pause time down to the GraphicsLayer, and using it to stop time on the layer.
+        
+        I also added an assertion to check that the animation has actually started when we
+        try to pause it, to check that the pause time we compute is valid. This revealed a number
+        of tests that tried to pause before animations started, required some amount of test fixing.
+
+        * page/animation/AnimationBase.cpp:
+        (WebCore::AnimationBase::freezeAtTime):
+        * page/animation/AnimationBase.h:
+        * page/animation/CompositeAnimation.cpp:
+        (WebCore::CompositeAnimation::pauseAnimationAtTime):
+        (WebCore::CompositeAnimation::pauseTransitionAtTime):
+        * platform/graphics/GraphicsLayer.cpp:
+        (WebCore::GraphicsLayer::suspendAnimations):
+        * platform/graphics/GraphicsLayer.h:
+        * platform/graphics/mac/GraphicsLayerCA.h:
+        * platform/graphics/mac/GraphicsLayerCA.mm:
+        (WebCore::GraphicsLayerCA::suspendAnimations):
+        * rendering/RenderLayerBacking.cpp:
+        (WebCore::RenderLayerBacking::suspendAnimations):
+        * rendering/RenderLayerBacking.h:
+
 2009-08-03  Tony Chang  <tony@chromium.org>
 
         Reviewed by Darin Adler.
index d01704d..0e2a586 100644 (file)
@@ -45,6 +45,8 @@
 #include "MatrixTransformOperation.h"
 #include "Matrix3DTransformOperation.h"
 #include "RenderBox.h"
+#include "RenderLayer.h"
+#include "RenderLayerBacking.h"
 #include "RenderStyle.h"
 #include "UnitBezier.h"
 
@@ -1077,10 +1079,18 @@ void AnimationBase::goIntoEndingOrLoopingState()
     m_animState = isLooping ? AnimationStateLooping : AnimationStateEnding;
 }
   
-void AnimationBase::pauseAtTime(double t)
+void AnimationBase::freezeAtTime(double t)
 {
-    updatePlayState(false);
+    ASSERT(m_startTime);        // if m_startTime is zero, we haven't started yet, so we'll get a bad pause time.
     m_pauseTime = m_startTime + t - m_animation->delay();
+
+#if USE(ACCELERATED_COMPOSITING)
+    if (m_object && m_object->hasLayer()) {
+        RenderLayer* layer = toRenderBoxModelObject(m_object)->layer();
+        if (layer->isComposited())
+            layer->backing()->suspendAnimations(m_pauseTime);
+    }
+#endif
 }
 
 double AnimationBase::beginAnimationUpdateTime() const
index 8f55a8e..3482f65 100644 (file)
@@ -156,7 +156,8 @@ public:
 
     bool isTransformFunctionListValid() const { return m_transformFunctionListValid; }
     
-    void pauseAtTime(double t);
+    // Freeze the animation; used by DumpRenderTree.
+    void freezeAtTime(double t);
     
     double beginAnimationUpdateTime() const;
     
index 9481b8d..8946d80 100644 (file)
@@ -480,7 +480,7 @@ bool CompositeAnimation::pauseAnimationAtTime(const AtomicString& name, double t
 
     int count = keyframeAnim->m_animation->iterationCount();
     if ((t >= 0.0) && (!count || (t <= count * keyframeAnim->duration()))) {
-        keyframeAnim->pauseAtTime(t);
+        keyframeAnim->freezeAtTime(t);
         return true;
     }
 
@@ -497,7 +497,7 @@ bool CompositeAnimation::pauseTransitionAtTime(int property, double t)
         return false;
 
     if ((t >= 0.0) && (t <= implAnim->duration())) {
-        implAnim->pauseAtTime(t);
+        implAnim->freezeAtTime(t);
         return true;
     }
 
index 9a9ef67..7d43832 100644 (file)
@@ -210,7 +210,7 @@ void GraphicsLayer::paintGraphicsLayerContents(GraphicsContext& context, const I
         m_client->paintContents(this, context, m_paintingPhase, clip);
 }
 
-void GraphicsLayer::suspendAnimations()
+void GraphicsLayer::suspendAnimations(double)
 {
 }
 
index d761cdb..4d7668a 100644 (file)
@@ -247,7 +247,7 @@ public:
     virtual void removeAnimationsForKeyframes(const String& /* keyframesName */) { }
     virtual void pauseAnimation(const String& /* keyframesName */) { }
     
-    virtual void suspendAnimations();
+    virtual void suspendAnimations(double time);
     virtual void resumeAnimations();
     
     // Layer contents
index 2c12164..ebdc6ac 100644 (file)
@@ -88,7 +88,7 @@ public:
 
     virtual void setContentsRect(const IntRect&);
     
-    virtual void suspendAnimations();
+    virtual void suspendAnimations(double time);
     virtual void resumeAnimations();
 
     virtual bool addAnimation(const KeyframeValueList&, const IntSize& boxSize, const Animation*, const String& keyframesName, double beginTime);
index 685cfcf..e5b9035 100644 (file)
@@ -1546,9 +1546,9 @@ bool GraphicsLayerCA::setTransformAnimationKeyframes(const KeyframeValueList& va
     return true;
 }
 
-void GraphicsLayerCA::suspendAnimations()
+void GraphicsLayerCA::suspendAnimations(double time)
 {
-    double t = currentTimeToMediaTime(currentTime());
+    double t = currentTimeToMediaTime(time ? time : currentTime());
     [primaryLayer() setSpeed:0];
     [primaryLayer() setTimeOffset:t];
 }
index 4349155..246472a 100644 (file)
@@ -1020,9 +1020,9 @@ void RenderLayerBacking::transitionFinished(int property)
         m_graphicsLayer->removeAnimationsForProperty(animatedProperty);
 }
 
-void RenderLayerBacking::suspendAnimations()
+void RenderLayerBacking::suspendAnimations(double time)
 {
-    m_graphicsLayer->suspendAnimations();
+    m_graphicsLayer->suspendAnimations(time);
 }
 
 void RenderLayerBacking::resumeAnimations()
index cc4d75d..b027685 100644 (file)
@@ -101,7 +101,7 @@ public:
     void animationPaused(const String& name);
     void transitionFinished(int property);
 
-    void suspendAnimations();
+    void suspendAnimations(double time = 0);
     void resumeAnimations();
 
     IntRect compositedBounds() const;