[iOS 13] Caret does not appear in text field if the body element is translated comple...
authorwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Aug 2019 22:17:46 +0000 (22:17 +0000)
committerwenson_hsieh@apple.com <wenson_hsieh@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 29 Aug 2019 22:17:46 +0000 (22:17 +0000)
https://bugs.webkit.org/show_bug.cgi?id=201287
<rdar://problem/54780864>

Reviewed by Tim Horton.

Source/WebKit:

During EditorState computation, we use the hidden editable element heuristic to determine whether we should
begin suppressing selection gestures and UI. Currently, we use the editable root of the selection range to
determine where in the layer tree we should start our ascent, in search of a completely transparent or
completely clipped container.

However, in the case where the selection is inside a focused text field, this causes us to walk up the layer
tree starting at the RenderLayer corresponding to the text field's inner contenteditable div, which is different
than the text field's enclosing RenderLayer in the case where the containing block is transformed, such that no
part of it is within the visible viewport. This scenario is exercised by the below test case, in which the caret
after transforming the body horizontally by -100vw is hidden due to a false positive in the hidden editable area
heuristic.

Fix this by starting the layer tree ascent from the enclosing layer of the text form control if applicable,
instead of the inner editable area under the shadow root of the form control.

Test: editing/selection/ios/show-selection-in-transformed-container.html

* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::platformEditorState const):

LayoutTests:

Add a new layout test that covers this scenario. See WebKit ChangeLog for additional detail.

* editing/selection/ios/show-selection-in-transformed-container-expected.txt: Added.
* editing/selection/ios/show-selection-in-transformed-container.html: Added.

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

LayoutTests/ChangeLog
LayoutTests/editing/selection/ios/show-selection-in-transformed-container-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/ios/show-selection-in-transformed-container.html [new file with mode: 0644]
Source/WebKit/ChangeLog
Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

index 6708a99..436e10f 100644 (file)
@@ -1,3 +1,16 @@
+2019-08-29  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS 13] Caret does not appear in text field if the body element is translated completely out of the viewport
+        https://bugs.webkit.org/show_bug.cgi?id=201287
+        <rdar://problem/54780864>
+
+        Reviewed by Tim Horton.
+
+        Add a new layout test that covers this scenario. See WebKit ChangeLog for additional detail.
+
+        * editing/selection/ios/show-selection-in-transformed-container-expected.txt: Added.
+        * editing/selection/ios/show-selection-in-transformed-container.html: Added.
+
 2019-08-29  Ryosuke Niwa  <rniwa@webkit.org>
 
         Flaky Test: svg/custom/tabindex-order.html
diff --git a/LayoutTests/editing/selection/ios/show-selection-in-transformed-container-expected.txt b/LayoutTests/editing/selection/ios/show-selection-in-transformed-container-expected.txt
new file mode 100644 (file)
index 0000000..f7b3128
--- /dev/null
@@ -0,0 +1,19 @@
+
+
+Apply transform
+This test verifies that after focusing a visible input field in a body element that has been translated horizontally out of view, the caret is still visible. To run the test manually, first tap the input field and check that the caret shows up. Then, tap the button to apply the CSS transform, tap the input field again, and check that the caret is still visible.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+PASS caretBefore.left is 20
+PASS caretBefore.top is 14
+PASS caretBefore.width is 2
+PASS caretBefore.height is 23
+PASS caretAfter.left is 20
+PASS caretAfter.top is 14
+PASS caretAfter.width is 2
+PASS caretAfter.height is 23
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/selection/ios/show-selection-in-transformed-container.html b/LayoutTests/editing/selection/ios/show-selection-in-transformed-container.html
new file mode 100644 (file)
index 0000000..69bc09f
--- /dev/null
@@ -0,0 +1,89 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+<meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/ui-helper.js"></script>
+<style>
+.container {
+    width: 100%;
+    display: -webkit-box;
+}
+
+.container > div {
+    width: 100vw;
+}
+
+.hidden {
+    overflow: hidden;
+}
+
+input {
+    font-size: 18px;
+}
+
+button {
+    margin: 1em;
+}
+
+.translated {
+    transform: translate3d(-100vw, 0, 0);
+}
+</style>
+<script>
+jsTestIsAsync = true;
+
+async function waitForCaretToAppear() {
+    while (true) {
+        const rect = await UIHelper.getUICaretViewRect();
+        if (rect && rect.width && rect.height)
+            return rect;
+    }
+}
+
+addEventListener("load", async () => {
+    description("This test verifies that after focusing a visible input field in a body element that has been translated horizontally out of view, the caret is still visible. To run the test manually, first tap the input field and check that the caret shows up. Then, tap the button to apply the CSS transform, tap the input field again, and check that the caret is still visible.");
+
+    transformButton = document.querySelector("button");
+    transformButton.addEventListener("click", () => document.body.classList.add("translated"));
+    if (!window.testRunner)
+        return;
+
+    // Tap the input and wait for the caret.
+    await UIHelper.activateAndWaitForInputSessionAt(20, 20);
+    caretBefore = await waitForCaretToAppear();
+    shouldBe("caretBefore.left", "20");
+    shouldBe("caretBefore.top", "14");
+    shouldBe("caretBefore.width", "2");
+    shouldBe("caretBefore.height", "23");
+
+    // Translate the body, tap the input again, and wait for the caret.
+    await UIHelper.activateElement(transformButton);
+    await UIHelper.waitForKeyboardToHide();
+    await UIHelper.activateAndWaitForInputSessionAt(20, 20);
+    caretAfter = await waitForCaretToAppear();
+    shouldBe("caretAfter.left", "20");
+    shouldBe("caretAfter.top", "14");
+    shouldBe("caretAfter.width", "2");
+    shouldBe("caretAfter.height", "23");
+
+    document.activeElement.blur();
+    await UIHelper.waitForKeyboardToHide();
+    finishJSTest();
+});
+</script>
+</head>
+<body class="hidden">
+    <div class="container">
+        <div class="hidden">
+            <input id="target">
+        </div>
+        <div class="hidden">
+            <input>
+        </div>
+    </div>
+    <button>Apply transform</button>
+<div id="description"></div>
+<div id="console"></div>
+</body>
+</html>
index ee25bd6..476aa36 100644 (file)
@@ -1,3 +1,31 @@
+2019-08-29  Wenson Hsieh  <wenson_hsieh@apple.com>
+
+        [iOS 13] Caret does not appear in text field if the body element is translated completely out of the viewport
+        https://bugs.webkit.org/show_bug.cgi?id=201287
+        <rdar://problem/54780864>
+
+        Reviewed by Tim Horton.
+
+        During EditorState computation, we use the hidden editable element heuristic to determine whether we should
+        begin suppressing selection gestures and UI. Currently, we use the editable root of the selection range to
+        determine where in the layer tree we should start our ascent, in search of a completely transparent or
+        completely clipped container.
+
+        However, in the case where the selection is inside a focused text field, this causes us to walk up the layer
+        tree starting at the RenderLayer corresponding to the text field's inner contenteditable div, which is different
+        than the text field's enclosing RenderLayer in the case where the containing block is transformed, such that no
+        part of it is within the visible viewport. This scenario is exercised by the below test case, in which the caret
+        after transforming the body horizontally by -100vw is hidden due to a false positive in the hidden editable area
+        heuristic.
+
+        Fix this by starting the layer tree ascent from the enclosing layer of the text form control if applicable,
+        instead of the inner editable area under the shadow root of the form control.
+
+        Test: editing/selection/ios/show-selection-in-transformed-container.html
+
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::platformEditorState const):
+
 2019-08-29  Youenn Fablet  <youenn@apple.com>
 
         Skip fetch event dispatching if no fetch event handler is added at script evaluation time
index d75b2ab..6bb8715 100644 (file)
@@ -273,8 +273,11 @@ void WebPage::platformEditorState(Frame& frame, EditorState& result, IncludePost
             postLayoutData.caretColor = renderer.style().caretColor();
         }
         if (result.isContentEditable) {
-            if (auto container = makeRefPtr(selection.rootEditableElement()))
-                postLayoutData.editableRootIsTransparentOrFullyClipped = isTransparentOrFullyClipped(*container);
+            if (auto editableRootOrFormControl = makeRefPtr(selection.rootEditableElement())) {
+                if (is<HTMLTextFormControlElement>(editableRootOrFormControl->shadowHost()))
+                    editableRootOrFormControl = editableRootOrFormControl->shadowHost();
+                postLayoutData.editableRootIsTransparentOrFullyClipped = isTransparentOrFullyClipped(*editableRootOrFormControl);
+            }
         }
         computeEditableRootHasContentAndPlainText(selection, postLayoutData);
         postLayoutData.selectionStartIsAtParagraphBoundary = atBoundaryOfGranularity(selection.visibleStart(), TextGranularity::ParagraphGranularity, SelectionDirection::DirectionBackward);