Skip to content

Commit

Permalink
[UIAsyncTextInput] Edit menu doesn't appear after choosing "Select" i…
Browse files Browse the repository at this point in the history
…n editable content

https://bugs.webkit.org/show_bug.cgi?id=265265
rdar://118726761

Reviewed by Aditya Keerthi.

Previously, `UIWKTextInteractionAssistant` would automatically present the edit menu after a 200 ms
delay, when invoking `-selectAll:` or `-selectWord`. These SPIs are no longer exposed on
`UIAsyncTextInteraction`, but we instead have the ability to directly request edit menu presentation
via `-presentEditMenuForSelection`.

This causes the layout test `editing/selection/ios/show-callout-bar-after-selecting-word.html` to
fail when using the async text input/interaction codepath. To fix this, we add logic to schedule
edit menu presentation 200 ms after the next selection change, after triggering `-selectWord` or
`-selectAll:`.

* LayoutTests/editing/selection/ios/show-callout-bar-after-selecting-word-expected.txt:
* LayoutTests/editing/selection/ios/show-callout-bar-after-selecting-word.html:

Also adjust this test to be more robust, by waiting for the edit menu to no longer contain "Select
All". This is because it's possible for the last step of the test:

```
await UIHelper.rectForMenuAction("Select All");
```

...to check the contents of the edit menu, before the internal collection view for the edit menu has
been reloaded since being shown for the caret selection. This currently causes the test to be flaky,
with or without `--use-async-uikit-interactions`, on some versions of iOS 17.

* Source/WebKit/UIProcess/ios/WKTextInteractionWrapper.mm:
(-[WKTextInteractionWrapper dealloc]):
(-[WKTextInteractionWrapper deactivateSelection]):
(-[WKTextInteractionWrapper selectionChanged]):

Schedule a timer to present the edit menu after 200 ms, after the next selection change after
calling `-selectWord` or `-selectAll:`. See above for more details.

(-[WKTextInteractionWrapper selectWord]):
(-[WKTextInteractionWrapper selectAll:]):
(-[WKTextInteractionWrapper showEditMenuTimerFired]):
(-[WKTextInteractionWrapper stopShowEditMenuTimer]):

Canonical link: https://commits.webkit.org/271114@main
  • Loading branch information
whsieh committed Nov 25, 2023
1 parent 955c089 commit 457b73c
Show file tree
Hide file tree
Showing 3 changed files with 36 additions and 3 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE
PASS Showed menu by tapping
PASS Selected word using menu action
PASS Callout bar contains 'Copy' action
PASS selectAllItemRect is null
PASS Callout bar no longer contains 'Select All'
PASS successfullyParsed is true

TEST COMPLETE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,11 @@
await waitUntilMenuContains("Copy");
testPassed("Callout bar contains 'Copy' action");

selectAllItemRect = await UIHelper.rectForMenuAction("Select All");
shouldBeNull("selectAllItemRect");
var selectAllItemRect;
do {
selectAllItemRect = await UIHelper.rectForMenuAction("Select All");
} while (selectAllItemRect);
testPassed("Callout bar no longer contains 'Select All'");

editor.remove();
finishJSTest();
Expand Down
30 changes: 30 additions & 0 deletions Source/WebKit/UIProcess/ios/WKTextInteractionWrapper.mm
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ @implementation WKTextInteractionWrapper {
RetainPtr<UIWKTextInteractionAssistant> _textInteractionAssistant;
#if HAVE(UI_ASYNC_TEXT_INTERACTION)
RetainPtr<UIAsyncTextInteraction> _asyncTextInteraction;
RetainPtr<NSTimer> _showEditMenuTimer;
BOOL _showEditMenuAfterNextSelectionChange;
#endif
BOOL _shouldRestoreEditMenuAfterOverflowScrolling;
}
Expand Down Expand Up @@ -67,6 +69,7 @@ - (instancetype)initWithView:(WKContentView *)view
- (void)dealloc
{
#if HAVE(UI_ASYNC_TEXT_INTERACTION)
[self stopShowEditMenuTimer];
if (_asyncTextInteraction)
[_view removeInteraction:_asyncTextInteraction.get()];
#endif
Expand All @@ -92,6 +95,8 @@ - (void)deactivateSelection
[_textInteractionAssistant deactivateSelection];
#if HAVE(UI_ASYNC_TEXT_INTERACTION)
[_asyncTextInteraction textSelectionDisplayInteraction].activated = NO;
_showEditMenuAfterNextSelectionChange = NO;
[self stopShowEditMenuTimer];
#endif
}

Expand All @@ -100,6 +105,10 @@ - (void)selectionChanged
[_textInteractionAssistant selectionChanged];
#if HAVE(UI_ASYNC_TEXT_INTERACTION)
[_asyncTextInteraction selectionChanged];

[self stopShowEditMenuTimer];
if (std::exchange(_showEditMenuAfterNextSelectionChange, NO))
_showEditMenuTimer = [NSTimer scheduledTimerWithTimeInterval:0.2 target:self selector:@selector(showEditMenuTimerFired) userInfo:nil repeats:NO];
#endif
}

Expand Down Expand Up @@ -224,13 +233,34 @@ - (void)translate:(NSString *)text fromRect:(CGRect)presentationRect
- (void)selectWord
{
[_textInteractionAssistant selectWord];
#if HAVE(UI_ASYNC_TEXT_INTERACTION)
_showEditMenuAfterNextSelectionChange = YES;
#endif
}

- (void)selectAll:(id)sender
{
[_textInteractionAssistant selectAll:sender];
#if HAVE(UI_ASYNC_TEXT_INTERACTION)
_showEditMenuAfterNextSelectionChange = YES;
#endif
}

#if HAVE(UI_ASYNC_TEXT_INTERACTION)

- (void)showEditMenuTimerFired
{
[self stopShowEditMenuTimer];
[_asyncTextInteraction presentEditMenuForSelection];
}

- (void)stopShowEditMenuTimer
{
[std::exchange(_showEditMenuTimer, nil) invalidate];
}

#endif // HAVE(UI_ASYNC_TEXT_INTERACTION)

#if USE(UICONTEXTMENU)

- (UIContextMenuInteraction *)contextMenuInteraction
Expand Down

0 comments on commit 457b73c

Please sign in to comment.