Title: [261632] trunk
Revision
261632
Author
[email protected]
Date
2020-05-13 12:00:53 -0700 (Wed, 13 May 2020)

Log Message

composited scrolling interferes with the propagation of perspective
https://bugs.webkit.org/show_bug.cgi?id=156435
<rdar://problem/25642222>

Reviewed by Antti Koivisto.
Source/WebCore:

When we made RenderLayerBacking-internal layers for composited scrolling (m_scrollContainerLayer,
m_scrolledContentsLayer) we'd lose the effects of the sublayer transform, which was applied
to the primary layer, causing perspective on the scroller to not propagate to transformed descendants.

Fix by moving the sublayer transform to the scroll container layer (adjusting the perspective
matrix as appropriate), and making the scrolled contents layer a "preserve3D" layer so
that it doesn't flatten.

This fixes both macOS and iOS.

Test: compositing/transforms/perspective-with-scrolling.html

* rendering/RenderLayerBacking.cpp:
(WebCore::RenderLayerBacking::updateChildrenTransformAndAnchorPoint):

LayoutTests:

* compositing/transforms/perspective-with-scrolling-expected.html: Added.
* compositing/transforms/perspective-with-scrolling.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (261631 => 261632)


--- trunk/LayoutTests/ChangeLog	2020-05-13 18:38:40 UTC (rev 261631)
+++ trunk/LayoutTests/ChangeLog	2020-05-13 19:00:53 UTC (rev 261632)
@@ -1,3 +1,14 @@
+2020-05-13  Simon Fraser  <[email protected]>
+
+        composited scrolling interferes with the propagation of perspective
+        https://bugs.webkit.org/show_bug.cgi?id=156435
+        <rdar://problem/25642222>
+
+        Reviewed by Antti Koivisto.
+
+        * compositing/transforms/perspective-with-scrolling-expected.html: Added.
+        * compositing/transforms/perspective-with-scrolling.html: Added.
+
 2020-05-13  Ryan Haddad  <[email protected]>
 
         [Win EWS] webanimations/accelerated-animation-slot-invalidation.html is frequently failing

Added: trunk/LayoutTests/compositing/transforms/perspective-with-scrolling-expected.html (0 => 261632)


--- trunk/LayoutTests/compositing/transforms/perspective-with-scrolling-expected.html	                        (rev 0)
+++ trunk/LayoutTests/compositing/transforms/perspective-with-scrolling-expected.html	2020-05-13 19:00:53 UTC (rev 261632)
@@ -0,0 +1,82 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:useMockScrollbars=false internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        .container {
+            width: 200px;
+            height: 200px;
+            margin: 20px;
+            display: inline-block;
+            background-color: silver;
+            position: relative;
+            overflow: scroll;
+            perspective: 500px;
+        }
+
+        /* Transformed to be edge-on, and therefore invisible */
+        .angled {
+            position: absolute;
+            top: 0;
+            left: 0;
+            width: 100%;
+            height: 100%;
+            opacity: 0;
+            transform: rotateX(90deg);
+        }
+
+        .angled.vertical {
+            transform: rotateY(90deg);
+        }
+
+        .contents {
+            height: 300%;
+        }
+        .wide.contents {
+            width: 300%;
+        }
+
+        #scroll-down .angled {
+            top: 100px;
+        }
+
+        #scroll-right .angled {
+            left: 100px;
+        }
+    </style>
+    <script>
+        // This is a hack to avoid having scrollbars take space, needed until webkit.org/b/211787 is fixed.
+        if (window.internals)
+            internals.setUsesOverlayScrollbars(true);
+
+        window.addEventListener('load', () => {
+            document.getElementById('scroll-down').scrollTop = 100;
+            document.getElementById('scroll-right').scrollLeft = 100;
+        }, false);
+    </script>
+</head>
+<body>
+    <div class="container" style="transform-box: border-box; border-left: 50px solid black; border-top: 50px solid black; perspective-origin: 150px 150px;">
+        <div class="angled"></div>
+        <div class="angled vertical"></div>
+        <div class="contents"></div>
+    </div>
+
+    <div id="scroll-down" class="container" style="transform-box: border-box; border-left: 50px solid black; border-top: 50px solid black; perspective-origin: 150px 150px;">
+        <div class="angled"></div>
+        <div class="angled vertical"></div>
+        <div class="contents"></div>
+    </div>
+
+    <div class="container" style="transform-box: content-box; border-right: 50px solid black; border-bottom: 50px solid black; perspective-origin: 100px 100px">
+        <div class="angled"></div>
+        <div class="angled vertical"></div>
+        <div class="contents"></div>
+    </div>
+
+    <div id="scroll-right" class="container" style="transform-box: content-box; border-right: 50px solid black; border-bottom: 50px solid black; perspective-origin: 100px 100px">
+        <div class="angled"></div>
+        <div class="angled vertical"></div>
+        <div class="wide contents"></div>
+    </div>
+</body>
+</html>

Added: trunk/LayoutTests/compositing/transforms/perspective-with-scrolling.html (0 => 261632)


--- trunk/LayoutTests/compositing/transforms/perspective-with-scrolling.html	                        (rev 0)
+++ trunk/LayoutTests/compositing/transforms/perspective-with-scrolling.html	2020-05-13 19:00:53 UTC (rev 261632)
@@ -0,0 +1,82 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:useMockScrollbars=false internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        .container {
+            width: 200px;
+            height: 200px;
+            margin: 20px;
+            display: inline-block;
+            background-color: silver;
+            position: relative;
+            overflow: scroll;
+            perspective: 500px;
+        }
+
+        /* Transformed to be edge-on, and therefore invisible */
+        .angled {
+            position: absolute;
+            top: 0;
+            left: 0;
+            width: 100%;
+            height: 100%;
+            background-color: red;
+            transform: rotateX(90deg);
+        }
+
+        .angled.vertical {
+            transform: rotateY(90deg);
+        }
+
+        .contents {
+            height: 300%;
+        }
+        .wide.contents {
+            width: 300%;
+        }
+
+        #scroll-down .angled {
+            top: 100px;
+        }
+
+        #scroll-right .angled {
+            left: 100px;
+        }
+    </style>
+    <script>
+        // This is a hack to avoid having scrollbars take space, needed until webkit.org/b/211787 is fixed.
+        if (window.internals)
+            internals.setUsesOverlayScrollbars(true);
+
+        window.addEventListener('load', () => {
+            document.getElementById('scroll-down').scrollTop = 100;
+            document.getElementById('scroll-right').scrollLeft = 100;
+        }, false);
+    </script>
+</head>
+<body>
+    <div class="container" style="transform-box: border-box; border-left: 50px solid black; border-top: 50px solid black; perspective-origin: 150px 150px;">
+        <div class="angled"></div>
+        <div class="angled vertical"></div>
+        <div class="contents"></div>
+    </div>
+
+    <div id="scroll-down" class="container" style="transform-box: border-box; border-left: 50px solid black; border-top: 50px solid black; perspective-origin: 150px 150px;">
+        <div class="angled"></div>
+        <div class="angled vertical"></div>
+        <div class="contents"></div>
+    </div>
+
+    <div class="container" style="transform-box: content-box; border-right: 50px solid black; border-bottom: 50px solid black; perspective-origin: 100px 100px">
+        <div class="angled"></div>
+        <div class="angled vertical"></div>
+        <div class="contents"></div>
+    </div>
+
+    <div id="scroll-right" class="container" style="transform-box: content-box; border-right: 50px solid black; border-bottom: 50px solid black; perspective-origin: 100px 100px">
+        <div class="angled"></div>
+        <div class="angled vertical"></div>
+        <div class="wide contents"></div>
+    </div>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (261631 => 261632)


--- trunk/Source/WebCore/ChangeLog	2020-05-13 18:38:40 UTC (rev 261631)
+++ trunk/Source/WebCore/ChangeLog	2020-05-13 19:00:53 UTC (rev 261632)
@@ -1,3 +1,26 @@
+2020-05-13  Simon Fraser  <[email protected]>
+
+        composited scrolling interferes with the propagation of perspective
+        https://bugs.webkit.org/show_bug.cgi?id=156435
+        <rdar://problem/25642222>
+
+        Reviewed by Antti Koivisto.
+        
+        When we made RenderLayerBacking-internal layers for composited scrolling (m_scrollContainerLayer,
+        m_scrolledContentsLayer) we'd lose the effects of the sublayer transform, which was applied
+        to the primary layer, causing perspective on the scroller to not propagate to transformed descendants.
+
+        Fix by moving the sublayer transform to the scroll container layer (adjusting the perspective
+        matrix as appropriate), and making the scrolled contents layer a "preserve3D" layer so
+        that it doesn't flatten.
+
+        This fixes both macOS and iOS.
+
+        Test: compositing/transforms/perspective-with-scrolling.html
+
+        * rendering/RenderLayerBacking.cpp:
+        (WebCore::RenderLayerBacking::updateChildrenTransformAndAnchorPoint):
+
 2020-05-13  Youenn Fablet  <[email protected]>
 
         Allow WebAudioBufferList to dynamically change its number of frames

Modified: trunk/Source/WebCore/rendering/RenderLayerBacking.cpp (261631 => 261632)


--- trunk/Source/WebCore/rendering/RenderLayerBacking.cpp	2020-05-13 18:38:40 UTC (rev 261631)
+++ trunk/Source/WebCore/rendering/RenderLayerBacking.cpp	2020-05-13 19:00:53 UTC (rev 261632)
@@ -646,11 +646,15 @@
         m_graphicsLayer->setAnchorPoint(defaultAnchorPoint);
         if (m_contentsContainmentLayer)
             m_contentsContainmentLayer->setAnchorPoint(defaultAnchorPoint);
+
+        if (m_scrolledContentsLayer)
+            m_scrolledContentsLayer->setPreserves3D(false);
         return;
     }
 
+    auto& renderBox = downcast<RenderBox>(renderer());
     const auto deviceScaleFactor = this->deviceScaleFactor();
-    auto borderBoxRect = downcast<RenderBox>(renderer()).borderBoxRect();
+    auto borderBoxRect = renderBox.borderBoxRect();
     auto transformOrigin = computeTransformOriginForPainting(borderBoxRect);
     auto layerOffset = roundPointToDevicePixels(toLayoutPoint(offsetFromParentGraphicsLayer), deviceScaleFactor);
     auto anchor = FloatPoint3D {
@@ -664,23 +668,46 @@
     else
         m_graphicsLayer->setAnchorPoint(anchor);
 
-    auto* clipLayer = clippingLayer();
+    auto removeChildrenTransformFromLayers = [&](GraphicsLayer* layerToIgnore = nullptr) {
+        auto* clippingLayer = this->clippingLayer();
+        if (clippingLayer && clippingLayer != layerToIgnore)
+            clippingLayer->setChildrenTransform({ });
+        
+        if (m_scrollContainerLayer && m_scrollContainerLayer != layerToIgnore) {
+            m_scrollContainerLayer->setChildrenTransform({ });
+            m_scrolledContentsLayer->setPreserves3D(false);
+        }
+
+        if (m_graphicsLayer != layerToIgnore)
+            m_graphicsLayer->setChildrenTransform({ });
+    };
+
     if (!renderer().style().hasPerspective()) {
-        if (clipLayer)
-            clipLayer->setChildrenTransform({ });
-        else
-            m_graphicsLayer->setChildrenTransform({ });
+        removeChildrenTransformFromLayers();
         return;
     }
 
-    // FIXME: borderBoxRect isn't quite right here. This needs work: webkit.org/b/211787.
-    auto perspectiveRelativeBox = clipLayer ? clippingLayerBox(downcast<RenderBox>(renderer())) : borderBoxRect;
+    auto layerForChildrenTransform = [&] {
+        if (m_scrollContainerLayer)
+            return std::make_tuple(m_scrollContainerLayer.get(), scrollContainerLayerBox(renderBox));
+
+        if (auto* layer = clippingLayer())
+            return std::make_tuple(layer, clippingLayerBox(renderBox));
+
+        return std::make_tuple(m_graphicsLayer.get(), borderBoxRect);
+    };
+
+    auto [layerForPerspective, perspectiveRelativeBox] = layerForChildrenTransform();
+    // FIXME: perspectiveRelativeBox isn't quite right here. This needs work: webkit.org/b/211787.
     auto perspectiveTransform = owningLayer().perspectiveTransform(perspectiveRelativeBox);
-    if (clipLayer) {
-        clipLayer->setChildrenTransform(perspectiveTransform);
-        m_graphicsLayer->setChildrenTransform({ });
-    } else
-        m_graphicsLayer->setChildrenTransform(perspectiveTransform);
+    
+    // If we have scrolling layers, we need the children transform on m_scrollContainerLayer to
+    // affect children of m_scrolledContentsLayer, so set setPreserves3D(true).
+    if (layerForPerspective == m_scrollContainerLayer)
+        m_scrolledContentsLayer->setPreserves3D(true);
+    
+    layerForPerspective->setChildrenTransform(perspectiveTransform);
+    removeChildrenTransformFromLayers(layerForPerspective);
 }
 
 void RenderLayerBacking::updateFilters(const RenderStyle& style)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to