Title: [242173] trunk
Revision
242173
Author
wenson_hs...@apple.com
Date
2019-02-27 15:54:02 -0800 (Wed, 27 Feb 2019)

Log Message

[iOS] Web pages shouldn't be able to present a keyboard after the web view resigns first responder
https://bugs.webkit.org/show_bug.cgi?id=195118
<rdar://problem/43411940>

Reviewed by Tim Horton.

Source/WebKit:

It's currently possible for websites to redirect focus into an editable element on the page by programmatically
requesting focus within the "blur" event handler. This is because our current heuristics:

(1) Allow programmatic focus to show the keyboard when an element is already focused; this is meant to handle
    the case where the page moves focus between different elements on the page.
(2) And also allow programmatic focus to show the keyboard when changing activity state; this is meant to handle
    the case where a focused element should show the keyboard when first responder is restored on a web view
    (e.g. in the case where a modal view controller is dismissed, and the web view regains first responder once
    again).

In both of these scenarios, we actually only want the keyboard to appear if the web view itself is either
becoming the first responder, or is already the first responder. Importantly, when blurring the focused element
by calling -[WKWebViewe resignFirstResponder] (as is the case when dismissing the keyboard by tapping 'Done' or
focusing other platform UI), we don't want to allow the page to show the keyboard due to element focus.

To fix this issue, we enforce that we're either becoming the first responder or already are the first responder
before showing the keyboard due to activity state change or focused element change.

Test: fast/events/ios/do-not-show-keyboard-when-focusing-after-blur.html

* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setupInteraction]):
(-[WKContentView textInputTraits]):

Quick drive-by tweak: rename _isBlurringFocusedNode to _isBlurringFocusedElement, to match the rest of the
terminology used in WebKit.

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

Make our heuristics for determining whether to show the keyboard a tiny bit easier to follow, by moving the
logic into a lambda function and using early returns. See above for more details.

(-[WKContentView _elementDidBlur]):
(-[WKContentView focusedFormControllerDidUpdateSuggestions:]):

LayoutTests:

Add a test to verify that after resigning first responder (e.g. tapping 'Done' on the keyboard, or focusing a
native input field elsewhere in the app), the page cannot force the keyboard to appear by focusing an input
field.

* fast/events/ios/do-not-show-keyboard-when-focusing-after-blur-expected.txt: Added.
* fast/events/ios/do-not-show-keyboard-when-focusing-after-blur.html: Added.

Modified Paths

Added Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (242172 => 242173)


--- trunk/LayoutTests/ChangeLog	2019-02-27 23:52:01 UTC (rev 242172)
+++ trunk/LayoutTests/ChangeLog	2019-02-27 23:54:02 UTC (rev 242173)
@@ -1,3 +1,18 @@
+2019-02-27  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] Web pages shouldn't be able to present a keyboard after the web view resigns first responder
+        https://bugs.webkit.org/show_bug.cgi?id=195118
+        <rdar://problem/43411940>
+
+        Reviewed by Tim Horton.
+
+        Add a test to verify that after resigning first responder (e.g. tapping 'Done' on the keyboard, or focusing a
+        native input field elsewhere in the app), the page cannot force the keyboard to appear by focusing an input
+        field.
+
+        * fast/events/ios/do-not-show-keyboard-when-focusing-after-blur-expected.txt: Added.
+        * fast/events/ios/do-not-show-keyboard-when-focusing-after-blur.html: Added.
+
 2019-02-27  John Wilander  <wilan...@apple.com>
 
         Adopt WebCore::RegistrableDomain in WebCore::ResourceLoadStatistics and WebKit::NetworkProcessProxy

Added: trunk/LayoutTests/fast/events/ios/do-not-show-keyboard-when-focusing-after-blur-expected.txt (0 => 242173)


--- trunk/LayoutTests/fast/events/ios/do-not-show-keyboard-when-focusing-after-blur-expected.txt	                        (rev 0)
+++ trunk/LayoutTests/fast/events/ios/do-not-show-keyboard-when-focusing-after-blur-expected.txt	2019-02-27 23:54:02 UTC (rev 242173)
@@ -0,0 +1,10 @@
+PASS inputBounds.top is >= screenHeight
+PASS inputBounds.height is 0
+PASS document.activeElement is field
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
+Verifies that an input field cannot force the web view to become first responder after the web view resigns first responder using platform API. To manually test, focus the text field and hit the 'Done' button on the keyboard; the keyboard should not pop back up.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".

Added: trunk/LayoutTests/fast/events/ios/do-not-show-keyboard-when-focusing-after-blur.html (0 => 242173)


--- trunk/LayoutTests/fast/events/ios/do-not-show-keyboard-when-focusing-after-blur.html	                        (rev 0)
+++ trunk/LayoutTests/fast/events/ios/do-not-show-keyboard-when-focusing-after-blur.html	2019-02-27 23:54:02 UTC (rev 242173)
@@ -0,0 +1,50 @@
+<html> <!-- webkit-test-runner [ shouldIgnoreMetaViewport=true ] -->
+<head>
+    <meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no">
+    <script src=""
+    <script src=""
+    <style>
+    input {
+        font-size: 18px;
+        border: black 2px solid;
+        border-radius: 4px;
+        padding: 1em;
+        width: 100%;
+    }
+
+    body {
+        margin: 0;
+    }
+    </style>
+</head>
+<body>
+<div><input type="text"></div>
+<div id="description"></div>
+</body>
+<script>
+jsTestIsAsync = true;
+
+field = document.querySelector("input");
+field.addEventListener("blur", () => {
+    field.focus();
+    setTimeout(() => field.focus(), 0);
+});
+
+addEventListener("load", async () => {
+    description("Verifies that an input field cannot force the web view to become first responder after the web view resigns first responder using platform API. To manually test, focus the text field and hit the 'Done' button on the keyboard; the keyboard should not pop back up.");
+
+    await UIHelper.activateAndWaitForInputSessionAt(160, 30);
+    await UIHelper.resignFirstResponder();
+    await UIHelper.waitForKeyboardToHide();
+    inputBounds = await UIHelper.inputViewBounds();
+    screenHeight = screen.height;
+
+    // Verify that the keyboard is positioned off-screen.
+    shouldBeGreaterThanOrEqual("inputBounds.top", "screenHeight");
+    shouldBe("inputBounds.height", "0");
+    shouldBe("document.activeElement", "field");
+    finishJSTest();
+});
+</script>
+</html>
+

Modified: trunk/Source/WebKit/ChangeLog (242172 => 242173)


--- trunk/Source/WebKit/ChangeLog	2019-02-27 23:52:01 UTC (rev 242172)
+++ trunk/Source/WebKit/ChangeLog	2019-02-27 23:54:02 UTC (rev 242173)
@@ -1,3 +1,47 @@
+2019-02-27  Wenson Hsieh  <wenson_hs...@apple.com>
+
+        [iOS] Web pages shouldn't be able to present a keyboard after the web view resigns first responder
+        https://bugs.webkit.org/show_bug.cgi?id=195118
+        <rdar://problem/43411940>
+
+        Reviewed by Tim Horton.
+
+        It's currently possible for websites to redirect focus into an editable element on the page by programmatically
+        requesting focus within the "blur" event handler. This is because our current heuristics:
+
+        (1) Allow programmatic focus to show the keyboard when an element is already focused; this is meant to handle
+            the case where the page moves focus between different elements on the page.
+        (2) And also allow programmatic focus to show the keyboard when changing activity state; this is meant to handle
+            the case where a focused element should show the keyboard when first responder is restored on a web view
+            (e.g. in the case where a modal view controller is dismissed, and the web view regains first responder once
+            again).
+
+        In both of these scenarios, we actually only want the keyboard to appear if the web view itself is either
+        becoming the first responder, or is already the first responder. Importantly, when blurring the focused element
+        by calling -[WKWebViewe resignFirstResponder] (as is the case when dismissing the keyboard by tapping 'Done' or
+        focusing other platform UI), we don't want to allow the page to show the keyboard due to element focus.
+
+        To fix this issue, we enforce that we're either becoming the first responder or already are the first responder
+        before showing the keyboard due to activity state change or focused element change.
+
+        Test: fast/events/ios/do-not-show-keyboard-when-focusing-after-blur.html
+
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setupInteraction]):
+        (-[WKContentView textInputTraits]):
+
+        Quick drive-by tweak: rename _isBlurringFocusedNode to _isBlurringFocusedElement, to match the rest of the
+        terminology used in WebKit.
+
+        (-[WKContentView _elementDidFocus:userIsInteracting:blurPreviousNode:changingActivityState:userObject:]):
+
+        Make our heuristics for determining whether to show the keyboard a tiny bit easier to follow, by moving the
+        logic into a lambda function and using early returns. See above for more details.
+
+        (-[WKContentView _elementDidBlur]):
+        (-[WKContentView focusedFormControllerDidUpdateSuggestions:]):
+
 2019-02-27  John Wilander  <wilan...@apple.com>
 
         Adopt WebCore::RegistrableDomain in WebCore::ResourceLoadStatistics and WebKit::NetworkProcessProxy

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (242172 => 242173)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2019-02-27 23:52:01 UTC (rev 242172)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2019-02-27 23:54:02 UTC (rev 242173)
@@ -313,7 +313,7 @@
     BOOL _resigningFirstResponder;
     BOOL _needsDeferredEndScrollingSelectionUpdate;
     BOOL _isChangingFocus;
-    BOOL _isBlurringFocusedNode;
+    BOOL _isBlurringFocusedElement;
 
     BOOL _focusRequiresStrongPasswordAssistance;
 

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (242172 => 242173)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2019-02-27 23:52:01 UTC (rev 242172)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2019-02-27 23:54:02 UTC (rev 242173)
@@ -761,7 +761,7 @@
     _showDebugTapHighlightsForFastClicking = [[NSUserDefaults standardUserDefaults] boolForKey:@"WebKitShowFastClickDebugTapHighlights"];
     _needsDeferredEndScrollingSelectionUpdate = NO;
     _isChangingFocus = NO;
-    _isBlurringFocusedNode = NO;
+    _isBlurringFocusedElement = NO;
 
 #if ENABLE(DATALIST_ELEMENT)
     _dataListTextSuggestionsInputView = nil;
@@ -4100,7 +4100,7 @@
 
 #if USE(UIKIT_KEYBOARD_ADDITIONS)
     // Do not change traits when dismissing the keyboard.
-    if (_isBlurringFocusedNode)
+    if (_isBlurringFocusedElement)
         return _traits.get();
 #endif
 
@@ -4802,7 +4802,6 @@
     id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
     RetainPtr<WKFocusedElementInfo> focusedElementInfo = adoptNS([[WKFocusedElementInfo alloc] initWithFocusedElementInformation:information isUserInitiated:userIsInteracting userObject:userObject]);
 
-    BOOL shouldShowKeyboard = NO;
     _WKFocusStartsInputSessionPolicy startInputSessionPolicy = _WKFocusStartsInputSessionPolicyAuto;
 
     if ([inputDelegate respondsToSelector:@selector(_webView:focusShouldStartInputSession:)]) {
@@ -4826,31 +4825,44 @@
     else
         [self _stopSuppressingSelectionAssistantForReason:WebKit::FocusedElementIsTooSmall];
 
-    switch (startInputSessionPolicy) {
-    case _WKFocusStartsInputSessionPolicyAuto:
-        // The default behavior is to allow node assistance if the user is interacting.
-        // We also allow node assistance if the keyboard already is showing, unless we're in extra zoom mode.
-        shouldShowKeyboard = userIsInteracting
+    BOOL shouldShowKeyboard = [&] {
+        switch (startInputSessionPolicy) {
+        case _WKFocusStartsInputSessionPolicyAuto:
+            // The default behavior is to allow node assistance if the user is interacting.
+            // We also allow node assistance if the keyboard already is showing, unless we're in extra zoom mode.
+            if (userIsInteracting)
+                return YES;
+
+#if ENABLE(DRAG_SUPPORT)
+            if (_dragDropInteractionState.isPerformingDrop())
+                return YES;
+#endif
+
+            if (self.isFirstResponder || _becomingFirstResponder) {
+                if (changingActivityState)
+                    return YES;
+
 #if PLATFORM(WATCHOS)
-            || (_isChangingFocus && ![_focusedFormControlView isHidden])
+                if (_isChangingFocus && ![_focusedFormControlView isHidden])
+                    return YES;
 #else
-            || _isChangingFocus
-            || [UIKeyboard isInHardwareKeyboardMode]
+                if (_isChangingFocus)
+                    return YES;
+
+                if (UIKeyboard.isInHardwareKeyboardMode)
+                    return YES;
 #endif
-#if ENABLE(DRAG_SUPPORT)
-            || _dragDropInteractionState.isPerformingDrop()
-#endif
-            || changingActivityState;
-        break;
-    case _WKFocusStartsInputSessionPolicyAllow:
-        shouldShowKeyboard = YES;
-        break;
-    case _WKFocusStartsInputSessionPolicyDisallow:
-        shouldShowKeyboard = NO;
-        break;
-    default:
-        ASSERT_NOT_REACHED();
-    }
+            }
+            return NO;
+        case _WKFocusStartsInputSessionPolicyAllow:
+            return YES;
+        case _WKFocusStartsInputSessionPolicyDisallow:
+            return NO;
+        default:
+            ASSERT_NOT_REACHED();
+            return NO;
+        }
+    }();
 
     if (blurPreviousNode)
         [self _elementDidBlur];
@@ -4946,7 +4958,7 @@
 
 - (void)_elementDidBlur
 {
-    SetForScope<BOOL> isBlurringFocusedNodeForScope { _isBlurringFocusedNode, YES };
+    SetForScope<BOOL> isBlurringFocusedElementForScope { _isBlurringFocusedElement, YES };
 
 #if HAVE(PENCILKIT)
     [_drawingCoordinator uninstallInkPicker];
@@ -5303,7 +5315,7 @@
 
 - (void)focusedFormControllerDidUpdateSuggestions:(WKFocusedFormControlView *)view
 {
-    if (_isBlurringFocusedNode || ![_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]])
+    if (_isBlurringFocusedElement || ![_presentedFullScreenInputViewController isKindOfClass:[WKTextInputListViewController class]])
         return;
 
     [(WKTextInputListViewController *)_presentedFullScreenInputViewController reloadTextSuggestions];
_______________________________________________
webkit-changes mailing list
webkit-changes@lists.webkit.org
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to