Branch: refs/heads/main Home: https://github.com/WebKit/WebKit Commit: b532c60a79794ae5c6d41f02419bbba79a3da347 https://github.com/WebKit/WebKit/commit/b532c60a79794ae5c6d41f02419bbba79a3da347 Author: Wenson Hsieh <wenson_hs...@apple.com> Date: 2023-07-24 (Mon, 24 Jul 2023)
Changed paths: A LayoutTests/editing/selection/ios/become-first-responder-after-relinquishing-focus-expected.txt A LayoutTests/editing/selection/ios/become-first-responder-after-relinquishing-focus.html M Source/WebCore/page/FocusController.cpp Log Message: ----------- [iOS 17] [Mail] Tabbing from subject field to compose body dismisses the keyboard and ends editing https://bugs.webkit.org/show_bug.cgi?id=259434 rdar://109244061 Reviewed by Megan Gardner. In Mail on iOS 17, shift-tabbing from the compose body field to the subject field and attempting to tab back into the body field causes the keyboard to dismiss. This happens because when the user attempts to tab back into the web view, we fail to set the focused element to the `body`, while handling the `IsFocused` activity state change in the web process after `-becomeFirstResponder`; more precisely, when entering `dispatchEventsOnWindowAndFocusedElement` during the activity state change, `document->focusedElement()` is null and so we don't dispatch a focus event. On iOS 16, this bug doesn't happen — when the selection is moved into the native subject field, UIKit code ends up calling into `-[WKContentView clearSelection]` to clear out the previous selection, such that when we try to tab back into the web view, we set the focused element back to the `body` as a byproduct of the fact that we're changing the selection underneath this call stack: ``` 1 WebCore::Document::setFocusedElement(…) 2 WebCore::FocusController::setFocusedElement(…) 3 WebCore::FrameSelection::setFocusedElementIfNeeded(…) 4 WebCore::FrameSelection::setSelectionWithoutUpdatingAppearance(…) 5 WebCore::FrameSelection::setSelectionWithoutUpdatingAppearance(…) 6 WebCore::FrameSelection::setSelection(…) 7 WebCore::FrameSelection::setSelectionFromNone(…) 8 WebCore::FrameSelection::focusedOrActiveStateChanged(…) 9 WebCore::FrameSelection::setFocused(…) ``` In contrast, on iOS 17, the call to `-[WKContentView clearSelection]` never happens; this in turn causes the selection to remain the same after tabbing back into the web view, which means that `willMutateSelection := false` in `FrameSelection::setSelectionWithoutUpdatingAppearance` and we bail before we get a chance to invoke `setFocusedElementIfNeeded();`. From examining UIKit sources, this basically worked by sheer accident on iOS 16: there's code in `UITextSelectionView` to *collapse* the selection to the end, but ends up clearing the selection only in WebKit views because `-[WKContentView textRangeFromPosition:toPosition:]` unconditionally returns `nil`. In iOS 17, we now use `UITextSelectionDisplayInteraction` instead of the legacy `UITextSelectionView`, which skips this codepath, and so the (accidental-but-necessary) process of clearing the selection no longer happens. Rather than try and ressurrect the iOS 16 codepath in a way that works with `UITextSelectionDisplayInteraction`, this patch fixes the bug by instead changing `relinquishFocusToChrome` to additionally clear the selection, such that when we subsequently refocus the web view and set the initial selection, we'll trigger a selection change that will also update the focused element. Note that this also brings `FocusController::relinquishFocusToChrome` more in line with `FocusController::setFocusedElement`, which also uses the same helper to clear the selection prior to updating the focused element. * LayoutTests/editing/selection/ios/become-first-responder-after-relinquishing-focus-expected.txt: Added. * LayoutTests/editing/selection/ios/become-first-responder-after-relinquishing-focus.html: Added. Add a layout test to exercise the bug, by using shift-tab and `-resignFirstResponder` to dismiss the keyboard in an editable web view, and then verifying that the keyboard shows up again after calling `-becomeFirstResponder`. * Source/WebCore/page/FocusController.cpp: (WebCore::clearSelectionIfNeeded): Move this helper function to the top of this file, so we can use it in `relinquishFocusToChrome`. Also make some minor adjustments to handle the case where the `newFocusedFrame` is `nullptr`. (WebCore::FocusController::relinquishFocusToChrome): Call `clearSelectionIfNeeded` when relinquishing focus to be consistent with the regular `setFocusedElement` codepath. Canonical link: https://commits.webkit.org/266256@main _______________________________________________ webkit-changes mailing list webkit-changes@lists.webkit.org https://lists.webkit.org/mailman/listinfo/webkit-changes