Title: [264415] trunk
Revision
264415
Author
[email protected]
Date
2020-07-15 13:01:53 -0700 (Wed, 15 Jul 2020)

Log Message

itsnicethat.com page is sometimes non-scrollable
https://bugs.webkit.org/show_bug.cgi?id=214362
<rdar://problem/65431127>

Reviewed by Tim Horton.

Source/WebCore:

This page uses 'clip-path: inset(1px); on a 1px x 1px element to hit descendants from
hit-testing and compositing code turns this into a layer mask using a CAShapeLayer.
Our layer/view hit-testing code failed to check for masking; if a layer has a mask,
it needs to be handled like -masksToBounds, in that points outside the mask should
never hit descendant layers.

Make a -_web_maskContainsPoint: to test whether the point is inside the mask layer,
and _web_maskMayIntersectRect: which does approximate testing using path bounds
intersection (this is used for editable regions).

Also convert some hard-coded winding rule strings to use the CA constants.

Tests: fast/scrolling/ios/clip-path-hit-test.html
       fast/scrolling/mac/clip-path-hit-test.html

* page/scrolling/mac/ScrollingTreeMac.mm:
(collectDescendantLayersAtPoint):
* platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm:
(WebCore::PlatformCALayerCocoa::shapeWindRule const):
(WebCore::PlatformCALayerCocoa::setShapeWindRule):
* platform/graphics/cocoa/WebCoreCALayerExtras.h:
* platform/graphics/cocoa/WebCoreCALayerExtras.mm:
(-[CALayer _web_maskContainsPoint:]):
(-[CALayer _web_maskMayIntersectRect:]):

Source/WebKit:

This page uses 'clip-path: inset(1px); on a 1px x 1px element to hit descendants from
hit-testing and compositing code turns this into a layer mask using a CAShapeLayer.
Our layer/view hit-testing code failed to check for masking; if a layer has a mask,
it needs to be handled like -masksToBounds, in that points outside the mask should
never hit descendant layers.

Make a -_web_maskContainsPoint: to test whether the point is inside the mask layer,
and _web_maskMayIntersectRect: which does approximate testing using path bounds
intersection (this is used for editable regions).

Also convert some hard-coded winding rule strings to use the CA constants.

* Shared/RemoteLayerTree/RemoteLayerTreePropertyApplier.mm:
(WebKit::RemoteLayerTreePropertyApplier::applyPropertiesToLayer):
* UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:
(WebKit::collectDescendantViewsAtPoint):
(WebKit::collectDescendantViewsInRect):

LayoutTests:

* fast/scrolling/ios/clip-path-hit-test-expected.txt: Added.
* fast/scrolling/ios/clip-path-hit-test.html: Added.
* fast/scrolling/mac/clip-path-hit-test-expected.txt: Added.
* fast/scrolling/mac/clip-path-hit-test.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (264414 => 264415)


--- trunk/LayoutTests/ChangeLog	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/LayoutTests/ChangeLog	2020-07-15 20:01:53 UTC (rev 264415)
@@ -1,3 +1,16 @@
+2020-07-15  Simon Fraser  <[email protected]>
+
+        itsnicethat.com page is sometimes non-scrollable
+        https://bugs.webkit.org/show_bug.cgi?id=214362
+        <rdar://problem/65431127>
+
+        Reviewed by Tim Horton.
+
+        * fast/scrolling/ios/clip-path-hit-test-expected.txt: Added.
+        * fast/scrolling/ios/clip-path-hit-test.html: Added.
+        * fast/scrolling/mac/clip-path-hit-test-expected.txt: Added.
+        * fast/scrolling/mac/clip-path-hit-test.html: Added.
+
 2020-07-15  Mark Lam  <[email protected]>
 
         Add handling of out of memory handling while adding a worklet module.

Added: trunk/LayoutTests/fast/scrolling/ios/clip-path-hit-test-expected.txt (0 => 264415)


--- trunk/LayoutTests/fast/scrolling/ios/clip-path-hit-test-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/clip-path-hit-test-expected.txt	2020-07-15 20:01:53 UTC (rev 264415)
@@ -0,0 +1,16 @@
+
+Test scroll over content
+PASS overflowScrollEventCount > 0 is true
+PASS windowScrollEventCount is 0
+
+Test scroll over content
+PASS overflowScrollEventCount is 0
+PASS windowScrollEventCount > 0 is true
+
+Test scroll over content
+PASS overflowScrollEventCount is 0
+PASS windowScrollEventCount > 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/ios/clip-path-hit-test.html (0 => 264415)


--- trunk/LayoutTests/fast/scrolling/ios/clip-path-hit-test.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/ios/clip-path-hit-test.html	2020-07-15 20:01:53 UTC (rev 264415)
@@ -0,0 +1,117 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        body {
+            height: 2000px;
+        }
+        .container {
+            clip-path: circle(100px at 120px 120px);
+        }
+        .scroller {
+            height: 300px;
+            width: 300px;
+            border: 1px solid black;
+            padding: 5px;
+            overflow: scroll;
+        }
+        .content {
+            width: 200%;
+            height: 300%;
+            background-image: repeating-linear-gradient(white, silver 200px);
+        }
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        var jsTestIsAsync = true;
+
+        var scroller;
+        var overflowScrollEventCount = 0;
+        var windowScrollEventCount = 0;
+
+        async function resetScrollPositions()
+        {
+            window.scrollTo(0, 0);
+            scroller.scrollTop = 0;
+            
+            // Wait for scroll events to fire.
+            await UIHelper.animationFrame();
+
+            overflowScrollEventCount = 0;
+            windowScrollEventCount = 0;
+        }
+
+        async function verticalSwipeGestureAt(x, y)
+        {
+            await UIHelper.callFunctionAndWaitForScrollToFinish(async () => {
+                await UIHelper.dragFromPointToPoint(x, y, x, y - 30, 0.25);
+            });
+        }
+        
+        async function testScrollInCenter()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await verticalSwipeGestureAt(150, 150);
+
+            shouldBe('overflowScrollEventCount > 0', 'true');
+            shouldBe('windowScrollEventCount', '0');
+        }
+
+        async function testScrollInUpperLeft()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await verticalSwipeGestureAt(50, 50);
+
+            shouldBe('overflowScrollEventCount', '0');
+            shouldBe('windowScrollEventCount > 0', 'true');
+        }
+
+        async function testScrollInLowerRight()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await verticalSwipeGestureAt(200, 200);
+
+            shouldBe('overflowScrollEventCount', '0');
+            shouldBe('windowScrollEventCount > 0', 'true');
+        }
+
+        async function scrollTest()
+        {
+            await testScrollInCenter();
+            await testScrollInUpperLeft();
+            await testScrollInLowerRight();
+
+            finishJSTest();
+        }
+
+        window.addEventListener('load', () => {
+            scroller = document.querySelector('.scroller');
+            scroller.addEventListener('scroll', () => {
+                ++overflowScrollEventCount;
+            }, false);
+
+            window.addEventListener('scroll', () => {
+                ++windowScrollEventCount;
+            }, false);
+
+            setTimeout(scrollTest, 0);
+        }, false);
+    </script>
+</head>
+<body>
+    <div class="container">
+        <div class="scroller">
+            <div class="content"></div>
+        </div>
+    </div>
+    <div id="console"></div>
+    <script src=""
+</body>
+</html>

Added: trunk/LayoutTests/fast/scrolling/mac/clip-path-hit-test-expected.txt (0 => 264415)


--- trunk/LayoutTests/fast/scrolling/mac/clip-path-hit-test-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/clip-path-hit-test-expected.txt	2020-07-15 20:01:53 UTC (rev 264415)
@@ -0,0 +1,16 @@
+
+Test scroll over content
+PASS overflowScrollEventCount > 0 is true
+PASS windowScrollEventCount is 0
+
+Test scroll over content
+PASS overflowScrollEventCount is 0
+PASS windowScrollEventCount > 0 is true
+
+Test scroll over content
+PASS overflowScrollEventCount is 0
+PASS windowScrollEventCount > 0 is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/scrolling/mac/clip-path-hit-test.html (0 => 264415)


--- trunk/LayoutTests/fast/scrolling/mac/clip-path-hit-test.html	                        (rev 0)
+++ trunk/LayoutTests/fast/scrolling/mac/clip-path-hit-test.html	2020-07-15 20:01:53 UTC (rev 264415)
@@ -0,0 +1,110 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ internal:AsyncOverflowScrollingEnabled=true ] -->
+<html>
+<head>
+    <style>
+        body {
+            height: 2000px;
+        }
+        .container {
+            clip-path: circle(100px at 120px 120px);
+        }
+        .scroller {
+            height: 300px;
+            width: 300px;
+            border: 1px solid black;
+            padding: 5px;
+            overflow: scroll;
+        }
+        .content {
+            width: 200%;
+            height: 300%;
+            background-image: repeating-linear-gradient(white, silver 200px);
+        }
+    </style>
+    <script src=""
+    <script src=""
+    <script>
+        var jsTestIsAsync = true;
+
+        var scroller;
+        var overflowScrollEventCount = 0;
+        var windowScrollEventCount = 0;
+
+        async function resetScrollPositions()
+        {
+            window.scrollTo(0, 0);
+            scroller.scrollTop = 0;
+            
+            // Wait for scroll events to fire.
+            await UIHelper.animationFrame();
+
+            overflowScrollEventCount = 0;
+            windowScrollEventCount = 0;
+        }
+        
+        async function testScrollInCenter()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(150, 150);
+
+            shouldBe('overflowScrollEventCount > 0', 'true');
+            shouldBe('windowScrollEventCount', '0');
+        }
+
+        async function testScrollInUpperLeft()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(50, 50);
+
+            shouldBe('overflowScrollEventCount', '0');
+            shouldBe('windowScrollEventCount > 0', 'true');
+        }
+
+        async function testScrollInLowerRight()
+        {
+            debug('');
+            debug('Test scroll over content');
+            await resetScrollPositions();
+            await UIHelper.mouseWheelScrollAt(200, 200);
+
+            shouldBe('overflowScrollEventCount', '0');
+            shouldBe('windowScrollEventCount > 0', 'true');
+        }
+
+        async function scrollTest()
+        {
+            await testScrollInCenter();
+            await testScrollInUpperLeft();
+            await testScrollInLowerRight();
+
+            finishJSTest();
+        }
+
+        window.addEventListener('load', () => {
+            scroller = document.querySelector('.scroller');
+            scroller.addEventListener('scroll', () => {
+                ++overflowScrollEventCount;
+            }, false);
+
+            window.addEventListener('scroll', () => {
+                ++windowScrollEventCount;
+            }, false);
+
+            setTimeout(scrollTest, 0);
+        }, false);
+    </script>
+</head>
+<body>
+    <div class="container">
+        <div class="scroller">
+            <div class="content"></div>
+        </div>
+    </div>
+    <div id="console"></div>
+    <script src=""
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (264414 => 264415)


--- trunk/Source/WebCore/ChangeLog	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/Source/WebCore/ChangeLog	2020-07-15 20:01:53 UTC (rev 264415)
@@ -1,3 +1,36 @@
+2020-07-15  Simon Fraser  <[email protected]>
+
+        itsnicethat.com page is sometimes non-scrollable
+        https://bugs.webkit.org/show_bug.cgi?id=214362
+        <rdar://problem/65431127>
+
+        Reviewed by Tim Horton.
+
+        This page uses 'clip-path: inset(1px); on a 1px x 1px element to hit descendants from
+        hit-testing and compositing code turns this into a layer mask using a CAShapeLayer.
+        Our layer/view hit-testing code failed to check for masking; if a layer has a mask,
+        it needs to be handled like -masksToBounds, in that points outside the mask should
+        never hit descendant layers.
+
+        Make a -_web_maskContainsPoint: to test whether the point is inside the mask layer,
+        and _web_maskMayIntersectRect: which does approximate testing using path bounds
+        intersection (this is used for editable regions).
+
+        Also convert some hard-coded winding rule strings to use the CA constants.
+
+        Tests: fast/scrolling/ios/clip-path-hit-test.html
+               fast/scrolling/mac/clip-path-hit-test.html
+
+        * page/scrolling/mac/ScrollingTreeMac.mm:
+        (collectDescendantLayersAtPoint):
+        * platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm:
+        (WebCore::PlatformCALayerCocoa::shapeWindRule const):
+        (WebCore::PlatformCALayerCocoa::setShapeWindRule):
+        * platform/graphics/cocoa/WebCoreCALayerExtras.h:
+        * platform/graphics/cocoa/WebCoreCALayerExtras.mm:
+        (-[CALayer _web_maskContainsPoint:]):
+        (-[CALayer _web_maskMayIntersectRect:]):
+
 2020-07-15  Mark Lam  <[email protected]>
 
         Add handling of out of memory handling while adding a worklet module.

Modified: trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.mm (264414 => 264415)


--- trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.mm	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/Source/WebCore/page/scrolling/mac/ScrollingTreeMac.mm	2020-07-15 20:01:53 UTC (rev 264415)
@@ -35,6 +35,7 @@
 #import "ScrollingTreeOverflowScrollingNodeMac.h"
 #import "ScrollingTreePositionedNode.h"
 #import "ScrollingTreeStickyNode.h"
+#import "WebCoreCALayerExtras.h"
 #import "WebLayer.h"
 #import "WheelEventTestMonitor.h"
 #import <wtf/text/TextStream.h>
@@ -83,6 +84,9 @@
     if (parent.masksToBounds && ![parent containsPoint:point])
         return;
 
+    if (parent.mask && ![parent _web_maskContainsPoint:point])
+        return;
+
     for (CALayer *layer in [parent sublayers]) {
         CALayer *layerWithResolvedAnimations = layer;
 

Modified: trunk/Source/WebCore/platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm (264414 => 264415)


--- trunk/Source/WebCore/platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/Source/WebCore/platform/graphics/ca/cocoa/PlatformCALayerCocoa.mm	2020-07-15 20:01:53 UTC (rev 264415)
@@ -966,7 +966,7 @@
     ASSERT(m_layerType == LayerTypeShapeLayer);
 
     NSString *fillRule = [(CAShapeLayer *)m_layer fillRule];
-    if ([fillRule isEqualToString:@"even-odd"])
+    if ([fillRule isEqualToString:kCAFillRuleEvenOdd])
         return WindRule::EvenOdd;
 
     return WindRule::NonZero;
@@ -978,10 +978,10 @@
 
     switch (windRule) {
     case WindRule::NonZero:
-        [(CAShapeLayer *)m_layer setFillRule:@"non-zero"];
+        [(CAShapeLayer *)m_layer setFillRule:kCAFillRuleNonZero];
         break;
     case WindRule::EvenOdd:
-        [(CAShapeLayer *)m_layer setFillRule:@"even-odd"];
+        [(CAShapeLayer *)m_layer setFillRule:kCAFillRuleEvenOdd];
         break;
     }
 }

Modified: trunk/Source/WebCore/platform/graphics/cocoa/WebCoreCALayerExtras.h (264414 => 264415)


--- trunk/Source/WebCore/platform/graphics/cocoa/WebCoreCALayerExtras.h	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/Source/WebCore/platform/graphics/cocoa/WebCoreCALayerExtras.h	2020-07-15 20:01:53 UTC (rev 264415)
@@ -27,9 +27,12 @@
 
 @interface CALayer (WebCoreCALayerExtras)
 
++ (CALayer *)_web_renderLayerWithContextID:(uint32_t)contextID;
+
 - (void)web_disableAllActions;
 - (void)_web_setLayerBoundsOrigin:(CGPoint)origin;
 - (void)_web_setLayerTopLeftPosition:(CGPoint)position;
-+ (CALayer *)_web_renderLayerWithContextID:(uint32_t)contextID;
+- (BOOL)_web_maskContainsPoint:(CGPoint)point;
+- (BOOL)_web_maskMayIntersectRect:(CGRect)rect;
 
 @end

Modified: trunk/Source/WebCore/platform/graphics/cocoa/WebCoreCALayerExtras.mm (264414 => 264415)


--- trunk/Source/WebCore/platform/graphics/cocoa/WebCoreCALayerExtras.mm	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/Source/WebCore/platform/graphics/cocoa/WebCoreCALayerExtras.mm	2020-07-15 20:01:53 UTC (rev 264415)
@@ -88,4 +88,32 @@
     return layerHost;
 }
 
+- (BOOL)_web_maskContainsPoint:(CGPoint)point
+{
+    if (!self.mask)
+        return NO;
+
+    CGPoint pointInMask = [self.mask convertPoint:point fromLayer:self];
+    if (auto *shapeMask = dynamic_objc_cast<CAShapeLayer>(self.mask)) {
+        bool isEvenOddFill = [shapeMask.fillRule isEqualToString:kCAFillRuleEvenOdd];
+        return CGPathContainsPoint(shapeMask.path, nullptr, pointInMask, isEvenOddFill);
+    }
+
+    return [self.mask containsPoint:pointInMask];
+}
+
+- (BOOL)_web_maskMayIntersectRect:(CGRect)rect
+{
+    if (!self.mask)
+        return NO;
+
+    CGRect rectInMask = [self.mask convertRect:rect fromLayer:self];
+    if (auto *shapeMask = dynamic_objc_cast<CAShapeLayer>(self.mask)) {
+        CGRect pathBounds = CGPathGetPathBoundingBox(shapeMask.path);
+        return CGRectIntersectsRect(pathBounds, rectInMask);
+    }
+
+    return CGRectIntersectsRect(self.mask.bounds, rectInMask);
+}
+
 @end

Modified: trunk/Source/WebKit/ChangeLog (264414 => 264415)


--- trunk/Source/WebKit/ChangeLog	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/Source/WebKit/ChangeLog	2020-07-15 20:01:53 UTC (rev 264415)
@@ -1,3 +1,29 @@
+2020-07-15  Simon Fraser  <[email protected]>
+
+        itsnicethat.com page is sometimes non-scrollable
+        https://bugs.webkit.org/show_bug.cgi?id=214362
+        <rdar://problem/65431127>
+
+        Reviewed by Tim Horton.
+
+        This page uses 'clip-path: inset(1px); on a 1px x 1px element to hit descendants from
+        hit-testing and compositing code turns this into a layer mask using a CAShapeLayer.
+        Our layer/view hit-testing code failed to check for masking; if a layer has a mask,
+        it needs to be handled like -masksToBounds, in that points outside the mask should
+        never hit descendant layers.
+
+        Make a -_web_maskContainsPoint: to test whether the point is inside the mask layer,
+        and _web_maskMayIntersectRect: which does approximate testing using path bounds
+        intersection (this is used for editable regions).
+
+        Also convert some hard-coded winding rule strings to use the CA constants.
+
+        * Shared/RemoteLayerTree/RemoteLayerTreePropertyApplier.mm:
+        (WebKit::RemoteLayerTreePropertyApplier::applyPropertiesToLayer):
+        * UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm:
+        (WebKit::collectDescendantViewsAtPoint):
+        (WebKit::collectDescendantViewsInRect):
+
 2020-07-15  Chris Dumez  <[email protected]>
 
         Improve navigation policy decision release logging to help better diagnose issues

Modified: trunk/Source/WebKit/Shared/RemoteLayerTree/RemoteLayerTreePropertyApplier.mm (264414 => 264415)


--- trunk/Source/WebKit/Shared/RemoteLayerTree/RemoteLayerTreePropertyApplier.mm	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/Source/WebKit/Shared/RemoteLayerTree/RemoteLayerTreePropertyApplier.mm	2020-07-15 20:01:53 UTC (rev 264415)
@@ -230,10 +230,10 @@
         CAShapeLayer *shapeLayer = (CAShapeLayer *)layer;
         switch (properties.windRule) {
         case WindRule::NonZero:
-            shapeLayer.fillRule = @"non-zero";
+            shapeLayer.fillRule = kCAFillRuleNonZero;
             break;
         case WindRule::EvenOdd:
-            shapeLayer.fillRule = @"even-odd";
+            shapeLayer.fillRule = kCAFillRuleEvenOdd;
             break;
         }
     }

Modified: trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm (264414 => 264415)


--- trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm	2020-07-15 19:38:00 UTC (rev 264414)
+++ trunk/Source/WebKit/UIProcess/RemoteLayerTree/ios/RemoteLayerTreeViews.mm	2020-07-15 20:01:53 UTC (rev 264415)
@@ -36,6 +36,7 @@
 #import "WKDrawingView.h"
 #import <WebCore/Region.h>
 #import <WebCore/TransformationMatrix.h>
+#import <WebCore/WebCoreCALayerExtras.h>
 #import <pal/spi/cocoa/QuartzCoreSPI.h>
 #import <wtf/SoftLinking.h>
 
@@ -46,6 +47,9 @@
     if (parent.clipsToBounds && ![parent pointInside:point withEvent:event])
         return;
 
+    if (parent.layer.mask && ![parent.layer _web_maskContainsPoint:point])
+        return;
+
     for (UIView *view in [parent subviews]) {
         CGPoint subviewPoint = [view convertPoint:point fromView:parent];
 
@@ -88,6 +92,9 @@
     if (parent.clipsToBounds && !CGRectIntersectsRect(parent.bounds, rect))
         return;
 
+    if (parent.layer.mask && ![parent.layer _web_maskMayIntersectRect:rect])
+        return;
+
     for (UIView *view in parent.subviews) {
         CGRect subviewRect = [view convertRect:rect fromView:parent];
 
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to