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

Reply via email to