Changeset 247651 in webkit


Ignore:
Timestamp:
Jul 19, 2019 12:55:20 PM (5 years ago)
Author:
Wenson Hsieh
Message:

[iOS] Entering 2FA code on idmsa.apple.com causes unexpected scrolling
https://bugs.webkit.org/show_bug.cgi?id=199949
<rdar://problem/49944428>

Reviewed by Tim Horton and Megan Gardner.

Source/WebKit:

Since at least iOS 11, -[UIScrollView _adjustForAutomaticKeyboardInfo:animated:lastAdjustment:] adjusts the
scroll view's content offset to account for updated keyboard bottom insets. In WebKit, we call this method
whenever keyboard geometry changes (based on system notifications, such as UIKeyboardWillHideNotification).

When switching between focused form fields, we hide the keyboard for the previous focused element prior to
showing the keyboard for the newly focused element. This means that we will actually dismiss the keyboard in the
process of changing the focused element, which posts keyboard geometry notifications, which causes us to scroll
WKScrollView.

On iOS 12, this would be immediately followed by re-presenting the keyboard for the new focused element, which
causes us to adjust the scroll view back to its original position right away; this means that the scrolling that
happens as a result of adjusting for the keyboard insets after dismissal doesn't result in any visible change.

However, on iOS 13, after r239441 and r244546, we now defer scrolling and zooming to reveal the focused element
until later; this means the scrolling that happens as a result of initially dismissing the keyboard now causes a
consistent jump in the scroll view's scroll position (whereas on iOS 12, this only happens rarely, and the jump
is also less noticeable).

To mitigate this, we detect the case where we're moving focus from one element to another; if we're about to
show a keyboard for the newly focused element, then we should avoid scrolling as a result of the impending
"keyboard will hide" notification.

Test: fast/forms/ios/no-scrolling-when-moving-focus-between-adjacent-fields.html

  • UIProcess/API/Cocoa/WKWebView.mm:

(-[WKWebView _keyboardChangedWithInfo:adjustScrollView:]):
(-[WKWebView _keyboardWillHide:]):

  • UIProcess/ios/WKContentViewInteraction.h:
  • UIProcess/ios/WKContentViewInteraction.mm:

(shouldShowKeyboardForElement):

Add a helper to determine whether we're focusing an element which presents a "keyboard" (i.e. a UIKit input
view, as opposed to modal select pickers, modal date pickers, or fields with inputmode="none", for which we
don't show an input view).

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

LayoutTests:

Add a new layout test to verify that moving focus between horizontally adjacent form controls doesn't induce
vertical scrolling.

  • fast/forms/ios/no-scrolling-when-moving-focus-between-adjacent-fields-expected.txt: Added.
  • fast/forms/ios/no-scrolling-when-moving-focus-between-adjacent-fields.html: Added.
Location:
trunk
Files:
2 added
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/LayoutTests/ChangeLog

    r247650 r247651  
     12019-07-19  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Entering 2FA code on idmsa.apple.com causes unexpected scrolling
     4        https://bugs.webkit.org/show_bug.cgi?id=199949
     5        <rdar://problem/49944428>
     6
     7        Reviewed by Tim Horton and Megan Gardner.
     8
     9        Add a new layout test to verify that moving focus between horizontally adjacent form controls doesn't induce
     10        vertical scrolling.
     11
     12        * fast/forms/ios/no-scrolling-when-moving-focus-between-adjacent-fields-expected.txt: Added.
     13        * fast/forms/ios/no-scrolling-when-moving-focus-between-adjacent-fields.html: Added.
     14
    1152019-07-19  Antoine Quint  <graouts@apple.com>
    216
  • trunk/Source/WebKit/ChangeLog

    r247635 r247651  
     12019-07-19  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Entering 2FA code on idmsa.apple.com causes unexpected scrolling
     4        https://bugs.webkit.org/show_bug.cgi?id=199949
     5        <rdar://problem/49944428>
     6
     7        Reviewed by Tim Horton and Megan Gardner.
     8
     9        Since at least iOS 11, -[UIScrollView _adjustForAutomaticKeyboardInfo:animated:lastAdjustment:] adjusts the
     10        scroll view's content offset to account for updated keyboard bottom insets. In WebKit, we call this method
     11        whenever keyboard geometry changes (based on system notifications, such as UIKeyboardWillHideNotification).
     12
     13        When switching between focused form fields, we hide the keyboard for the previous focused element prior to
     14        showing the keyboard for the newly focused element. This means that we will actually dismiss the keyboard in the
     15        process of changing the focused element, which posts keyboard geometry notifications, which causes us to scroll
     16        WKScrollView.
     17
     18        On iOS 12, this would be immediately followed by re-presenting the keyboard for the new focused element, which
     19        causes us to adjust the scroll view back to its original position right away; this means that the scrolling that
     20        happens as a result of adjusting for the keyboard insets after dismissal doesn't result in any visible change.
     21
     22        However, on iOS 13, after r239441 and r244546, we now defer scrolling and zooming to reveal the focused element
     23        until later; this means the scrolling that happens as a result of initially dismissing the keyboard now causes a
     24        consistent jump in the scroll view's scroll position (whereas on iOS 12, this only happens rarely, and the jump
     25        is also less noticeable).
     26
     27        To mitigate this, we detect the case where we're moving focus from one element to another; if we're about to
     28        show a keyboard for the newly focused element, then we should avoid scrolling as a result of the impending
     29        "keyboard will hide" notification.
     30
     31        Test: fast/forms/ios/no-scrolling-when-moving-focus-between-adjacent-fields.html
     32
     33        * UIProcess/API/Cocoa/WKWebView.mm:
     34        (-[WKWebView _keyboardChangedWithInfo:adjustScrollView:]):
     35        (-[WKWebView _keyboardWillHide:]):
     36        * UIProcess/ios/WKContentViewInteraction.h:
     37        * UIProcess/ios/WKContentViewInteraction.mm:
     38        (shouldShowKeyboardForElement):
     39
     40        Add a helper to determine whether we're focusing an element which presents a "keyboard" (i.e. a UIKit input
     41        view, as opposed to modal select pickers, modal date pickers, or fields with inputmode="none", for which we
     42        don't show an input view).
     43
     44        (-[WKContentView _elementDidFocus:userIsInteracting:blurPreviousNode:activityStateChanges:userObject:]):
     45        (-[WKContentView shouldIgnoreKeyboardWillHideNotification]):
     46
    1472019-07-18  Alex Christensen  <achristensen@webkit.org>
    248
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm

    r247490 r247651  
    33303330        [_scrollView _adjustForAutomaticKeyboardInfo:keyboardInfo animated:YES lastAdjustment:&_lastAdjustmentForScroller];
    33313331        CGFloat bottomInsetAfterAdjustment = [_scrollView contentInset].bottom;
     3332        // FIXME: This "total bottom content inset adjustment" mechanism hasn't worked since iOS 11, since -_adjustForAutomaticKeyboardInfo:animated:lastAdjustment:
     3333        // no longer sets -[UIScrollView contentInset] for apps linked on or after iOS 11. We should consider removing this logic, since the original bug this was
     3334        // intended to fix, <rdar://problem/23202254>, remains fixed through other means.
    33323335        if (bottomInsetBeforeAdjustment != bottomInsetAfterAdjustment)
    33333336            _totalScrollViewBottomInsetAdjustmentForKeyboard += bottomInsetAfterAdjustment - bottomInsetBeforeAdjustment;
     
    33723375- (void)_keyboardWillHide:(NSNotification *)notification
    33733376{
    3374     // Ignore keyboard will hide notifications sent during rotation. They're just there for
    3375     // backwards compatibility reasons and processing the will hide notification would
    3376     // temporarily screw up the unobscured view area.
    3377     if ([[UIPeripheralHost sharedInstance] rotationState])
     3377    if ([_contentView shouldIgnoreKeyboardWillHideNotification])
    33783378        return;
    33793379
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h

    r247197 r247651  
    346346    BOOL _needsDeferredEndScrollingSelectionUpdate;
    347347    BOOL _isChangingFocus;
     348    BOOL _isFocusingElementWithKeyboard;
    348349    BOOL _isBlurringFocusedElement;
    349350
     
    398399@property (nonatomic, readonly) BOOL isEditable;
    399400@property (nonatomic, readonly) BOOL shouldHideSelectionWhenScrolling;
     401@property (nonatomic, readonly) BOOL shouldIgnoreKeyboardWillHideNotification;
    400402@property (nonatomic, readonly) const WebKit::InteractionInformationAtPosition& positionInformation;
    401403@property (nonatomic, readonly) const WebKit::WKAutoCorrectionData& autocorrectionData;
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r247559 r247651  
    51605160}
    51615161
     5162static bool shouldShowKeyboardForElement(const WebKit::FocusedElementInformation& information)
     5163{
     5164    if (information.inputMode == WebCore::InputMode::None)
     5165        return false;
     5166
     5167    if (information.elementType == WebKit::InputType::Drawing)
     5168        return false;
     5169
     5170    if (mayContainSelectableText(information.elementType))
     5171        return true;
     5172
     5173    return !currentUserInterfaceIdiomIsPad();
     5174}
     5175
    51625176static WebCore::FloatRect rectToRevealWhenZoomingToFocusedElement(const WebKit::FocusedElementInformation& elementInfo, const WebKit::EditorState& editorState)
    51635177{
     
    52065220{
    52075221    SetForScope<BOOL> isChangingFocusForScope { _isChangingFocus, hasFocusedElement(_focusedElementInformation) };
     5222    SetForScope<BOOL> isFocusingElementWithKeyboardForScope { _isFocusingElementWithKeyboard, shouldShowKeyboardForElement(information) };
     5223
    52085224    auto inputViewUpdateDeferrer = std::exchange(_inputViewUpdateDeferrer, nullptr);
    52095225
     
    53995415    if (!_isChangingFocus)
    54005416        _didAccessoryTabInitiateFocus = NO;
     5417}
     5418
     5419- (BOOL)shouldIgnoreKeyboardWillHideNotification
     5420{
     5421    // Ignore keyboard will hide notifications sent during rotation. They're just there for
     5422    // backwards compatibility reasons and processing the will hide notification would
     5423    // temporarily screw up the unobscured view area.
     5424    if (UIPeripheralHost.sharedInstance.rotationState)
     5425        return YES;
     5426
     5427    if (_isChangingFocus && _isFocusingElementWithKeyboard)
     5428        return YES;
     5429
     5430    return NO;
    54015431}
    54025432
Note: See TracChangeset for help on using the changeset viewer.