Title: [243656] trunk
Revision
243656
Author
wenson_hs...@apple.com
Date
2019-03-29 13:09:02 -0700 (Fri, 29 Mar 2019)

Log Message

REGRESSION (r243250): Text interactions are no longer suppressed when editing in some websites
https://bugs.webkit.org/show_bug.cgi?id=196378
<rdar://problem/49231299>

Reviewed by Simon Fraser.

Source/WebCore:

Enabling async overflow scrolling by default in r243250 exposed an issue with hidden editable area detection
heuristics. Currently, an empty value for RenderLayer::selfClipRect is used to determine whether the layer
enclosing the editable element or form control is completely clipped by a parent (in other words, the clip rect
is empty). With async overflow scrolling, the enclosing layer of the editable element (as seen in the websites
affected by this bug) will now be a clipping root for painting, since it is composited. This means selfClipRect
returns a non-empty rect despite the layer being entirely clipped, which negates the heuristic.

To address this, we adjust the clipping heuristic to instead walk up the layer tree (crossing frame boundaries)
and look for enclosing ancestors with overflow clip. For each layer we find with an overflow clip, compute the
clip rect of the previous layer relative to the ancestor with overflow clip. If the clipping rect is empty, we
know that the layer is hidden.

This isn't a perfect strategy, since it may still report false negatives (reporting a layer as visible when it
is not) in some cases. One such edge case is a series of overflow hidden containers, nested in such a way that
each container is only partially clipped relative to its ancestor, but the deepest layer is completely clipped
relative to the topmost layer. However, this heuristic is relatively cheap (entailing a layer tree walk at
worst) and works for common use cases on the web without risking scenarios in which text selection that
shouldn't be suppressed ends up becoming suppressed.

Test: editing/selection/ios/hide-selection-in-textarea-with-transform.html

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

LayoutTests:

Add a new layout test to exercise the scenario in which a transformed textarea is hidden inside an empty
overflow: hidden container.

* editing/selection/ios/hide-selection-in-textarea-with-transform-expected.txt: Added.
* editing/selection/ios/hide-selection-in-textarea-with-transform.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (243655 => 243656)


--- trunk/LayoutTests/ChangeLog	2019-03-29 20:00:58 UTC (rev 243655)
+++ trunk/LayoutTests/ChangeLog	2019-03-29 20:09:02 UTC (rev 243656)
@@ -1,3 +1,17 @@
+2019-03-29  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        REGRESSION (r243250): Text interactions are no longer suppressed when editing in some websites
+        https://bugs.webkit.org/show_bug.cgi?id=196378
+        <rdar://problem/49231299>
+
+        Reviewed by Simon Fraser.
+
+        Add a new layout test to exercise the scenario in which a transformed textarea is hidden inside an empty
+        overflow: hidden container.
+
+        * editing/selection/ios/hide-selection-in-textarea-with-transform-expected.txt: Added.
+        * editing/selection/ios/hide-selection-in-textarea-with-transform.html: Added.
+
 2019-03-29  Alex Christensen  <achristen...@webkit.org>
 
         Unreviewed test gardening for imported/w3c/web-platform-tests/xhr/send-redirect-post-upload.htm

Added: trunk/LayoutTests/editing/selection/ios/hide-selection-in-textarea-with-transform-expected.txt (0 => 243656)


--- trunk/LayoutTests/editing/selection/ios/hide-selection-in-textarea-with-transform-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/hide-selection-in-textarea-with-transform-expected.txt	2019-03-29 20:09:02 UTC (rev 243656)
@@ -0,0 +1,14 @@
+The caret should appear 
+The caret should not appear
+This test verifies that a textarea with a transform that is completely clipped by an overflow: hidden container can be treated as a hidden editable area. To run the test manually, tap the top button and verify that a caret is shown and the keyboard appears. Then, tap the bottom button and verify that the caret is hidden, but the keyboard is still shown.
+
+Tapping button: visible
+PASS document.activeElement is textarea
+PASS Caret is visible.
+Tapping button: hidden
+PASS document.activeElement is textarea
+PASS Caret is hidden.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/editing/selection/ios/hide-selection-in-textarea-with-transform.html (0 => 243656)


--- trunk/LayoutTests/editing/selection/ios/hide-selection-in-textarea-with-transform.html	                        (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/hide-selection-in-textarea-with-transform.html	2019-03-29 20:09:02 UTC (rev 243656)
@@ -0,0 +1,64 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<script src=""
+<script src=""
+<style>
+div.container {
+    position: relative;
+    height: 0;
+}
+
+textarea {
+    top: 100px;
+    position: absolute;
+    transform: translateZ(0);
+}
+</style>
+</head>
+<body>
+<div class="container">
+    <textarea></textarea>
+</div>
+<button id="visible" _onclick_="focusTextarea('visible')">The caret should appear</button>
+<br>
+<button id="hidden" _onclick_="focusTextarea('hidden')">The caret should not appear</button>
+<p id="description">This test verifies that a textarea with a transform that is completely clipped by an overflow: hidden container can be treated as a hidden editable area. To run the test manually, tap the top button and verify that a caret is shown and the keyboard appears. Then, tap the bottom button and verify that the caret is hidden, but the keyboard is still shown.</p>
+<p id="console"></p>
+<script>
+jsTestIsAsync = true;
+textarea = document.querySelector("textarea");
+
+async function expectCaretVisibility(caretShouldBeVisible)
+{
+    let rect = null;
+    while (!rect || (!caretShouldBeVisible && rect.width && rect.height) || (caretShouldBeVisible && (!rect.width || !rect.height)))
+        rect = await UIHelper.getUICaretViewRect();
+    testPassed(`Caret is ${caretShouldBeVisible ? "visible" : "hidden"}.`);
+}
+
+function focusTextarea(overflow)
+{
+    document.querySelector("div.container").style.overflow = overflow;
+    textarea.focus();
+}
+
+async function tapButtonAndWaitForKeyboard(button, caretShouldBeVisible)
+{
+    debug(`Tapping button: ${button.id}`);
+    await UIHelper.activateElementAndWaitForInputSession(button);
+    shouldBe("document.activeElement", "textarea");
+    await expectCaretVisibility(caretShouldBeVisible);
+    document.activeElement.blur();
+    await UIHelper.waitForKeyboardToHide();
+}
+
+addEventListener("load", async () => {
+    await tapButtonAndWaitForKeyboard(document.getElementById("visible"), true);
+    await tapButtonAndWaitForKeyboard(document.getElementById("hidden"), false);
+    finishJSTest();
+});
+</script>
+</body>
+</html>

Modified: trunk/Source/WebCore/ChangeLog (243655 => 243656)


--- trunk/Source/WebCore/ChangeLog	2019-03-29 20:00:58 UTC (rev 243655)
+++ trunk/Source/WebCore/ChangeLog	2019-03-29 20:09:02 UTC (rev 243656)
@@ -1,3 +1,35 @@
+2019-03-29  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        REGRESSION (r243250): Text interactions are no longer suppressed when editing in some websites
+        https://bugs.webkit.org/show_bug.cgi?id=196378
+        <rdar://problem/49231299>
+
+        Reviewed by Simon Fraser.
+
+        Enabling async overflow scrolling by default in r243250 exposed an issue with hidden editable area detection
+        heuristics. Currently, an empty value for RenderLayer::selfClipRect is used to determine whether the layer
+        enclosing the editable element or form control is completely clipped by a parent (in other words, the clip rect
+        is empty). With async overflow scrolling, the enclosing layer of the editable element (as seen in the websites
+        affected by this bug) will now be a clipping root for painting, since it is composited. This means selfClipRect
+        returns a non-empty rect despite the layer being entirely clipped, which negates the heuristic.
+
+        To address this, we adjust the clipping heuristic to instead walk up the layer tree (crossing frame boundaries)
+        and look for enclosing ancestors with overflow clip. For each layer we find with an overflow clip, compute the
+        clip rect of the previous layer relative to the ancestor with overflow clip. If the clipping rect is empty, we
+        know that the layer is hidden.
+
+        This isn't a perfect strategy, since it may still report false negatives (reporting a layer as visible when it
+        is not) in some cases. One such edge case is a series of overflow hidden containers, nested in such a way that
+        each container is only partially clipped relative to its ancestor, but the deepest layer is completely clipped
+        relative to the topmost layer. However, this heuristic is relatively cheap (entailing a layer tree walk at
+        worst) and works for common use cases on the web without risking scenarios in which text selection that
+        shouldn't be suppressed ends up becoming suppressed.
+
+        Test: editing/selection/ios/hide-selection-in-textarea-with-transform.html
+
+        * rendering/RenderLayer.cpp:
+        (WebCore::RenderLayer::isTransparentOrFullyClippedRespectingParentFrames const):
+
 2019-03-29  Takashi Komori  <takashi.kom...@sony.com>
 
         [Curl] Add Server Trust Evaluation Support.

Modified: trunk/Source/WebCore/rendering/RenderLayer.cpp (243655 => 243656)


--- trunk/Source/WebCore/rendering/RenderLayer.cpp	2019-03-29 20:00:58 UTC (rev 243655)
+++ trunk/Source/WebCore/rendering/RenderLayer.cpp	2019-03-29 20:09:02 UTC (rev 243656)
@@ -6648,8 +6648,17 @@
             return true;
     }
 
-    for (auto* layer = this; layer; layer = enclosingFrameRenderLayer(*layer)) {
-        if (layer->selfClipRect().isEmpty())
+    RenderLayer* enclosingClipLayer = nullptr;
+    for (auto* layer = this; layer; layer = enclosingClipLayer ? enclosingClipLayer->parent() : enclosingFrameRenderLayer(*layer)) {
+        enclosingClipLayer = layer->enclosingOverflowClipLayer(IncludeSelfOrNot::IncludeSelf);
+        if (!enclosingClipLayer)
+            continue;
+
+        LayoutRect layerBounds;
+        ClipRect backgroundRect;
+        ClipRect foregroundRect;
+        layer->calculateRects({ enclosingClipLayer, TemporaryClipRects }, LayoutRect::infiniteRect(), layerBounds, backgroundRect, foregroundRect, layer->offsetFromAncestor(enclosingClipLayer));
+        if (backgroundRect.isEmpty())
             return true;
     }
 
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to