Composited frames incorrectly get requestAnimationFrame throttled
authorsimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Dec 2017 23:30:48 +0000 (23:30 +0000)
committersimon.fraser@apple.com <simon.fraser@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 5 Dec 2017 23:30:48 +0000 (23:30 +0000)
https://bugs.webkit.org/show_bug.cgi?id=178396

Reviewed by Zalan Bujtas.

Source/WebCore:

If a composited iframe was inside overflow:scroll, and the overflow was scrolled to the left
further than the window width, then RenderLayer::childrenClipRect() would return an empty rectangle
and we'd incorrectly consider the iframe out of view, and thus throttle its requestAnimationFrame.

childrenClipRect() was taking unscaledDocumentRect(), applying the clip, and then mapping the result
from local to absolute coordinates (which mapped it through the overflow scroll offset). This is incorrect
because it considers the unscaledDocumentRect() to be in local coordinates, which it is not.

Instead, start with the infinite rect, which essentially tells us if we have any clipping, and then
intersect that with the document rect at the end. Something similar is done in other places that call.
calculateRects().

Test: fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html

* rendering/RenderLayer.cpp:
(WebCore::RenderLayer::calculateClipRects const):

LayoutTests:

* fast/animation/request-animation-frame-throttle-inside-overflow-scroll-expected.txt: Added.
* fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/fast/animation/request-animation-frame-throttle-inside-overflow-scroll-expected.txt [new file with mode: 0644]
LayoutTests/fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/rendering/RenderLayer.cpp

index 3d4920d..0c018fe 100644 (file)
@@ -1,3 +1,13 @@
+2017-12-05  Simon Fraser  <simon.fraser@apple.com>
+
+        Composited frames incorrectly get requestAnimationFrame throttled
+        https://bugs.webkit.org/show_bug.cgi?id=178396
+
+        Reviewed by Zalan Bujtas.
+
+        * fast/animation/request-animation-frame-throttle-inside-overflow-scroll-expected.txt: Added.
+        * fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html: Added.
+
 2017-12-05  Joseph Pecoraro  <pecoraro@apple.com>
 
         Web Inspector: content views for resources loaded through XHR do not reflect declared mime-type
diff --git a/LayoutTests/fast/animation/request-animation-frame-throttle-inside-overflow-scroll-expected.txt b/LayoutTests/fast/animation/request-animation-frame-throttle-inside-overflow-scroll-expected.txt
new file mode 100644 (file)
index 0000000..6cdbcb8
--- /dev/null
@@ -0,0 +1,14 @@
+Tests that requestAnimationFrame is throttled in subframes are inside scrolled overflow scroll
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Frame is initially inside the viewport so requestAnimationFrame should not be throttled
+PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
+Scrolling overflow.
+requestAnimationFrame should still not be throttled
+PASS testFrame.contentWindow.internals.isRequestAnimationFrameThrottled() is false
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html b/LayoutTests/fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html
new file mode 100644 (file)
index 0000000..fbe5f06
--- /dev/null
@@ -0,0 +1,53 @@
+<!DOCTYPE html>
+<html>
+<body>
+<script src="../../resources/js-test-pre.js"></script>
+<script>
+description("Tests that requestAnimationFrame is throttled in subframes are inside scrolled overflow scroll");
+window.jsTestIsAsync = true;
+
+function scrollOverflow()
+{
+
+    debug("Scrolling overflow.");
+    var container = document.getElementById('container');
+    container.scrollLeft = window.innerWidth + 10;
+
+    debug("requestAnimationFrame should still not be throttled");
+    shouldBeFalse("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
+    
+    finishJSTest();
+}
+
+function runTest()
+{
+    testFrame = document.getElementById("testFrame");
+    debug("Frame is initially inside the viewport so requestAnimationFrame should not be throttled");
+    shouldBeFalse("testFrame.contentWindow.internals.isRequestAnimationFrameThrottled()");
+    scrollOverflow();
+}
+</script>
+<style>
+#container {
+    position: absolute;
+    left: 0;
+    width: 300px;
+    height: 200px;
+    overflow-x: auto;
+    background: silver;
+}
+
+#testFrame {
+    width: 2000px;
+    height: 100px;
+    border: 2px solid black;
+    will-change: transform;
+}
+
+</style>
+<div id="container">
+    <iframe id="testFrame" src="resources/requestAnimationFrame-frame.html" onload="runTest()"></iframe>
+</div>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
index 84b9a5f..bd6c806 100644 (file)
@@ -1,3 +1,27 @@
+2017-12-05  Simon Fraser  <simon.fraser@apple.com>
+
+        Composited frames incorrectly get requestAnimationFrame throttled
+        https://bugs.webkit.org/show_bug.cgi?id=178396
+
+        Reviewed by Zalan Bujtas.
+
+        If a composited iframe was inside overflow:scroll, and the overflow was scrolled to the left
+        further than the window width, then RenderLayer::childrenClipRect() would return an empty rectangle
+        and we'd incorrectly consider the iframe out of view, and thus throttle its requestAnimationFrame.
+
+        childrenClipRect() was taking unscaledDocumentRect(), applying the clip, and then mapping the result
+        from local to absolute coordinates (which mapped it through the overflow scroll offset). This is incorrect
+        because it considers the unscaledDocumentRect() to be in local coordinates, which it is not.
+
+        Instead, start with the infinite rect, which essentially tells us if we have any clipping, and then
+        intersect that with the document rect at the end. Something similar is done in other places that call.
+        calculateRects().
+
+        Test: fast/animation/request-animation-frame-throttle-inside-overflow-scroll.html
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::calculateClipRects const):
+
 2017-12-05  Michael Saboff  <msaboff@apple.com>
 
         Make WebKit build for ARM64_32
index 24077f2..e5e42cc 100644 (file)
@@ -5633,8 +5633,12 @@ LayoutRect RenderLayer::childrenClipRect() const
     ClipRect foregroundRect;
     ClipRectsContext clipRectsContext(clippingRootLayer, TemporaryClipRects);
     // Need to use temporary clip rects, because the value of 'dontClipToOverflow' may be different from the painting path (<rdar://problem/11844909>).
-    calculateRects(clipRectsContext, renderer().view().unscaledDocumentRect(), layerBounds, backgroundRect, foregroundRect, offsetFromAncestor(clipRectsContext.rootLayer));
-    return clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox();
+    calculateRects(clipRectsContext, LayoutRect::infiniteRect(), layerBounds, backgroundRect, foregroundRect, offsetFromAncestor(clipRectsContext.rootLayer));
+    if (foregroundRect.rect().isInfinite())
+        return renderer().view().unscaledDocumentRect();
+
+    auto absoluteClippingRect = clippingRootLayer->renderer().localToAbsoluteQuad(FloatQuad(foregroundRect.rect())).enclosingBoundingBox();
+    return intersection(absoluteClippingRect, renderer().view().unscaledDocumentRect());
 }
 
 LayoutRect RenderLayer::clipRectRelativeToAncestor(RenderLayer* ancestor, LayoutSize offsetFromAncestor, const LayoutRect& constrainingRect) const