Skip to content

Commit

Permalink
[iOS] [UIAsyncTextInput] WKWebView AutoFill input assistant item does…
Browse files Browse the repository at this point in the history
…n't show up in the keyboard

https://bugs.webkit.org/show_bug.cgi?id=267865
rdar://121160501

Reviewed by Aditya Keerthi and Abrar Rahman Protyasha.

When async text input is enabled, we use `-requestTextContextForAutocorrectionWithCompletionHandler:`
to surface autocorrection context information to UIKit. The conversion from
`WebAutocorrectionContext::markedTextRange` to `DocumentEditingContext::selectedRangeInMarkedText`
is currently straightforward:

```
editingContext.selectedRangeInMarkedText = {
    .location = correctionContext.selectedRangeInMarkedText.location,
    .length = correctionContext.selectedRangeInMarkedText.length
};
```

…however, this hides a subtle bug, since the "not found" location of `markedTextRange` (an
`EditingRange`) is represented using `WTF::notFound`, while the `selectedRangeInMarkedText`
represents "not found" with `NSNotFound`, directly mirroring `NSRange`. The `NSRange` operator in
`EditingRange` is aware of this difference:

```
operator NSRange() const
{
    if (location == notFound)
        return NSMakeRange(NSNotFound, 0);
    return NSMakeRange(location, length);
}
```

…but we don't use it in the above code since we set the location and length directly. Fix this by
simply mapping `notFound` to `NSNotFound` when creating the platform context in the async text input
codepath.

Test: AutocorrectionTests.AutocorrectionContextBeforeAndAfterEditing

* Source/WebKit/Shared/ios/WebAutocorrectionContext.h:
* Source/WebKit/Shared/ios/WebAutocorrectionContext.serialization.in:

While we're here, also rename `markedTextRange` to `selectedRangeInMarkedText`, to clarify what this
range actually represents.

* Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView requestTextContextForAutocorrectionWithCompletionHandler:]):
(+[WKAutocorrectionContext autocorrectionContextWithWebContext:]):
* Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::autocorrectionContext):
* Tools/TestRunnerShared/spi/UIKitSPIForTesting.h:
* Tools/TestWebKitAPI/Tests/ios/AutocorrectionTestsIOS.mm:

Augment an existing API test to also verify that when there's no IME composition range, `markedText`
is `nil` and `selectedRangeInMarkedText` is exactly equal to `{ NSNotFound, 0 }`.

* Tools/TestWebKitAPI/cocoa/TestWKWebView.h:
* Tools/TestWebKitAPI/cocoa/TestWKWebView.mm:
(-[WKWebView autocorrectionContext]):

Canonical link: https://commits.webkit.org/273316@main
  • Loading branch information
whsieh committed Jan 22, 2024
1 parent 4c9e5ce commit 851a947
Show file tree
Hide file tree
Showing 8 changed files with 23 additions and 10 deletions.
2 changes: 1 addition & 1 deletion Source/WebKit/Shared/ios/WebAutocorrectionContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ struct WebAutocorrectionContext {
String markedText;
String selectedText;
String contextAfter;
EditingRange markedTextRange;
EditingRange selectedRangeInMarkedText;
};

} // namespace WebKit
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ struct WebKit::WebAutocorrectionContext {
String markedText;
String selectedText;
String contextAfter;
WebKit::EditingRange markedTextRange;
WebKit::EditingRange selectedRangeInMarkedText;
};

#endif
7 changes: 4 additions & 3 deletions Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Original file line number Diff line number Diff line change
Expand Up @@ -12888,9 +12888,10 @@ - (void)requestTextContextForAutocorrectionWithCompletionHandler:(void (^)(WKSET
context.markedText.string = webContext.markedText;
context.selectedText.string = webContext.selectedText;
context.contextAfter.string = webContext.contextAfter;
NSRange selectedRangeInMarkedText = webContext.selectedRangeInMarkedText;
context.selectedRangeInMarkedText = {
.location = webContext.markedTextRange.location,
.length = webContext.markedTextRange.length,
.location = selectedRangeInMarkedText.location,
.length = selectedRangeInMarkedText.length
};
break;
}
Expand Down Expand Up @@ -14751,7 +14752,7 @@ + (WKAutocorrectionContext *)autocorrectionContextWithWebContext:(const WebKit::
[correction setSelectedText:nsStringNilIfEmpty(webCorrection.selectedText)];
[correction setMarkedText:nsStringNilIfEmpty(webCorrection.markedText)];
[correction setContextAfterSelection:nsStringNilIfEmpty(webCorrection.contextAfter)];
[correction setRangeInMarkedText:webCorrection.markedTextRange];
[correction setRangeInMarkedText:webCorrection.selectedRangeInMarkedText];
return correction.autorelease();
}

Expand Down
8 changes: 4 additions & 4 deletions Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2709,7 +2709,7 @@ static inline bool rectIsTooBigForSelection(const IntRect& blockRect, const Loca
String markedText;
String selectedText;
String contextAfter;
EditingRange markedTextRange;
EditingRange selectedRangeInMarkedText;

VisiblePosition startPosition = frame->selection().selection().start();
VisiblePosition endPosition = frame->selection().selection().end();
Expand All @@ -2725,8 +2725,8 @@ static inline bool rectIsTooBigForSelection(const IntRect& blockRect, const Loca
auto markedTextAfter = plainTextForContext(makeSimpleRange(endPosition, compositionRange->end));
markedText = markedTextBefore + selectedText + markedTextAfter;
if (!markedText.isEmpty()) {
markedTextRange.location = markedTextBefore.length();
markedTextRange.length = selectedText.length();
selectedRangeInMarkedText.location = markedTextBefore.length();
selectedRangeInMarkedText.length = selectedText.length();
}
} else {
auto firstPositionInEditableContent = startOfEditableContent(startPosition);
Expand Down Expand Up @@ -2768,7 +2768,7 @@ static inline bool rectIsTooBigForSelection(const IntRect& blockRect, const Loca
correction.markedText = WTFMove(markedText);
correction.selectedText = WTFMove(selectedText);
correction.contextAfter = WTFMove(contextAfter);
correction.markedTextRange = WTFMove(markedTextRange);
correction.selectedRangeInMarkedText = WTFMove(selectedRangeInMarkedText);
return correction;
}

Expand Down
2 changes: 2 additions & 0 deletions Tools/TestRunnerShared/spi/UIKitSPIForTesting.h
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,8 @@ typedef NS_OPTIONS(NSInteger, UIWKDocumentRequestFlags) {
@property (nonatomic, copy) NSString *contextBeforeSelection;
@property (nonatomic, copy) NSString *selectedText;
@property (nonatomic, copy) NSString *contextAfterSelection;
@property (nonatomic, copy) NSString *markedText;
@property (nonatomic) NSRange rangeInMarkedText;
@end

typedef NS_ENUM(NSInteger, UIWKGestureType) {
Expand Down
6 changes: 6 additions & 0 deletions Tools/TestWebKitAPI/Tests/ios/AutocorrectionTestsIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -259,18 +259,24 @@ static void checkCGRectIsNotEmpty(CGRect rect)
EXPECT_TRUE(contextBeforeInsertingText.contextBeforeSelection.isEmpty());
EXPECT_TRUE(contextBeforeInsertingText.selectedText.isEmpty());
EXPECT_TRUE(contextBeforeInsertingText.contextAfterSelection.isEmpty());
EXPECT_TRUE(contextBeforeInsertingText.markedText.isNull());
EXPECT_TRUE(NSEqualRanges(contextBeforeInsertingText.selectedRangeInMarkedText, NSMakeRange(NSNotFound, 0)));

[contentView insertText:@"hello"];
auto contextAfterInsertingText = [webView autocorrectionContext];
EXPECT_WK_STREQ("hello", contextAfterInsertingText.contextBeforeSelection);
EXPECT_TRUE(contextAfterInsertingText.selectedText.isEmpty());
EXPECT_TRUE(contextAfterInsertingText.contextAfterSelection.isEmpty());
EXPECT_TRUE(contextAfterInsertingText.markedText.isNull());
EXPECT_TRUE(NSEqualRanges(contextAfterInsertingText.selectedRangeInMarkedText, NSMakeRange(NSNotFound, 0)));

[contentView selectAll:nil];
auto contextAfterSelecting = [webView autocorrectionContext];
EXPECT_TRUE(contextAfterSelecting.contextBeforeSelection.isEmpty());
EXPECT_WK_STREQ("hello", contextAfterSelecting.selectedText);
EXPECT_TRUE(contextAfterSelecting.contextAfterSelection.isEmpty());
EXPECT_TRUE(contextAfterSelecting.markedText.isNull());
EXPECT_TRUE(NSEqualRanges(contextAfterSelecting.selectedRangeInMarkedText, NSMakeRange(NSNotFound, 0)));
}

TEST(AutocorrectionTests, AvoidDeadlockWithGPUProcessCreationInEmptyView)
Expand Down
2 changes: 2 additions & 0 deletions Tools/TestWebKitAPI/cocoa/TestWKWebView.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ struct AutocorrectionContext {
String contextBeforeSelection;
String selectedText;
String contextAfterSelection;
String markedText;
NSRange selectedRangeInMarkedText;
};

} // namespace TestWebKitAPI
Expand Down
4 changes: 3 additions & 1 deletion Tools/TestWebKitAPI/cocoa/TestWKWebView.mm
Original file line number Diff line number Diff line change
Expand Up @@ -327,14 +327,16 @@ - (void)extendSelectionToEndOfParagraph
dynamic_objc_cast<NSString>(uiContext.contextBefore),
dynamic_objc_cast<NSString>(uiContext.selectedText),
dynamic_objc_cast<NSString>(uiContext.contextAfter),
dynamic_objc_cast<NSString>(uiContext.markedText),
uiContext.selectedRangeInMarkedText,
};
done = true;
}];
} else
#endif // HAVE(UI_ASYNC_TEXT_INTERACTION)
{
[self.textInputContentView requestAutocorrectionContextWithCompletionHandler:[&](UIWKAutocorrectionContext *context) {
result = { context.contextBeforeSelection, context.selectedText, context.contextAfterSelection };
result = { context.contextBeforeSelection, context.selectedText, context.contextAfterSelection, context.markedText, context.rangeInMarkedText };
done = true;
}];
}
Expand Down

0 comments on commit 851a947

Please sign in to comment.