Non-editable text selections should be modifiable with hardware keyboard
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 25 Jun 2019 23:41:31 +0000 (23:41 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Tue, 25 Jun 2019 23:41:31 +0000 (23:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=199204
<rdar://problem/51651496>

Reviewed by Wenson Hsieh.

Source/WebKit:

UIKit changes are need for this to work, including <rdar://problem/48322899>.
Note that without these changes, -canPerformAction is never called for _move*
selectors.

* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView canPerformAction:withSender:]):

LayoutTests:

Add a test, skipped for now, until we have the fix for <rdar://problem/48322899>.

* editing/selection/ios/select-non-editable-text-using-keyboard-expected.txt: Added.
* editing/selection/ios/select-non-editable-text-using-keyboard.html: Added.
* platform/ios/TestExpectations:
* resources/ui-helper.js:
(window.UIHelper.callFunctionAndWaitForEvent): Added.

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

LayoutTests/ChangeLog
LayoutTests/editing/selection/ios/select-non-editable-text-using-keyboard-expected.txt [new file with mode: 0644]
LayoutTests/editing/selection/ios/select-non-editable-text-using-keyboard.html [new file with mode: 0644]
LayoutTests/platform/ios/TestExpectations
LayoutTests/resources/ui-helper.js
Source/WebKit/ChangeLog
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

index cd025e0..6c61f18 100644 (file)
@@ -1,5 +1,21 @@
 2019-06-25  Daniel Bates  <dabates@apple.com>
 
+        Non-editable text selections should be modifiable with hardware keyboard
+        https://bugs.webkit.org/show_bug.cgi?id=199204
+        <rdar://problem/51651496>
+
+        Reviewed by Wenson Hsieh.
+
+        Add a test, skipped for now, until we have the fix for <rdar://problem/48322899>.
+
+        * editing/selection/ios/select-non-editable-text-using-keyboard-expected.txt: Added.
+        * editing/selection/ios/select-non-editable-text-using-keyboard.html: Added.
+        * platform/ios/TestExpectations:
+        * resources/ui-helper.js:
+        (window.UIHelper.callFunctionAndWaitForEvent): Added.
+
+2019-06-25  Daniel Bates  <dabates@apple.com>
+
         [iOS] Should not process key events in non-editable elements using IME
         https://bugs.webkit.org/show_bug.cgi?id=199122
         <rdar://problem/52006654>
diff --git a/LayoutTests/editing/selection/ios/select-non-editable-text-using-keyboard-expected.txt b/LayoutTests/editing/selection/ios/select-non-editable-text-using-keyboard-expected.txt
new file mode 100644 (file)
index 0000000..be2232e
--- /dev/null
@@ -0,0 +1,45 @@
+Test selecting non-editable text using the keyboard.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+
+Press Shift + right arrow to select the next character:
+PASS window.getSelection().toString() is "He"
+
+Press Shift + left arrow to select the previous character:
+PASS window.getSelection().toString() is "Her"
+
+Press Shift + Option + right arrow to select to the end of the word:
+PASS window.getSelection().toString() is "Here's "
+
+Press Shift + Option + left arrow to select to the beginning of the word:
+PASS window.getSelection().toString() is "The"
+
+Press Shift + Control + right arrow to select to the end of the line:
+PASS window.getSelection().toString() is "Here's to the crazy ones."
+
+Press Shift + Control + left arrow to select to the beginning of the line:
+PASS window.getSelection().toString() is "The misfits."
+
+Press Shift + up arrow to select up:
+PASS window.getSelection().toString() is "Here's to the crazy ones.\n\nT"
+
+Press Shift + down arrow to select down:
+PASS window.getSelection().toString() is "The misfits.\n\nT"
+
+Press Shift + Option + down arrow to select to the end of the paragraph:
+PASS window.getSelection().toString() is "Here's to the crazy ones."
+
+Press Shift + Option + up arrow to select to the beginning of the paragraph:
+PASS window.getSelection().toString() is "The rebels."
+
+Press Shift + Control + down arrow to select to the end of the document:
+PASS window.getSelection().toString() is "Here's to the crazy ones.\n\nThe misfits.\n\nThe rebels."
+
+Press Shift + Control + up arrow to select to the beginning of the document:
+PASS window.getSelection().toString() is "Here's to the crazy ones.\n\nThe misfits."
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/editing/selection/ios/select-non-editable-text-using-keyboard.html b/LayoutTests/editing/selection/ios/select-non-editable-text-using-keyboard.html
new file mode 100644 (file)
index 0000000..e14810a
--- /dev/null
@@ -0,0 +1,176 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src="../../../resources/js-test.js"></script>
+<script src="../../../resources/ui-helper.js"></script>
+<style>
+body.hide-everything-except-test-container > :not(#test-container) {
+    display: none;
+}
+</style>
+</head>
+<body>
+<div id="test-container">
+    <p>Here's to the crazy ones.</p>
+    <p>The misfits.</p>
+    <p>The rebels.</p>
+</div>
+<script>
+let testContainer = document.getElementById("test-container");
+let paragraphs = testContainer.children;
+let selection = window.getSelection();
+
+function toggleOnlyShowTestContainer()
+{
+    document.body.classList.toggle("hide-everything-except-test-container");
+}
+
+async function testExtendSelectionToNextCharacter()
+{
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[0].firstChild, 0, paragraphs[0].firstChild, 1), document, "selectionchange");
+
+    debug("<br>Press Shift + right arrow to select the next character:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("rightArrow", ["shiftKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "He");
+}
+
+async function testExtendSelectionToPreviousCharacter()
+{
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[0].firstChild, 3, paragraphs[0].firstChild, 1), document, "selectionchange");
+
+    debug("<br>Press Shift + left arrow to select the previous character:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("leftArrow", ["shiftKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "Her");
+}
+
+async function testExtendSelectionToEndOfWord()
+{
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[0].firstChild, 0, paragraphs[0].firstChild, 1), document, "selectionchange");
+
+    debug("<br>Press Shift + Option + right arrow to select to the end of the word:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("rightArrow", ["shiftKey", "altKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "Here's ");
+}
+
+async function testExtendSelectionToBeginningOfWord()
+{
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[1].firstChild, 3, paragraphs[1].firstChild, 1), document, "selectionchange");
+
+    debug("<br>Press Shift + Option + left arrow to select to the beginning of the word:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("leftArrow", ["shiftKey", "altKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "The");
+}
+
+async function textExtendSelectionToEndOfLine()
+{
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[0].firstChild, 0, paragraphs[0].firstChild, 1), document, "selectionchange");
+
+    debug("<br>Press Shift + Control + right arrow to select to the end of the line:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("rightArrow", ["shiftKey", "ctrlKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "Here's to the crazy ones.");
+}
+
+async function testExtendSelectionToBeginningOfLine()
+{
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[1].firstChild, paragraphs[1].firstChild.length, paragraphs[1].firstChild, paragraphs[1].firstChild.length - 1), document, "selectionchange");
+
+    debug("<br>Press Shift + Control + left arrow to select to the beginning of the line:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("leftArrow", ["shiftKey", "ctrlKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "The misfits.");
+}
+
+async function testExtendSelectionUp()
+{
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[1].firstChild, 1, paragraphs[1].firstChild, 0), document, "selectionchange");
+
+    debug("<br>Press Shift + up arrow to select up:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("upArrow", ["shiftKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "Here's to the crazy ones.\n\nT");
+}
+
+async function testExtendSelectionDown()
+{
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[1].firstChild, 0, paragraphs[1].firstChild, 1), document, "selectionchange");
+
+    debug("<br>Press Shift + down arrow to select down:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("downArrow", ["shiftKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "The misfits.\n\nT");
+}
+
+async function testExtendSelectionToEndOfParagraph()
+{
+    toggleOnlyShowTestContainer();
+
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[0].firstChild, 0, paragraphs[0].firstChild, 1), document, "selectionchange");
+
+    debug("<br>Press Shift + Option + down arrow to select to the end of the paragraph:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("downArrow", ["shiftKey", "altKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "Here's to the crazy ones.");
+
+    toggleOnlyShowTestContainer();
+}
+
+async function testExtendSelectionToBeginningOfParagraph()
+{
+    toggleOnlyShowTestContainer();
+
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[2].firstChild, paragraphs[2].firstChild.length, paragraphs[2].firstChild, paragraphs[2].firstChild.length - 1), document, "selectionchange");
+
+    debug("<br>Press Shift + Option + up arrow to select to the beginning of the paragraph:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("upArrow", ["shiftKey", "altKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "The rebels.");
+
+    toggleOnlyShowTestContainer();
+}
+
+async function testExtendSelectionToEndOfDocument()
+{
+    toggleOnlyShowTestContainer();
+
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[0].firstChild, 0, paragraphs[0].firstChild, 1), document, "selectionchange");
+
+    debug("<br>Press Shift + Control + down arrow to select to the end of the document:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("downArrow", ["shiftKey", "ctrlKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "Here's to the crazy ones.\n\nThe misfits.\n\nThe rebels.");
+
+    toggleOnlyShowTestContainer();
+}
+
+async function testExtendSelectionToBeginningOfDocument()
+{
+    toggleOnlyShowTestContainer();
+
+    await UIHelper.callFunctionAndWaitForEvent(() => selection.setBaseAndExtent(paragraphs[1].firstChild, paragraphs[1].firstChild.length, paragraphs[1].firstChild, paragraphs[1].firstChild.length - 1), document, "selectionchange");
+
+    debug("<br>Press Shift + Control + up arrow to select to the beginning of the document:");
+    await UIHelper.callFunctionAndWaitForEvent(() => window.testRunner && UIHelper.keyDown("upArrow", ["shiftKey", "ctrlKey"]) , document, "selectionchange");
+    shouldBeEqualToString("window.getSelection().toString()", "Here's to the crazy ones.\n\nThe misfits.");
+
+    toggleOnlyShowTestContainer();
+}
+
+async function runTests()
+{
+    await testExtendSelectionToNextCharacter();
+    await testExtendSelectionToPreviousCharacter();
+    await testExtendSelectionToEndOfWord();
+    await testExtendSelectionToBeginningOfWord();
+    await textExtendSelectionToEndOfLine();
+    await testExtendSelectionToBeginningOfLine();
+    await testExtendSelectionUp();
+    await testExtendSelectionDown();
+    await testExtendSelectionToEndOfParagraph();
+    await testExtendSelectionToBeginningOfParagraph();
+    await testExtendSelectionToEndOfDocument();
+    await testExtendSelectionToBeginningOfDocument();
+
+    document.body.removeChild(document.getElementById("test-container"));
+    finishJSTest();
+}
+
+window.jsTestIsAsync = true;
+description("Test selecting non-editable text using the keyboard.");
+runTests();
+</script>
+</body>
+</html>
index 453d558..914cca6 100644 (file)
@@ -3251,3 +3251,6 @@ webkit.org/b/197778 [ Debug ] webgl/2.0.0/conformance2/attribs/gl-vertexattribip
 
 # Was unskipped, but now has missing results in iOS . Skipping on iOS only.
 media/controls-after-reload.html [ Skip ]
+
+# FIXME: Unskip the following test once we have the fix for <rdar://problem/48322899>.
+editing/selection/ios/select-non-editable-text-using-keyboard.html [ Skip ]
index 5226b87..9eb97d0 100644 (file)
@@ -899,6 +899,15 @@ window.UIHelper = class UIHelper {
             await this.activateAt(menuRect.left + menuRect.width / 2, menuRect.top + menuRect.height / 2);
     }
 
+    static callFunctionAndWaitForEvent(functionToCall, target, eventName)
+    {
+        return new Promise((resolve) => {
+            target.addEventListener(eventName, resolve, { once: true });
+            functionToCall();
+        });
+
+    }
+
     static callFunctionAndWaitForScrollToFinish(functionToCall, ...theArguments)
     {
         return new Promise((resolved) => {
index 47e9813..c939e76 100644 (file)
@@ -1,5 +1,20 @@
 2019-06-25  Daniel Bates  <dabates@apple.com>
 
+        Non-editable text selections should be modifiable with hardware keyboard
+        https://bugs.webkit.org/show_bug.cgi?id=199204
+        <rdar://problem/51651496>
+
+        Reviewed by Wenson Hsieh.
+
+        UIKit changes are need for this to work, including <rdar://problem/48322899>.
+        Note that without these changes, -canPerformAction is never called for _move*
+        selectors.
+
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView canPerformAction:withSender:]):
+
+2019-06-25  Daniel Bates  <dabates@apple.com>
+
         [iOS] Should not process key events in non-editable elements using IME
         https://bugs.webkit.org/show_bug.cgi?id=199122
         <rdar://problem/52006654>
index 3e2fdbf..06cce9c 100644 (file)
@@ -2892,11 +2892,13 @@ WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle);
 
     // These are UIKit IPI selectors. We don't want to forward them to the web view.
     auto editorState = _page->editorState();
-    if (action == @selector(_deleteByWord) || action == @selector(_deleteForwardAndNotify:) || action == @selector(_deleteToEndOfParagraph) || action == @selector(_deleteToStartOfLine)
-        || action == @selector(_moveDown:withHistory:) || action == @selector(_moveLeft:withHistory:) || action == @selector(_moveRight:withHistory:)
+    if (action == @selector(_moveDown:withHistory:) || action == @selector(_moveLeft:withHistory:) || action == @selector(_moveRight:withHistory:)
         || action == @selector(_moveToEndOfDocument:withHistory:) || action == @selector(_moveToEndOfLine:withHistory:) || action == @selector(_moveToEndOfParagraph:withHistory:)
         || action == @selector(_moveToEndOfWord:withHistory:) || action == @selector(_moveToStartOfDocument:withHistory:) || action == @selector(_moveToStartOfLine:withHistory:)
-        || action == @selector(_moveToStartOfParagraph:withHistory:) || action == @selector(_moveToStartOfWord:withHistory:) || action == @selector(_moveUp:withHistory:)
+        || action == @selector(_moveToStartOfParagraph:withHistory:) || action == @selector(_moveToStartOfWord:withHistory:) || action == @selector(_moveUp:withHistory:))
+        return YES;
+
+    if (action == @selector(_deleteByWord) || action == @selector(_deleteForwardAndNotify:) || action == @selector(_deleteToEndOfParagraph) || action == @selector(_deleteToStartOfLine)
         || action == @selector(_transpose))
         return editorState.isContentEditable;