[webkit-changes] [WebKit/WebKit] b532c6: [iOS 17] [Mail] Tabbing from subject field to comp...

Wenson Hsieh noreply at github.com
Mon Jul 24 10:54:40 PDT 2023


  Branch: refs/heads/main
  Home:   https://github.com/WebKit/WebKit
  Commit: b532c60a79794ae5c6d41f02419bbba79a3da347
      https://github.com/WebKit/WebKit/commit/b532c60a79794ae5c6d41f02419bbba79a3da347
  Author: Wenson Hsieh <wenson_hsieh at 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




More information about the webkit-changes mailing list