Title: [295289] trunk
Revision
295289
Author
wenson_hs...@apple.com
Date
2022-06-06 08:57:15 -0700 (Mon, 06 Jun 2022)

Log Message

[iOS 15] Unable to type in Google Docs with a hardware keyboard without refocusing editor
https://bugs.webkit.org/show_bug.cgi?id=241308
rdar://90755619

Reviewed by Tim Horton.

After the changes in r277265, we no longer call into `-reloadInputViews` when starting an input
session after programmatically focusing an editable container with `inputmode="none"`, with a
hardware keyboard attached. This is because `-_shouldShowKeyboardForElement:` returns `NO` (due to
inputmode none), so we bail early at this check:

```
if (!shouldShowInputView || information.elementType == WebKit::InputType::None) {
    _page->setIsShowingInputViewForFocusedElement(false);
    return;
}
```

As a result, UIKit never resets UIKeyboardImpl's input delegate, which should now be the
`WKContentView`, and thus never consults whether we require key events (`-requiresKeyEvents`), which
in turn means that `-handleKeyWebEvent:withCompletionHandler:` is never invoked on the content view,
so we never dispatch key events to the page.

In the normal, non-editable key event case, we lazily call `-reloadInputViews` in
`-_handleKeyUIEvent:` (which is called just before keyboard WebEvents start getting dispatched by
UIKit). However, since we're still focusing an editable element in this case, we don't end up going
down this codepath.

When the hardware keyboard is not connected, avoiding the call to `-reloadInputViews` is expected,
since we aren't showing a keyboard anyways due to the fact that the element was programmatically
focused (so the user has no way of typing or dispatching key events, in the first place).

And finally, when the `inputmode` is not none, `_isFocusingElementWithKeyboard` is set to `YES`, so
we begin the input session and call `-reloadInputViews` as normal.

It's only in this `inputmode=none` case with both the hardware keyboard attached and the editable
container being programmatically focused, where we end up in a state where the user can type with a
hardware keyboard, but we haven't informed UIKit that we should receive key events.

We can fix this by consulting a separate `-_shouldShowKeyboardForElementIgnoringInputMode:` instead
which allows us to follow the normal routine for focusing an editable element with `inputmode="none"`
which includes zooming to reveal the focused element if it's on-screen and not hidden, as well as
calling the related delegate methods; the only difference is that we avoid showing the UCB or
software keyboard, by returning `YES` from `-_disableAutomaticKeyboardUI` in this case.

* LayoutTests/fast/forms/ios/keydown-in-hidden-contenteditable-with-inputmode-none-expected.txt: Added.
* LayoutTests/fast/forms/ios/keydown-in-hidden-contenteditable-with-inputmode-none.html: Added.

Add a new layout test to simulate typing in Google Docs with a hardware keyboard (i.e., focus a
hidden contenteditable container with `inputmode="none"`), and verify that we dispatch key events to
the focused editable element.

* Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _elementTypeRequiresAccessoryView:]):
(-[WKContentView _shouldShowKeyboardForElement:]):
(-[WKContentView _shouldShowKeyboardForElementIgnoringInputMode:]):

Split this helper method into two versions (one of which ignores `inputmode=none`). See above for
more details.

(-[WKContentView _elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:]):

Canonical link: https://commits.webkit.org/251335@main

Modified Paths

Added Paths

Diff

Added: trunk/LayoutTests/fast/forms/ios/keydown-in-hidden-contenteditable-with-inputmode-none-expected.txt (0 => 295289)


--- trunk/LayoutTests/fast/forms/ios/keydown-in-hidden-contenteditable-with-inputmode-none-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/ios/keydown-in-hidden-contenteditable-with-inputmode-none-expected.txt	2022-06-06 15:57:15 UTC (rev 295289)
@@ -0,0 +1,13 @@
+This test verifies that key events are dispatched when performing a keypress in a programmatically focused hidden editable container with inputmode='none'. To manually run the test, load the page with a hardware keyboard attached and press the 'a' key.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS Observed keydown with key: "a"
+PASS Observed keypress with key: "a"
+PASS Observed keyup with key: "a"
+PASS observedKeyUp became true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+

Added: trunk/LayoutTests/fast/forms/ios/keydown-in-hidden-contenteditable-with-inputmode-none.html (0 => 295289)


--- trunk/LayoutTests/fast/forms/ios/keydown-in-hidden-contenteditable-with-inputmode-none.html	                        (rev 0)
+++ trunk/LayoutTests/fast/forms/ios/keydown-in-hidden-contenteditable-with-inputmode-none.html	2022-06-06 15:57:15 UTC (rev 295289)
@@ -0,0 +1,44 @@
+<!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[contenteditable] {
+    opacity: 0;
+    position: absolute;
+    left: -1000px;
+}
+</style>
+</head>
+<body>
+<div contenteditable inputmode="none"></div>
+<script>
+jsTestIsAsync = true;
+observedKeyUp = 0;
+
+addEventListener("load", async () => {
+    description("This test verifies that key events are dispatched when performing a keypress in a programmatically focused hidden editable container with inputmode='none'. To manually run the test, load the page with a hardware keyboard attached and press the 'a' key.");
+
+    const logKeyEvent = (event) => testPassed(`Observed ${event.type} with key: "${event.key}"`);
+    const editor = document.querySelector("div[contenteditable]");
+    editor.addEventListener("keydown", logKeyEvent);
+    editor.addEventListener("keypress", logKeyEvent);
+    editor.addEventListener("keyup", (event) => {
+        logKeyEvent(event);
+        observedKeyUp = true;
+    });
+
+    await UIHelper.setHardwareKeyboardAttached(true);
+
+    editor.focus();
+    await UIHelper.keyDown("a");
+    await shouldBecomeEqual("observedKeyUp", "true");
+
+    editor.remove();
+    finishJSTest();
+});
+</script>
+</body>
+</html>
\ No newline at end of file

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (295288 => 295289)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2022-06-06 15:53:17 UTC (rev 295288)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2022-06-06 15:57:15 UTC (rev 295289)
@@ -3465,7 +3465,7 @@
 #endif
 }
 
-- (bool)_elementTypeRequiresAccessoryView:(WebKit::InputType)type
+- (BOOL)_elementTypeRequiresAccessoryView:(WebKit::InputType)type
 {
     switch (type) {
     case WebKit::InputType::None:
@@ -3477,7 +3477,7 @@
     case WebKit::InputType::DateTimeLocal:
     case WebKit::InputType::Month:
     case WebKit::InputType::Time:
-        return false;
+        return NO;
     case WebKit::InputType::Select: {
 #if ENABLE(IOS_FORM_CONTROL_REFRESH)
         if (self._shouldUseContextMenusForFormControls)
@@ -6650,15 +6650,17 @@
     }
 }
 
-- (bool)_shouldShowKeyboardForElement:(const WebKit::FocusedElementInformation&)information
+- (BOOL)_shouldShowKeyboardForElement:(const WebKit::FocusedElementInformation&)information
 {
     if (information.inputMode == WebCore::InputMode::None)
-        return false;
+        return NO;
 
-    if (mayContainSelectableText(information.elementType))
-        return true;
+    return [self _shouldShowKeyboardForElementIgnoringInputMode:information];
+}
 
-    return [self _elementTypeRequiresAccessoryView:information.elementType];
+- (BOOL)_shouldShowKeyboardForElementIgnoringInputMode:(const WebKit::FocusedElementInformation&)information
+{
+    return mayContainSelectableText(information.elementType) || [self _elementTypeRequiresAccessoryView:information.elementType];
 }
 
 static RetainPtr<NSObject <WKFormPeripheral>> createInputPeripheralWithView(WebKit::InputType type, WKContentView *view)
@@ -6738,7 +6740,7 @@
                 if (_isChangingFocus)
                     return YES;
 
-                if (_isFocusingElementWithKeyboard && [UIKeyboard isInHardwareKeyboardMode])
+                if ([self _shouldShowKeyboardForElementIgnoringInputMode:information] && UIKeyboard.isInHardwareKeyboardMode)
                     return YES;
 #endif
             }
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to