[iOS] REGRESSION (r238635): Text area fails to re-focus after dismissal of keyboard...
authordbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Jan 2019 04:41:35 +0000 (04:41 +0000)
committerdbates@webkit.org <dbates@webkit.org@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 31 Jan 2019 04:41:35 +0000 (04:41 +0000)
https://bugs.webkit.org/show_bug.cgi?id=193987
<rdar://problem/47230785>

Reviewed by Tim Horton.

It is unnecessary to relinquish first responder status when a user explicitly dismissing
the keyboard. Moreover, doing so prevents key commands from being intercepted when a
hardware keyboard is subsequently attached.

Following r238635 a page becomes focused (accepting of keyboard input) and defocused
when the WKContentView becomes first responder and resigns first responder, respectively.
When a user explicitly dismisses the keyboard by tapping Done (iPhone) or the hide keyboard
button (iPad) then UIKit tells WKContentView to resign its first responder status only
to make its superview, WKWebView, first responder. When a person subsequently taps on the
page again, the WKContentView requests to become the first responder. However changes to
page focus are not guaranteed to be sent to the WebProcess immediately (WebPageProxy::activityStateDidChange()
will schedule an update). In particular, they are not guaranteed to be sent before the
WebProcess is told about a tap. Therefore, the WebProcess has out-of-date information on
focus state of the page. Instead we should detect when WKWebView is being asked to resign
as a result of the keyboard dismissal and refuse the request, taking care to end the current
editing session, blur the focused element, and dismiss the on-screen keyboard.

* Platform/spi/ios/UIKitSPI.h: Expose some SPI.
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView setupInteraction]): Register to receive notifications whenever a user
explicitly dismisses the keyboard.
(-[WKContentView resignFirstResponderForWebView]): If we are being asked to resign as a
result of a user explicitly dismissing the keyboard then refuse to resign.
(-[WKContentView _keyboardDidRequestDismissal:]): Update state, if applicable.

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

Source/WebKit/ChangeLog
Source/WebKit/Platform/spi/ios/UIKitSPI.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.h
Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

index 21e4bb2..695fcbf 100644 (file)
@@ -1,3 +1,37 @@
+2019-01-30  Daniel Bates  <dabates@apple.com>
+
+        [iOS] REGRESSION (r238635): Text area fails to re-focus after dismissal of keyboard on support.apple.com
+        https://bugs.webkit.org/show_bug.cgi?id=193987
+        <rdar://problem/47230785>
+
+        Reviewed by Tim Horton.
+
+        It is unnecessary to relinquish first responder status when a user explicitly dismissing
+        the keyboard. Moreover, doing so prevents key commands from being intercepted when a
+        hardware keyboard is subsequently attached.
+        
+        Following r238635 a page becomes focused (accepting of keyboard input) and defocused
+        when the WKContentView becomes first responder and resigns first responder, respectively.
+        When a user explicitly dismisses the keyboard by tapping Done (iPhone) or the hide keyboard
+        button (iPad) then UIKit tells WKContentView to resign its first responder status only
+        to make its superview, WKWebView, first responder. When a person subsequently taps on the
+        page again, the WKContentView requests to become the first responder. However changes to
+        page focus are not guaranteed to be sent to the WebProcess immediately (WebPageProxy::activityStateDidChange()
+        will schedule an update). In particular, they are not guaranteed to be sent before the
+        WebProcess is told about a tap. Therefore, the WebProcess has out-of-date information on
+        focus state of the page. Instead we should detect when WKWebView is being asked to resign
+        as a result of the keyboard dismissal and refuse the request, taking care to end the current
+        editing session, blur the focused element, and dismiss the on-screen keyboard.
+
+        * Platform/spi/ios/UIKitSPI.h: Expose some SPI.
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView setupInteraction]): Register to receive notifications whenever a user
+        explicitly dismisses the keyboard.
+        (-[WKContentView resignFirstResponderForWebView]): If we are being asked to resign as a
+        result of a user explicitly dismissing the keyboard then refuse to resign.
+        (-[WKContentView _keyboardDidRequestDismissal:]): Update state, if applicable.
+
 2019-01-30  Keith Rollin  <krollin@apple.com>
 
         Add default constructor for NetworkActivityTracker
index d447906..53f3ff7 100644 (file)
@@ -1145,6 +1145,8 @@ extern NSString * const UIWindowDidRotateNotification;
 extern NSString * const UIWindowNewScreenUserInfoKey;
 extern NSString * const UIWindowWillRotateNotification;
 
+extern NSString * const UIKeyboardPrivateDidRequestDismissalNotification;
+
 extern NSString * const UIKeyboardIsLocalUserInfoKey;
 
 extern UIApplication *UIApp;
index b5e6ae2..af2f109 100644 (file)
@@ -306,6 +306,8 @@ struct WKAutoCorrectionData {
     BOOL _showDebugTapHighlightsForFastClicking;
     BOOL _isZoomingToRevealFocusedElement;
 
+    BOOL _keyboardDidRequestDismissal;
+
     BOOL _becomingFirstResponder;
     BOOL _resigningFirstResponder;
     BOOL _needsDeferredEndScrollingSelectionUpdate;
index 32820ff..5eaba0f 100644 (file)
@@ -741,7 +741,10 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
     [self _registerPreview];
 #endif
 
-    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(_resetShowingTextStyle:) name:UIMenuControllerDidHideMenuNotification object:nil];
+    NSNotificationCenter *center = [NSNotificationCenter defaultCenter];
+    [center addObserver:self selector:@selector(_resetShowingTextStyle:) name:UIMenuControllerDidHideMenuNotification object:nil];
+    [center addObserver:self selector:@selector(_keyboardDidRequestDismissal:) name:UIKeyboardPrivateDidRequestDismissalNotification object:nil];
+
     _showingTextStyleOptions = NO;
 
     // FIXME: This should be called when we get notified that loading has completed.
@@ -1115,7 +1118,8 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
     // FIXME: Maybe we should call resignFirstResponder on the superclass
     // and do nothing if the return value is NO.
 
-    _resigningFirstResponder = YES;
+    SetForScope<BOOL> resigningFirstResponderScope { _resigningFirstResponder, YES };
+
     if (!_webView._retainingActiveFocusedState) {
         // We need to complete the editing operation before we blur the element.
         [self _endEditing];
@@ -1127,13 +1131,18 @@ static inline bool hasFocusedElement(WebKit::FocusedElementInformation focusedEl
     
     _inputViewUpdateDeferrer = nullptr;
 
+    // If the user explicitly dismissed the keyboard then we will lose first responder
+    // status only to gain it back again. Just don't resign in that case.
+    if (_keyboardDidRequestDismissal) {
+        _keyboardDidRequestDismissal = NO;
+        return NO;
+    }
+
     bool superDidResign = [super resignFirstResponder];
 
     if (superDidResign)
         _page->activityStateDidChange(WebCore::ActivityState::IsFocused);
 
-    _resigningFirstResponder = NO;
-
     return superDidResign;
 }
 
@@ -2789,6 +2798,13 @@ WEBCORE_COMMAND_FOR_WEBVIEW(pasteAndMatchStyle);
     [_textSelectionAssistant hideTextStyleOptions];
 }
 
+- (void)_keyboardDidRequestDismissal:(NSNotification *)notification
+{
+    if (![self isFirstResponder])
+        return;
+    _keyboardDidRequestDismissal = YES;
+}
+
 - (void)copyForWebView:(id)sender
 {
     _page->executeEditCommand("copy"_s);