Title: [237524] branches/safari-606-branch
Revision
237524
Author
[email protected]
Date
2018-10-28 12:12:15 -0700 (Sun, 28 Oct 2018)

Log Message

Cherry-pick r237135. rdar://problem/45562440

    [iOS] Can't select text after dismissing the keyboard when changing focus
    https://bugs.webkit.org/show_bug.cgi?id=190563
    <rdar://problem/44613559>

    Reviewed by Tim Horton.

    Source/WebKit:

    In r230686, we switched from using UIWKSelectionAssistant to UIWKTextInteractionAssistant for handling selection
    in non-editable content on iOS; as such, when an editable element loses focus, instead of switching from the
    text interaction assistant to the web selection assistant as we've previously done, we now reset our text
    interaction assistant by calling `-[UIWKTextInteractionAssistant setGestureRecognizers]`, which removes all of
    the current text selection gesture recognizers from WKContentView and regenerates them by building up a tree of
    `UITextInteraction`s and adding them to the assistant (see `-[UITextInteractionAssistant
    addGestureRecognizersToView:]`). In particular, `_UITextSelectionForceGesture` is the gesture recognizer used to
    trigger text selection when long pressing.

    After dismissing the keyboard by tapping the "Done" button, the UITextInteractions and gesture recognizers on
    the interaction assistant include:

        <UITextInteraction>
            …
            <UITextIndirectNonEditableInteraction>
                <_UIKeyboardBasedNonEditableTextSelectionInteraction>
                  ↳ "_UIKeyboardTextSelectionGestureForcePress" → <_UITextSelectionForceGesture>

    However, after the keyboard dismisses due to an editable element losing focus, the UITextInteractions on the
    interaction assistant look like this:

        <UITextInteraction>
            …
            <UITextIndirectNonEditableInteraction>

    Subsequently, the lack of a `_UIKeyboardBasedNonEditableTextSelectionInteraction` makes text selection by long
    pressing impossible, since the `_UITextSelectionForceGesture` is never introduced to `WKContentView`. In UIKit,
    `UITextIndirectNonEditableInteraction` only adds `_UIKeyboardBasedNonEditableTextSelectionInteraction` as a
    child if the text input view — in our case, WKContentView — is missing an input delegate (see `-initWithView:`).
    In the case where the Done button is used to dismiss the keyboard, WKContentView loses first responder, and the
    input delegate of WKContentView is cleared out early on, before we call `-stopAssistingKeyboard`:

        -[WKContentView(WKInteraction) setInputDelegate:]
        -[UIKeyboardImpl setDelegate:force:]
        -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
        -[UIResponder _finishResignFirstResponder]
        -[UIResponder resignFirstResponder]
        -[WKContentView(WKInteraction) resignFirstResponderForWebView]
        -[UIKeyboardImpl dismissKeyboard]

    However, in the case where the focused element is blurred, we end up clearing out the delegate in
    `-_stopAssistingNode`, *after* we've already called `-setGestureRecognizers` on the interaction assistant. This
    means UIKit will skip adding `_UIKeyboardBasedNonEditableTextSelectionInteraction` to the text interaction
    assistant.

        -[WKContentView(WKInteraction) setInputDelegate:]
        -[UIKeyboardImpl setDelegate:force:]
        -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
        -[UIResponder(UIResponderInputViewAdditions) reloadInputViews]
        -[WKContentView(WKInteraction) _stopAssistingNode]

    To fix this, we simply reset our `inputDelegate` earlier in `_stopAssistingKeyboard` instead of waiting until
    we reload input views. This ensures that UIKit sets up the text interaction assistant's gestures when changing
    focus in the same way as we would when the keyboard is dismissed via `-resignFirstResponder` (e.g. when pressing
    the Done button).

    Test: editing/selection/ios/select-text-after-changing-focus.html

    * UIProcess/ios/WKContentViewInteraction.mm:
    (-[WKContentView setupInteraction]):
    (-[WKContentView setUpTextSelectionAssistant]):
    (-[WKContentView _startAssistingKeyboard]):
    (-[WKContentView _stopAssistingKeyboard]):
    (-[WKContentView useSelectionAssistantWithGranularity:]): Deleted.

    Additionally rename this to -setUpTextSelectionAssistant and remove the selection granularity argument. This was
    previously used to switch between web and text interaction assistants.

    Tools:

    * DumpRenderTree/ios/UIScriptControllerIOS.mm:
    (WTR::UIScriptController::isShowingKeyboard const):

    Add a new UIScriptController method that returns whether the keyboard is shown. See `ui-helper.js` for more
    details.

    * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
    * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
    (WTR::UIScriptController::isShowingKeyboard const):
    * TestRunnerShared/UIScriptContext/UIScriptController.h:
    * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
    * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:

    Also rename the `isShowingKeyboard` Objective-C property to the more canonical `showingKeyboard`, with
    `isShowingKeyboard` as the getter method.

    (-[TestRunnerWKWebView _invokeShowKeyboardCallbackIfNecessary]):
    (-[TestRunnerWKWebView _invokeHideKeyboardCallbackIfNecessary]):
    * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
    (WTR::UIScriptController::isShowingKeyboard const):

    LayoutTests:

    Add a new layout test to check that the user can make a selection by long pressing after the keyboard is
    dismissed due to changing the focused element.

    * editing/selection/ios/select-text-after-changing-focus-expected.txt: Added.
    * editing/selection/ios/select-text-after-changing-focus.html: Added.
    * resources/ui-helper.js:

    Also tweak the behavior of `UIHelper.waitForKeyboardToHide()`, so that it resolves immediately if the keyboard
    is not shown. This allows us to ensure that tests which use `UIHelper.waitForKeyboardToHide()` are robust in the
    case where they wait for another action to complete (e.g. a simulated tap) prior to registering a keyboard
    hiding callback.

    (window.UIHelper.waitForKeyboardToHide.return.new.Promise):
    (window.UIHelper.waitForKeyboardToHide):

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

Modified Paths

Added Paths

Diff

Modified: branches/safari-606-branch/LayoutTests/ChangeLog (237523 => 237524)


--- branches/safari-606-branch/LayoutTests/ChangeLog	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/LayoutTests/ChangeLog	2018-10-28 19:12:15 UTC (rev 237524)
@@ -1,3 +1,149 @@
+2018-10-28  Babak Shafiei  <[email protected]>
+
+        Cherry-pick r237135. rdar://problem/45562440
+
+    [iOS] Can't select text after dismissing the keyboard when changing focus
+    https://bugs.webkit.org/show_bug.cgi?id=190563
+    <rdar://problem/44613559>
+    
+    Reviewed by Tim Horton.
+    
+    Source/WebKit:
+    
+    In r230686, we switched from using UIWKSelectionAssistant to UIWKTextInteractionAssistant for handling selection
+    in non-editable content on iOS; as such, when an editable element loses focus, instead of switching from the
+    text interaction assistant to the web selection assistant as we've previously done, we now reset our text
+    interaction assistant by calling `-[UIWKTextInteractionAssistant setGestureRecognizers]`, which removes all of
+    the current text selection gesture recognizers from WKContentView and regenerates them by building up a tree of
+    `UITextInteraction`s and adding them to the assistant (see `-[UITextInteractionAssistant
+    addGestureRecognizersToView:]`). In particular, `_UITextSelectionForceGesture` is the gesture recognizer used to
+    trigger text selection when long pressing.
+    
+    After dismissing the keyboard by tapping the "Done" button, the UITextInteractions and gesture recognizers on
+    the interaction assistant include:
+    
+        <UITextInteraction>
+            …
+            <UITextIndirectNonEditableInteraction>
+                <_UIKeyboardBasedNonEditableTextSelectionInteraction>
+                  ↳ "_UIKeyboardTextSelectionGestureForcePress" → <_UITextSelectionForceGesture>
+    
+    However, after the keyboard dismisses due to an editable element losing focus, the UITextInteractions on the
+    interaction assistant look like this:
+    
+        <UITextInteraction>
+            …
+            <UITextIndirectNonEditableInteraction>
+    
+    Subsequently, the lack of a `_UIKeyboardBasedNonEditableTextSelectionInteraction` makes text selection by long
+    pressing impossible, since the `_UITextSelectionForceGesture` is never introduced to `WKContentView`. In UIKit,
+    `UITextIndirectNonEditableInteraction` only adds `_UIKeyboardBasedNonEditableTextSelectionInteraction` as a
+    child if the text input view — in our case, WKContentView — is missing an input delegate (see `-initWithView:`).
+    In the case where the Done button is used to dismiss the keyboard, WKContentView loses first responder, and the
+    input delegate of WKContentView is cleared out early on, before we call `-stopAssistingKeyboard`:
+    
+        -[WKContentView(WKInteraction) setInputDelegate:]
+        -[UIKeyboardImpl setDelegate:force:]
+        -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
+        -[UIResponder _finishResignFirstResponder]
+        -[UIResponder resignFirstResponder]
+        -[WKContentView(WKInteraction) resignFirstResponderForWebView]
+        -[UIKeyboardImpl dismissKeyboard]
+    
+    However, in the case where the focused element is blurred, we end up clearing out the delegate in
+    `-_stopAssistingNode`, *after* we've already called `-setGestureRecognizers` on the interaction assistant. This
+    means UIKit will skip adding `_UIKeyboardBasedNonEditableTextSelectionInteraction` to the text interaction
+    assistant.
+    
+        -[WKContentView(WKInteraction) setInputDelegate:]
+        -[UIKeyboardImpl setDelegate:force:]
+        -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
+        -[UIResponder(UIResponderInputViewAdditions) reloadInputViews]
+        -[WKContentView(WKInteraction) _stopAssistingNode]
+    
+    To fix this, we simply reset our `inputDelegate` earlier in `_stopAssistingKeyboard` instead of waiting until
+    we reload input views. This ensures that UIKit sets up the text interaction assistant's gestures when changing
+    focus in the same way as we would when the keyboard is dismissed via `-resignFirstResponder` (e.g. when pressing
+    the Done button).
+    
+    Test: editing/selection/ios/select-text-after-changing-focus.html
+    
+    * UIProcess/ios/WKContentViewInteraction.mm:
+    (-[WKContentView setupInteraction]):
+    (-[WKContentView setUpTextSelectionAssistant]):
+    (-[WKContentView _startAssistingKeyboard]):
+    (-[WKContentView _stopAssistingKeyboard]):
+    (-[WKContentView useSelectionAssistantWithGranularity:]): Deleted.
+    
+    Additionally rename this to -setUpTextSelectionAssistant and remove the selection granularity argument. This was
+    previously used to switch between web and text interaction assistants.
+    
+    Tools:
+    
+    * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    
+    Add a new UIScriptController method that returns whether the keyboard is shown. See `ui-helper.js` for more
+    details.
+    
+    * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+    * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    * TestRunnerShared/UIScriptContext/UIScriptController.h:
+    * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
+    * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+    
+    Also rename the `isShowingKeyboard` Objective-C property to the more canonical `showingKeyboard`, with
+    `isShowingKeyboard` as the getter method.
+    
+    (-[TestRunnerWKWebView _invokeShowKeyboardCallbackIfNecessary]):
+    (-[TestRunnerWKWebView _invokeHideKeyboardCallbackIfNecessary]):
+    * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    
+    LayoutTests:
+    
+    Add a new layout test to check that the user can make a selection by long pressing after the keyboard is
+    dismissed due to changing the focused element.
+    
+    * editing/selection/ios/select-text-after-changing-focus-expected.txt: Added.
+    * editing/selection/ios/select-text-after-changing-focus.html: Added.
+    * resources/ui-helper.js:
+    
+    Also tweak the behavior of `UIHelper.waitForKeyboardToHide()`, so that it resolves immediately if the keyboard
+    is not shown. This allows us to ensure that tests which use `UIHelper.waitForKeyboardToHide()` are robust in the
+    case where they wait for another action to complete (e.g. a simulated tap) prior to registering a keyboard
+    hiding callback.
+    
+    (window.UIHelper.waitForKeyboardToHide.return.new.Promise):
+    (window.UIHelper.waitForKeyboardToHide):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@237135 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2018-10-15  Wenson Hsieh  <[email protected]>
+
+            [iOS] Can't select text after dismissing the keyboard when changing focus
+            https://bugs.webkit.org/show_bug.cgi?id=190563
+            <rdar://problem/44613559>
+
+            Reviewed by Tim Horton.
+
+            Add a new layout test to check that the user can make a selection by long pressing after the keyboard is
+            dismissed due to changing the focused element.
+
+            * editing/selection/ios/select-text-after-changing-focus-expected.txt: Added.
+            * editing/selection/ios/select-text-after-changing-focus.html: Added.
+            * resources/ui-helper.js:
+
+            Also tweak the behavior of `UIHelper.waitForKeyboardToHide()`, so that it resolves immediately if the keyboard
+            is not shown. This allows us to ensure that tests which use `UIHelper.waitForKeyboardToHide()` are robust in the
+            case where they wait for another action to complete (e.g. a simulated tap) prior to registering a keyboard
+            hiding callback.
+
+            (window.UIHelper.waitForKeyboardToHide.return.new.Promise):
+            (window.UIHelper.waitForKeyboardToHide):
+
 2018-10-23  Kocsen Chung  <[email protected]>
 
         Cherry-pick r237238. rdar://problem/45363879

Added: branches/safari-606-branch/LayoutTests/editing/selection/ios/select-text-after-changing-focus-expected.txt (0 => 237524)


--- branches/safari-606-branch/LayoutTests/editing/selection/ios/select-text-after-changing-focus-expected.txt	                        (rev 0)
+++ branches/safari-606-branch/LayoutTests/editing/selection/ios/select-text-after-changing-focus-expected.txt	2018-10-28 19:12:15 UTC (rev 237524)
@@ -0,0 +1,4 @@
+
+WebKit
+The selected text is: "WebKit"
+This test verifies that it's possible to select text by long pressing after the keyboard is dismissed due to the focused element changing. To manually test, tap the input to show the keyboard then click the button to dismiss the keyboard, and finally try to select the word 'WebKit'.

Added: branches/safari-606-branch/LayoutTests/editing/selection/ios/select-text-after-changing-focus.html (0 => 237524)


--- branches/safari-606-branch/LayoutTests/editing/selection/ios/select-text-after-changing-focus.html	                        (rev 0)
+++ branches/safari-606-branch/LayoutTests/editing/selection/ios/select-text-after-changing-focus.html	2018-10-28 19:12:15 UTC (rev 237524)
@@ -0,0 +1,54 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<head>
+    <script src=""
+    <script src=""
+    <style>
+    body {
+        margin: 0;
+    }
+
+    button, input {
+        width: 100%;
+        height: 100px;
+        font-size: 60px;
+        display: block;
+    }
+
+    #select {
+        font-size: 100px;
+    }
+    </style>
+    <script>
+    if (window.testRunner) {
+        testRunner.dumpAsText();
+        testRunner.waitUntilDone();
+    }
+
+    async function run()
+    {
+        document.addEventListener("selectionchange", () => result.textContent = getSelection().toString());
+        if (!window.testRunner)
+            return;
+
+        await UIHelper.activateAndWaitForInputSessionAt(50, 50);
+        await UIHelper.tapAt(50, 150);
+        await UIHelper.waitForKeyboardToHide();
+        await longPressAtPoint(50, 250);
+        await liftUpAtPoint(50, 250);
+
+        testRunner.notifyDone();
+    }
+    </script>
+</head>
+<body _onload_="run()">
+    <input></input>
+    <button></button>
+    <div id="select">WebKit</div>
+    <pre>The selected text is: "<span id="result"></span>"</pre>
+    <p>This test verifies that it's possible to select text by long pressing after the keyboard is dismissed due to the
+    focused element changing. To manually test, tap the input to show the keyboard then click the button to dismiss the
+    keyboard, and finally try to select the word 'WebKit'.</p>
+</body>
+</html>

Modified: branches/safari-606-branch/LayoutTests/resources/ui-helper.js (237523 => 237524)


--- branches/safari-606-branch/LayoutTests/resources/ui-helper.js	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/LayoutTests/resources/ui-helper.js	2018-10-28 19:12:15 UTC (rev 237524)
@@ -116,7 +116,10 @@
         return new Promise(resolve => {
             testRunner.runUIScript(`
                 (function() {
-                    uiController.didHideKeyboardCallback = () => uiController.uiScriptComplete();
+                    if (uiController.isShowingKeyboard)
+                        uiController.didHideKeyboardCallback = () => uiController.uiScriptComplete();
+                    else
+                        uiController.uiScriptComplete();
                 })()`, resolve);
         });
     }

Modified: branches/safari-606-branch/Source/WebKit/ChangeLog (237523 => 237524)


--- branches/safari-606-branch/Source/WebKit/ChangeLog	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Source/WebKit/ChangeLog	2018-10-28 19:12:15 UTC (rev 237524)
@@ -1,3 +1,202 @@
+2018-10-28  Babak Shafiei  <[email protected]>
+
+        Cherry-pick r237135. rdar://problem/45562440
+
+    [iOS] Can't select text after dismissing the keyboard when changing focus
+    https://bugs.webkit.org/show_bug.cgi?id=190563
+    <rdar://problem/44613559>
+    
+    Reviewed by Tim Horton.
+    
+    Source/WebKit:
+    
+    In r230686, we switched from using UIWKSelectionAssistant to UIWKTextInteractionAssistant for handling selection
+    in non-editable content on iOS; as such, when an editable element loses focus, instead of switching from the
+    text interaction assistant to the web selection assistant as we've previously done, we now reset our text
+    interaction assistant by calling `-[UIWKTextInteractionAssistant setGestureRecognizers]`, which removes all of
+    the current text selection gesture recognizers from WKContentView and regenerates them by building up a tree of
+    `UITextInteraction`s and adding them to the assistant (see `-[UITextInteractionAssistant
+    addGestureRecognizersToView:]`). In particular, `_UITextSelectionForceGesture` is the gesture recognizer used to
+    trigger text selection when long pressing.
+    
+    After dismissing the keyboard by tapping the "Done" button, the UITextInteractions and gesture recognizers on
+    the interaction assistant include:
+    
+        <UITextInteraction>
+            …
+            <UITextIndirectNonEditableInteraction>
+                <_UIKeyboardBasedNonEditableTextSelectionInteraction>
+                  ↳ "_UIKeyboardTextSelectionGestureForcePress" → <_UITextSelectionForceGesture>
+    
+    However, after the keyboard dismisses due to an editable element losing focus, the UITextInteractions on the
+    interaction assistant look like this:
+    
+        <UITextInteraction>
+            …
+            <UITextIndirectNonEditableInteraction>
+    
+    Subsequently, the lack of a `_UIKeyboardBasedNonEditableTextSelectionInteraction` makes text selection by long
+    pressing impossible, since the `_UITextSelectionForceGesture` is never introduced to `WKContentView`. In UIKit,
+    `UITextIndirectNonEditableInteraction` only adds `_UIKeyboardBasedNonEditableTextSelectionInteraction` as a
+    child if the text input view — in our case, WKContentView — is missing an input delegate (see `-initWithView:`).
+    In the case where the Done button is used to dismiss the keyboard, WKContentView loses first responder, and the
+    input delegate of WKContentView is cleared out early on, before we call `-stopAssistingKeyboard`:
+    
+        -[WKContentView(WKInteraction) setInputDelegate:]
+        -[UIKeyboardImpl setDelegate:force:]
+        -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
+        -[UIResponder _finishResignFirstResponder]
+        -[UIResponder resignFirstResponder]
+        -[WKContentView(WKInteraction) resignFirstResponderForWebView]
+        -[UIKeyboardImpl dismissKeyboard]
+    
+    However, in the case where the focused element is blurred, we end up clearing out the delegate in
+    `-_stopAssistingNode`, *after* we've already called `-setGestureRecognizers` on the interaction assistant. This
+    means UIKit will skip adding `_UIKeyboardBasedNonEditableTextSelectionInteraction` to the text interaction
+    assistant.
+    
+        -[WKContentView(WKInteraction) setInputDelegate:]
+        -[UIKeyboardImpl setDelegate:force:]
+        -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
+        -[UIResponder(UIResponderInputViewAdditions) reloadInputViews]
+        -[WKContentView(WKInteraction) _stopAssistingNode]
+    
+    To fix this, we simply reset our `inputDelegate` earlier in `_stopAssistingKeyboard` instead of waiting until
+    we reload input views. This ensures that UIKit sets up the text interaction assistant's gestures when changing
+    focus in the same way as we would when the keyboard is dismissed via `-resignFirstResponder` (e.g. when pressing
+    the Done button).
+    
+    Test: editing/selection/ios/select-text-after-changing-focus.html
+    
+    * UIProcess/ios/WKContentViewInteraction.mm:
+    (-[WKContentView setupInteraction]):
+    (-[WKContentView setUpTextSelectionAssistant]):
+    (-[WKContentView _startAssistingKeyboard]):
+    (-[WKContentView _stopAssistingKeyboard]):
+    (-[WKContentView useSelectionAssistantWithGranularity:]): Deleted.
+    
+    Additionally rename this to -setUpTextSelectionAssistant and remove the selection granularity argument. This was
+    previously used to switch between web and text interaction assistants.
+    
+    Tools:
+    
+    * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    
+    Add a new UIScriptController method that returns whether the keyboard is shown. See `ui-helper.js` for more
+    details.
+    
+    * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+    * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    * TestRunnerShared/UIScriptContext/UIScriptController.h:
+    * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
+    * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+    
+    Also rename the `isShowingKeyboard` Objective-C property to the more canonical `showingKeyboard`, with
+    `isShowingKeyboard` as the getter method.
+    
+    (-[TestRunnerWKWebView _invokeShowKeyboardCallbackIfNecessary]):
+    (-[TestRunnerWKWebView _invokeHideKeyboardCallbackIfNecessary]):
+    * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    
+    LayoutTests:
+    
+    Add a new layout test to check that the user can make a selection by long pressing after the keyboard is
+    dismissed due to changing the focused element.
+    
+    * editing/selection/ios/select-text-after-changing-focus-expected.txt: Added.
+    * editing/selection/ios/select-text-after-changing-focus.html: Added.
+    * resources/ui-helper.js:
+    
+    Also tweak the behavior of `UIHelper.waitForKeyboardToHide()`, so that it resolves immediately if the keyboard
+    is not shown. This allows us to ensure that tests which use `UIHelper.waitForKeyboardToHide()` are robust in the
+    case where they wait for another action to complete (e.g. a simulated tap) prior to registering a keyboard
+    hiding callback.
+    
+    (window.UIHelper.waitForKeyboardToHide.return.new.Promise):
+    (window.UIHelper.waitForKeyboardToHide):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@237135 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2018-10-15  Wenson Hsieh  <[email protected]>
+
+            [iOS] Can't select text after dismissing the keyboard when changing focus
+            https://bugs.webkit.org/show_bug.cgi?id=190563
+            <rdar://problem/44613559>
+
+            Reviewed by Tim Horton.
+
+            In r230686, we switched from using UIWKSelectionAssistant to UIWKTextInteractionAssistant for handling selection
+            in non-editable content on iOS; as such, when an editable element loses focus, instead of switching from the
+            text interaction assistant to the web selection assistant as we've previously done, we now reset our text
+            interaction assistant by calling `-[UIWKTextInteractionAssistant setGestureRecognizers]`, which removes all of
+            the current text selection gesture recognizers from WKContentView and regenerates them by building up a tree of
+            `UITextInteraction`s and adding them to the assistant (see `-[UITextInteractionAssistant
+            addGestureRecognizersToView:]`). In particular, `_UITextSelectionForceGesture` is the gesture recognizer used to
+            trigger text selection when long pressing.
+
+            After dismissing the keyboard by tapping the "Done" button, the UITextInteractions and gesture recognizers on
+            the interaction assistant include:
+
+                <UITextInteraction>
+                    …
+                    <UITextIndirectNonEditableInteraction>
+                        <_UIKeyboardBasedNonEditableTextSelectionInteraction>
+                          ↳ "_UIKeyboardTextSelectionGestureForcePress" → <_UITextSelectionForceGesture>
+
+            However, after the keyboard dismisses due to an editable element losing focus, the UITextInteractions on the
+            interaction assistant look like this:
+
+                <UITextInteraction>
+                    …
+                    <UITextIndirectNonEditableInteraction>
+
+            Subsequently, the lack of a `_UIKeyboardBasedNonEditableTextSelectionInteraction` makes text selection by long
+            pressing impossible, since the `_UITextSelectionForceGesture` is never introduced to `WKContentView`. In UIKit,
+            `UITextIndirectNonEditableInteraction` only adds `_UIKeyboardBasedNonEditableTextSelectionInteraction` as a
+            child if the text input view — in our case, WKContentView — is missing an input delegate (see `-initWithView:`).
+            In the case where the Done button is used to dismiss the keyboard, WKContentView loses first responder, and the
+            input delegate of WKContentView is cleared out early on, before we call `-stopAssistingKeyboard`:
+
+                -[WKContentView(WKInteraction) setInputDelegate:]
+                -[UIKeyboardImpl setDelegate:force:]
+                -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
+                -[UIResponder _finishResignFirstResponder]
+                -[UIResponder resignFirstResponder]
+                -[WKContentView(WKInteraction) resignFirstResponderForWebView]
+                -[UIKeyboardImpl dismissKeyboard]
+
+            However, in the case where the focused element is blurred, we end up clearing out the delegate in
+            `-_stopAssistingNode`, *after* we've already called `-setGestureRecognizers` on the interaction assistant. This
+            means UIKit will skip adding `_UIKeyboardBasedNonEditableTextSelectionInteraction` to the text interaction
+            assistant.
+
+                -[WKContentView(WKInteraction) setInputDelegate:]
+                -[UIKeyboardImpl setDelegate:force:]
+                -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
+                -[UIResponder(UIResponderInputViewAdditions) reloadInputViews]
+                -[WKContentView(WKInteraction) _stopAssistingNode]
+
+            To fix this, we simply reset our `inputDelegate` earlier in `_stopAssistingKeyboard` instead of waiting until
+            we reload input views. This ensures that UIKit sets up the text interaction assistant's gestures when changing
+            focus in the same way as we would when the keyboard is dismissed via `-resignFirstResponder` (e.g. when pressing
+            the Done button).
+
+            Test: editing/selection/ios/select-text-after-changing-focus.html
+
+            * UIProcess/ios/WKContentViewInteraction.mm:
+            (-[WKContentView setupInteraction]):
+            (-[WKContentView setUpTextSelectionAssistant]):
+            (-[WKContentView _startAssistingKeyboard]):
+            (-[WKContentView _stopAssistingKeyboard]):
+            (-[WKContentView useSelectionAssistantWithGranularity:]): Deleted.
+
+            Additionally rename this to -setUpTextSelectionAssistant and remove the selection granularity argument. This was
+            previously used to switch between web and text interaction assistants.
+
 2018-10-24  Kocsen Chung  <[email protected]>
 
         Cherry-pick r236227. rdar://problem/45491949

Modified: branches/safari-606-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (237523 => 237524)


--- branches/safari-606-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2018-10-28 19:12:15 UTC (rev 237524)
@@ -673,7 +673,7 @@
     _showingTextStyleOptions = NO;
 
     // FIXME: This should be called when we get notified that loading has completed.
-    [self useSelectionAssistantWithGranularity:_webView._selectionGranularity];
+    [self setUpTextSelectionAssistant];
     
     _actionSheetAssistant = adoptNS([[WKActionSheetAssistant alloc] initWithView:self]);
     [_actionSheetAssistant setDelegate:self];
@@ -1946,14 +1946,12 @@
     _page->handleTap(location, _layerTreeTransactionIdAtLastTouchStart);
 }
 
-- (void)useSelectionAssistantWithGranularity:(WKSelectionGranularity)selectionGranularity
+- (void)setUpTextSelectionAssistant
 {
-    _webSelectionAssistant = nil;
-
     if (!_textSelectionAssistant)
         _textSelectionAssistant = adoptNS([[UIWKTextInteractionAssistant alloc] initWithView:self]);
     else {
-        // Reset the gesture recognizers in case editibility has changed.
+        // Reset the gesture recognizers in case editability has changed.
         [_textSelectionAssistant setGestureRecognizers];
     }
 }
@@ -3980,7 +3978,7 @@
 
 - (void)_startAssistingKeyboard
 {
-    [self useSelectionAssistantWithGranularity:WKSelectionGranularityCharacter];
+    [self setUpTextSelectionAssistant];
     
     if (self.isFirstResponder && !self.suppressAssistantSelectionView)
         [_textSelectionAssistant activateSelection];
@@ -3992,7 +3990,8 @@
 
 - (void)_stopAssistingKeyboard
 {
-    [self useSelectionAssistantWithGranularity:_webView._selectionGranularity];
+    self.inputDelegate = nil;
+    [self setUpTextSelectionAssistant];
     
     [_textSelectionAssistant deactivateSelection];
 }

Modified: branches/safari-606-branch/Tools/ChangeLog (237523 => 237524)


--- branches/safari-606-branch/Tools/ChangeLog	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Tools/ChangeLog	2018-10-28 19:12:15 UTC (rev 237524)
@@ -1,3 +1,155 @@
+2018-10-28  Babak Shafiei  <[email protected]>
+
+        Cherry-pick r237135. rdar://problem/45562440
+
+    [iOS] Can't select text after dismissing the keyboard when changing focus
+    https://bugs.webkit.org/show_bug.cgi?id=190563
+    <rdar://problem/44613559>
+    
+    Reviewed by Tim Horton.
+    
+    Source/WebKit:
+    
+    In r230686, we switched from using UIWKSelectionAssistant to UIWKTextInteractionAssistant for handling selection
+    in non-editable content on iOS; as such, when an editable element loses focus, instead of switching from the
+    text interaction assistant to the web selection assistant as we've previously done, we now reset our text
+    interaction assistant by calling `-[UIWKTextInteractionAssistant setGestureRecognizers]`, which removes all of
+    the current text selection gesture recognizers from WKContentView and regenerates them by building up a tree of
+    `UITextInteraction`s and adding them to the assistant (see `-[UITextInteractionAssistant
+    addGestureRecognizersToView:]`). In particular, `_UITextSelectionForceGesture` is the gesture recognizer used to
+    trigger text selection when long pressing.
+    
+    After dismissing the keyboard by tapping the "Done" button, the UITextInteractions and gesture recognizers on
+    the interaction assistant include:
+    
+        <UITextInteraction>
+            …
+            <UITextIndirectNonEditableInteraction>
+                <_UIKeyboardBasedNonEditableTextSelectionInteraction>
+                  ↳ "_UIKeyboardTextSelectionGestureForcePress" → <_UITextSelectionForceGesture>
+    
+    However, after the keyboard dismisses due to an editable element losing focus, the UITextInteractions on the
+    interaction assistant look like this:
+    
+        <UITextInteraction>
+            …
+            <UITextIndirectNonEditableInteraction>
+    
+    Subsequently, the lack of a `_UIKeyboardBasedNonEditableTextSelectionInteraction` makes text selection by long
+    pressing impossible, since the `_UITextSelectionForceGesture` is never introduced to `WKContentView`. In UIKit,
+    `UITextIndirectNonEditableInteraction` only adds `_UIKeyboardBasedNonEditableTextSelectionInteraction` as a
+    child if the text input view — in our case, WKContentView — is missing an input delegate (see `-initWithView:`).
+    In the case where the Done button is used to dismiss the keyboard, WKContentView loses first responder, and the
+    input delegate of WKContentView is cleared out early on, before we call `-stopAssistingKeyboard`:
+    
+        -[WKContentView(WKInteraction) setInputDelegate:]
+        -[UIKeyboardImpl setDelegate:force:]
+        -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
+        -[UIResponder _finishResignFirstResponder]
+        -[UIResponder resignFirstResponder]
+        -[WKContentView(WKInteraction) resignFirstResponderForWebView]
+        -[UIKeyboardImpl dismissKeyboard]
+    
+    However, in the case where the focused element is blurred, we end up clearing out the delegate in
+    `-_stopAssistingNode`, *after* we've already called `-setGestureRecognizers` on the interaction assistant. This
+    means UIKit will skip adding `_UIKeyboardBasedNonEditableTextSelectionInteraction` to the text interaction
+    assistant.
+    
+        -[WKContentView(WKInteraction) setInputDelegate:]
+        -[UIKeyboardImpl setDelegate:force:]
+        -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:]
+        -[UIResponder(UIResponderInputViewAdditions) reloadInputViews]
+        -[WKContentView(WKInteraction) _stopAssistingNode]
+    
+    To fix this, we simply reset our `inputDelegate` earlier in `_stopAssistingKeyboard` instead of waiting until
+    we reload input views. This ensures that UIKit sets up the text interaction assistant's gestures when changing
+    focus in the same way as we would when the keyboard is dismissed via `-resignFirstResponder` (e.g. when pressing
+    the Done button).
+    
+    Test: editing/selection/ios/select-text-after-changing-focus.html
+    
+    * UIProcess/ios/WKContentViewInteraction.mm:
+    (-[WKContentView setupInteraction]):
+    (-[WKContentView setUpTextSelectionAssistant]):
+    (-[WKContentView _startAssistingKeyboard]):
+    (-[WKContentView _stopAssistingKeyboard]):
+    (-[WKContentView useSelectionAssistantWithGranularity:]): Deleted.
+    
+    Additionally rename this to -setUpTextSelectionAssistant and remove the selection granularity argument. This was
+    previously used to switch between web and text interaction assistants.
+    
+    Tools:
+    
+    * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    
+    Add a new UIScriptController method that returns whether the keyboard is shown. See `ui-helper.js` for more
+    details.
+    
+    * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+    * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    * TestRunnerShared/UIScriptContext/UIScriptController.h:
+    * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
+    * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+    
+    Also rename the `isShowingKeyboard` Objective-C property to the more canonical `showingKeyboard`, with
+    `isShowingKeyboard` as the getter method.
+    
+    (-[TestRunnerWKWebView _invokeShowKeyboardCallbackIfNecessary]):
+    (-[TestRunnerWKWebView _invokeHideKeyboardCallbackIfNecessary]):
+    * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+    (WTR::UIScriptController::isShowingKeyboard const):
+    
+    LayoutTests:
+    
+    Add a new layout test to check that the user can make a selection by long pressing after the keyboard is
+    dismissed due to changing the focused element.
+    
+    * editing/selection/ios/select-text-after-changing-focus-expected.txt: Added.
+    * editing/selection/ios/select-text-after-changing-focus.html: Added.
+    * resources/ui-helper.js:
+    
+    Also tweak the behavior of `UIHelper.waitForKeyboardToHide()`, so that it resolves immediately if the keyboard
+    is not shown. This allows us to ensure that tests which use `UIHelper.waitForKeyboardToHide()` are robust in the
+    case where they wait for another action to complete (e.g. a simulated tap) prior to registering a keyboard
+    hiding callback.
+    
+    (window.UIHelper.waitForKeyboardToHide.return.new.Promise):
+    (window.UIHelper.waitForKeyboardToHide):
+    
+    
+    git-svn-id: https://svn.webkit.org/repository/webkit/trunk@237135 268f45cc-cd09-0410-ab3c-d52691b4dbfc
+
+    2018-10-15  Wenson Hsieh  <[email protected]>
+
+            [iOS] Can't select text after dismissing the keyboard when changing focus
+            https://bugs.webkit.org/show_bug.cgi?id=190563
+            <rdar://problem/44613559>
+
+            Reviewed by Tim Horton.
+
+            * DumpRenderTree/ios/UIScriptControllerIOS.mm:
+            (WTR::UIScriptController::isShowingKeyboard const):
+
+            Add a new UIScriptController method that returns whether the keyboard is shown. See `ui-helper.js` for more
+            details.
+
+            * TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl:
+            * TestRunnerShared/UIScriptContext/UIScriptController.cpp:
+            (WTR::UIScriptController::isShowingKeyboard const):
+            * TestRunnerShared/UIScriptContext/UIScriptController.h:
+            * WebKitTestRunner/cocoa/TestRunnerWKWebView.h:
+            * WebKitTestRunner/cocoa/TestRunnerWKWebView.mm:
+
+            Also rename the `isShowingKeyboard` Objective-C property to the more canonical `showingKeyboard`, with
+            `isShowingKeyboard` as the getter method.
+
+            (-[TestRunnerWKWebView _invokeShowKeyboardCallbackIfNecessary]):
+            (-[TestRunnerWKWebView _invokeHideKeyboardCallbackIfNecessary]):
+            * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+            (WTR::UIScriptController::isShowingKeyboard const):
+
 2018-09-06  Babak Shafiei  <[email protected]>
 
         Cherry-pick r235251. rdar://problem/44209840

Modified: branches/safari-606-branch/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm (237523 => 237524)


--- branches/safari-606-branch/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Tools/DumpRenderTree/ios/UIScriptControllerIOS.mm	2018-10-28 19:12:15 UTC (rev 237524)
@@ -230,6 +230,11 @@
 {
 }
 
+bool UIScriptController::isShowingKeyboard() const
+{
+    return false;
+}
+
 double UIScriptController::minimumZoomScale() const
 {
     return gWebScrollView.minimumZoomScale;

Modified: branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl (237523 => 237524)


--- branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/Bindings/UIScriptController.idl	2018-10-28 19:12:15 UTC (rev 237524)
@@ -206,6 +206,7 @@
     // These callbacks also work for the form accessory views.
     attribute object didShowKeyboardCallback;
     attribute object didHideKeyboardCallback;
+    readonly attribute boolean isShowingKeyboard;
 
     attribute object willBeginZoomingCallback;
     attribute object didEndZoomingCallback;

Modified: branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp (237523 => 237524)


--- branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/UIScriptController.cpp	2018-10-28 19:12:15 UTC (rev 237524)
@@ -345,6 +345,11 @@
 {
 }
 
+bool UIScriptController::isShowingKeyboard() const
+{
+    return false;
+}
+
 double UIScriptController::zoomScale() const
 {
     return 1;

Modified: branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h (237523 => 237524)


--- branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Tools/TestRunnerShared/UIScriptContext/UIScriptController.h	2018-10-28 19:12:15 UTC (rev 237524)
@@ -138,6 +138,8 @@
     void setDidHideKeyboardCallback(JSValueRef);
     JSValueRef didHideKeyboardCallback() const;
 
+    bool isShowingKeyboard() const;
+
     void setDidEndScrollingCallback(JSValueRef);
     JSValueRef didEndScrollingCallback() const;
 

Modified: branches/safari-606-branch/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h (237523 => 237524)


--- branches/safari-606-branch/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.h	2018-10-28 19:12:15 UTC (rev 237524)
@@ -49,6 +49,7 @@
 
 @property (nonatomic, assign) UIEdgeInsets overrideSafeAreaInsets;
 
+@property (nonatomic, readonly, getter=isShowingKeyboard) BOOL showingKeyboard;
 @property (nonatomic, assign) BOOL usesSafariLikeRotation;
 
 #endif

Modified: branches/safari-606-branch/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm (237523 => 237524)


--- branches/safari-606-branch/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Tools/WebKitTestRunner/cocoa/TestRunnerWKWebView.mm	2018-10-28 19:12:15 UTC (rev 237524)
@@ -53,7 +53,7 @@
 
 @property (nonatomic, copy) void (^zoomToScaleCompletionHandler)(void);
 @property (nonatomic, copy) void (^retrieveSpeakSelectionContentCompletionHandler)(void);
-@property (nonatomic) BOOL isShowingKeyboard;
+@property (nonatomic, getter=isShowingKeyboard, setter=setIsShowingKeyboard:) BOOL showingKeyboard;
 
 @end
 
@@ -142,10 +142,10 @@
 
 - (void)_invokeShowKeyboardCallbackIfNecessary
 {
-    if (self.isShowingKeyboard)
+    if (self.showingKeyboard)
         return;
 
-    self.isShowingKeyboard = YES;
+    self.showingKeyboard = YES;
     if (self.didShowKeyboardCallback)
         self.didShowKeyboardCallback();
 }
@@ -152,10 +152,10 @@
 
 - (void)_invokeHideKeyboardCallbackIfNecessary
 {
-    if (!self.isShowingKeyboard)
+    if (!self.showingKeyboard)
         return;
 
-    self.isShowingKeyboard = NO;
+    self.showingKeyboard = NO;
     if (self.didHideKeyboardCallback)
         self.didHideKeyboardCallback();
 }

Modified: branches/safari-606-branch/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (237523 => 237524)


--- branches/safari-606-branch/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2018-10-28 18:25:54 UTC (rev 237523)
+++ branches/safari-606-branch/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm	2018-10-28 19:12:15 UTC (rev 237524)
@@ -508,6 +508,11 @@
     [webView keyboardAccessoryBarPrevious];
 }
 
+bool UIScriptController::isShowingKeyboard() const
+{
+    return TestController::singleton().mainWebView()->platformView().showingKeyboard;
+}
+
 void UIScriptController::applyAutocorrection(JSStringRef newString, JSStringRef oldString, JSValueRef callback)
 {
     unsigned callbackID = m_context->prepareForAsyncTask(callback, CallbackTypeNonPersistent);
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to