⚠ Archived content — this site is no longer maintained.   Current WebKit documentation is at docs.webkit.org.

Changeset 277352 in webkit


Ignore:
Timestamp:
May 11, 2021, 8:46:21 PM (5 years ago)
Author:
Wenson Hsieh
Message:

[iOS] Mail compose web view doesn't scroll to reveal the selection in certain configurations
https://bugs.webkit.org/show_bug.cgi?id=225675
rdar://77095886

Reviewed by Tim Horton.

Source/WebKit:

Currently when computing input view bounds upon receiving UIKeyboardDidChangeFrameNotification, we attempt to
map the on-screen bounds of the keyboard to the window's coordinate space, and save the result in an ivar in
WKWebView, _inputViewBounds. The keyboard frame (which corresponds to UIKeyboardFrameEndUserInfoKey in the
notification's userInfo dictionary) is given to us in screen coordinates, and we currently pass this through
-convertRect:fromWindow:, with a nil UIWindow.

However, this results in mapping the rect from the coordinate space of the window's UIWindowScene rather than
the window screen. In shipping Mail on iOS, this doesn't matter because the window containing the compose web
view shares the same coordinate space as the screen. In some other configurations of MobileMail, however, the
compose web view appears inside its own UIWindow. This causes the above coordinate conversion logic to fail,
since we attempt to map a rect given to us in screen coordinates from the compose web view's window scene's
coordinate space, instead of the screen's coordinate space.

We fix this by using -convertRect:fromCoordinateSpace: instead, and explicitly pass in
self.window.screen.coordinateSpace.

  • UIProcess/API/Cocoa/WKWebViewInternal.h:
  • UIProcess/API/ios/WKWebViewIOS.mm:

(-[WKWebView _updateScrollViewForTransaction:]):
(-[WKWebView _zoomToFocusRect:selectionRect:fontSize:minimumScale:maximumScale:allowScaling:forceScroll:]):
(-[WKWebView _contentRectForUserInteraction]):
(-[WKWebView _updateVisibleContentRects]):
(-[WKWebView _keyboardChangedWithInfo:adjustScrollView:]):

Rename _inputViewBounds to _inputViewBoundsInWindow for clarity.

  • UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h:
  • UIProcess/API/ios/WKWebViewTestingIOS.mm:

(-[WKWebView _inputViewBoundsInWindow]):
(-[WKWebView _inputViewBounds]): Deleted.

Tools:

Rename some testing SPI. See WebKit/ChangeLog for more information.

  • WebKitTestRunner/ios/UIScriptControllerIOS.mm:

(WTR::UIScriptControllerIOS::inputViewBounds const):

Location:
trunk
Files:
7 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r277351 r277352  
     12021-05-11  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Mail compose web view doesn't scroll to reveal the selection in certain configurations
     4        https://bugs.webkit.org/show_bug.cgi?id=225675
     5        rdar://77095886
     6
     7        Reviewed by Tim Horton.
     8
     9        Currently when computing input view bounds upon receiving `UIKeyboardDidChangeFrameNotification`, we attempt to
     10        map the on-screen bounds of the keyboard to the window's coordinate space, and save the result in an ivar in
     11        `WKWebView`, `_inputViewBounds`. The keyboard frame (which corresponds to `UIKeyboardFrameEndUserInfoKey` in the
     12        notification's userInfo dictionary) is given to us in screen coordinates, and we currently pass this through
     13        `-convertRect:fromWindow:`, with a nil `UIWindow`.
     14
     15        However, this results in mapping the rect from the coordinate space of the window's `UIWindowScene` rather than
     16        the window screen. In shipping Mail on iOS, this doesn't matter because the window containing the compose web
     17        view shares the same coordinate space as the screen. In some other configurations of MobileMail, however, the
     18        compose web view appears inside its own `UIWindow`. This causes the above coordinate conversion logic to fail,
     19        since we attempt to map a rect given to us in screen coordinates from the compose web view's window scene's
     20        coordinate space, instead of the screen's coordinate space.
     21
     22        We fix this by using `-convertRect:fromCoordinateSpace:` instead, and explicitly pass in
     23        `self.window.screen.coordinateSpace`.
     24
     25        * UIProcess/API/Cocoa/WKWebViewInternal.h:
     26        * UIProcess/API/ios/WKWebViewIOS.mm:
     27        (-[WKWebView _updateScrollViewForTransaction:]):
     28        (-[WKWebView _zoomToFocusRect:selectionRect:fontSize:minimumScale:maximumScale:allowScaling:forceScroll:]):
     29        (-[WKWebView _contentRectForUserInteraction]):
     30        (-[WKWebView _updateVisibleContentRects]):
     31        (-[WKWebView _keyboardChangedWithInfo:adjustScrollView:]):
     32
     33        Rename `_inputViewBounds` to `_inputViewBoundsInWindow` for clarity.
     34
     35        * UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h:
     36        * UIProcess/API/ios/WKWebViewTestingIOS.mm:
     37        (-[WKWebView _inputViewBoundsInWindow]):
     38        (-[WKWebView _inputViewBounds]): Deleted.
     39
    1402021-05-11  Simon Fraser  <simon.fraser@apple.com>
    241
  • trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewInternal.h

    r276621 r277352  
    158158    Optional<CGSize> _maximumUnobscuredSizeOverride;
    159159    Optional<WebCore::FloatSize> _lastSentMaximumUnobscuredSize;
    160     CGRect _inputViewBounds;
     160    CGRect _inputViewBoundsInWindow;
    161161
    162162    CGFloat _viewportMetaTagWidth;
  • trunk/Source/WebKit/UIProcess/API/ios/WKWebViewIOS.mm

    r276780 r277352  
    808808    [_scrollView _setZoomEnabledInternal:layerTreeTransaction.allowsUserScaling()];
    809809
    810     bool hasDockedInputView = !CGRectIsEmpty(_inputViewBounds);
     810    bool hasDockedInputView = !CGRectIsEmpty(_inputViewBoundsInWindow);
    811811    bool isZoomed = layerTreeTransaction.pageScaleFactor() > layerTreeTransaction.initialScaleFactor();
    812812
     
    12431243    CGRect unobscuredScrollViewRectInWebViewCoordinates = UIEdgeInsetsInsetRect([self bounds], _obscuredInsets);
    12441244    CGRect visibleScrollViewBoundsInWebViewCoordinates = CGRectIntersection(unobscuredScrollViewRectInWebViewCoordinates, [fullScreenView convertRect:[fullScreenView bounds] toView:self]);
    1245     CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBounds toView:self];
     1245    CGRect formAssistantFrameInWebViewCoordinates = [window convertRect:_inputViewBoundsInWindow toView:self];
    12461246    CGRect intersectionBetweenScrollViewAndFormAssistant = CGRectIntersection(visibleScrollViewBoundsInWebViewCoordinates, formAssistantFrameInWebViewCoordinates);
    12471247    CGSize visibleSize = visibleScrollViewBoundsInWebViewCoordinates.size;
     
    18821882    // FIXME: handle split keyboard.
    18831883    UIEdgeInsets obscuredInsets = _obscuredInsets;
    1884     obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBounds.size.height);
     1884    obscuredInsets.bottom = std::max(_obscuredInsets.bottom, _inputViewBoundsInWindow.size.height);
    18851885    CGRect unobscuredRect = UIEdgeInsetsInsetRect(self.bounds, obscuredInsets);
    18861886    return [self convertRect:unobscuredRect toView:self._currentContentView];
     
    21422142        obscuredInsets:_obscuredInsets
    21432143        unobscuredSafeAreaInsets:[self _computedUnobscuredSafeAreaInset]
    2144         inputViewBounds:_inputViewBounds
     2144        inputViewBounds:_inputViewBoundsInWindow
    21452145        scale:scaleFactor minimumScale:[_scrollView minimumZoomScale]
    21462146        viewStability:viewStability
     
    22992299        return;
    23002300
    2301     auto previousInputViewBounds = _inputViewBounds;
     2301    auto previousInputViewBounds = _inputViewBoundsInWindow;
    23022302    BOOL selectionWasVisible = self._selectionRectIsFullyVisibleAndNonEmpty;
    23032303
    2304     // The keyboard rect is always in screen coordinates. In the view services case the window does not
    2305     // have the interface orientation rotation transformation; its host does. So, it makes no sense to
    2306     // clip the keyboard rect against its screen.
    2307     if ([[self window] _isHostedInAnotherProcess])
    2308         _inputViewBounds = [self.window convertRect:[endFrameValue CGRectValue] fromWindow:nil];
    2309     else
    2310         _inputViewBounds = [self.window convertRect:CGRectIntersection([endFrameValue CGRectValue], self.window.screen.bounds) fromWindow:nil];
    2311 
    2312     if ([[UIPeripheralHost sharedInstance] isUndocked])
    2313         _inputViewBounds = CGRectZero;
     2304    _inputViewBoundsInWindow = ([&] {
     2305        if (UIPeripheralHost.sharedInstance.isUndocked)
     2306            return CGRectZero;
     2307
     2308        auto keyboardFrameInScreen = endFrameValue.CGRectValue;
     2309        // The keyboard rect is always in screen coordinates. In the view services case the window does not
     2310        // have the interface orientation rotation transformation; its host does. So, it makes no sense to
     2311        // clip the keyboard rect against its screen.
     2312        if (!self.window._isHostedInAnotherProcess)
     2313            keyboardFrameInScreen = CGRectIntersection(keyboardFrameInScreen, self.window.screen.bounds);
     2314
     2315        return [self.window convertRect:keyboardFrameInScreen fromCoordinateSpace:self.window.screen.coordinateSpace];
     2316    })();
    23142317
    23152318    if (adjustScrollView) {
     
    23252328    }
    23262329
    2327     if (selectionWasVisible && [_contentView _hasFocusedElement] && !CGRectIsEmpty(previousInputViewBounds) && !CGRectIsEmpty(_inputViewBounds) && !CGRectEqualToRect(previousInputViewBounds, _inputViewBounds))
     2330    if (selectionWasVisible && [_contentView _hasFocusedElement] && !CGRectIsEmpty(previousInputViewBounds) && !CGRectIsEmpty(_inputViewBoundsInWindow) && !CGRectEqualToRect(previousInputViewBounds, _inputViewBoundsInWindow))
    23282331        [self _scrollToAndRevealSelectionIfNeeded];
    23292332
  • trunk/Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h

    r276853 r277352  
    4040@property (nonatomic, readonly) NSString *formInputLabel;
    4141@property (nonatomic, readonly) NSArray<NSValue *> *_uiTextSelectionRects;
    42 @property (nonatomic, readonly) CGRect _inputViewBounds;
     42@property (nonatomic, readonly) CGRect _inputViewBoundsInWindow;
    4343@property (nonatomic, readonly) NSString *_scrollingTreeAsText;
    4444@property (nonatomic, readonly) NSNumber *_stableStateOverride;
  • trunk/Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm

    r276853 r277352  
    204204}
    205205
    206 - (CGRect)_inputViewBounds
    207 {
    208     return _inputViewBounds;
     206- (CGRect)_inputViewBoundsInWindow
     207{
     208    return _inputViewBoundsInWindow;
    209209}
    210210
  • trunk/Tools/ChangeLog

    r277348 r277352  
     12021-05-11  Wenson Hsieh  <wenson_hsieh@apple.com>
     2
     3        [iOS] Mail compose web view doesn't scroll to reveal the selection in certain configurations
     4        https://bugs.webkit.org/show_bug.cgi?id=225675
     5        rdar://77095886
     6
     7        Reviewed by Tim Horton.
     8
     9        Rename some testing SPI. See WebKit/ChangeLog for more information.
     10
     11        * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
     12        (WTR::UIScriptControllerIOS::inputViewBounds const):
     13
    1142021-05-11  Commit Queue  <commit-queue@webkit.org>
    215
  • trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm

    r277265 r277352  
    850850JSObjectRef UIScriptControllerIOS::inputViewBounds() const
    851851{
    852     return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:toNSDictionary(webView()._inputViewBounds) inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
     852    return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:toNSDictionary(webView()._inputViewBoundsInWindow) inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
    853853}
    854854
Note: See TracChangeset for help on using the changeset viewer.