Skip to content

Commit

Permalink
[iOS] [Safari] Contact AutoFill fails to insert text after adopting B…
Browse files Browse the repository at this point in the history
…ETextInput/BETextSuggestion

https://bugs.webkit.org/show_bug.cgi?id=268099
rdar://121604196

Reviewed by Tim Horton.

When receiving a `BETextSuggestion` in `-insertTextSuggestion:`, we currently integrate with our SPI
clients by asking for the `-inputText`, creating a `UITextSuggestion` from this text, and passing it
back to the client in `-_webView:insertTextSuggestion:inInputSession:`. However, this discards any
custom classes provided by the SPI client (in this case, Safari's `SFTextSuggestion`). Safari's
logic performs a class check on the text suggestion handed back to them in the input delegate SPI,
and drops the text suggestion on the floor in the case where the suggestion isn't a subclass of
`SFTextSuggestion`.

Simply fix this by unwrapping the `BETextSuggestion` to get the backing `UITextSuggestion` to
preserve bincompat with SPI clients.

Test: WKWebViewAutoFillTests.AutoFillPreservesTextSuggestion

* Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView insertTextSuggestion:]):
* Tools/TestWebKitAPI/Tests/ios/TestInputDelegate.mm:
(-[TestInputDelegate _webView:insertTextSuggestion:inInputSession:]):

Add an API test and test infrastructure to exercise this fix.

* Tools/TestWebKitAPI/Tests/ios/WKWebViewAutofillTests.mm:

Canonical link: https://commits.webkit.org/273535@main
  • Loading branch information
whsieh committed Jan 25, 2024
1 parent 15911ba commit 8feab81
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 5 deletions.
2 changes: 1 addition & 1 deletion Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6057,7 +6057,7 @@ - (void)insertTextSuggestion:(WKSETextSuggestion *)textSuggestion
id <_WKInputDelegate> inputDelegate = [_webView _inputDelegate];
if ([inputDelegate respondsToSelector:@selector(_webView:insertTextSuggestion:inInputSession:)]) {
#if SERVICE_EXTENSIONS_TEXT_INPUT_IS_AVAILABLE
RetainPtr uiTextSuggestion = [UITextSuggestion textSuggestionWithInputText:textSuggestion.inputText];
RetainPtr uiTextSuggestion = [textSuggestion _uikitTextSuggestion];
#else
RetainPtr uiTextSuggestion = textSuggestion;
#endif
Expand Down
1 change: 1 addition & 0 deletions Tools/TestWebKitAPI/Tests/ios/TestInputDelegate.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
@property (nonatomic, copy) void (^didStartInputSessionHandler)(WKWebView *, id <_WKFormInputSession>);
@property (nonatomic, copy) NSDictionary<id, NSString *> * (^webViewAdditionalContextForStrongPasswordAssistanceHandler)(WKWebView *);
@property (nonatomic, copy) BOOL (^focusRequiresStrongPasswordAssistanceHandler)(WKWebView *, id <_WKFocusedElementInfo>);
@property (nonatomic, copy) void (^insertTextSuggestionHandler)(WKWebView *, UITextSuggestion *, id<_WKFormInputSession>);
@end

#endif
25 changes: 21 additions & 4 deletions Tools/TestWebKitAPI/Tests/ios/TestInputDelegate.mm
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,22 @@
#import <wtf/BlockPtr.h>

@implementation TestInputDelegate {
BlockPtr<_WKFocusStartsInputSessionPolicy(WKWebView *, id <_WKFocusedElementInfo>)> _focusStartsInputSessionPolicyHandler;
BlockPtr<void(WKWebView *, id <_WKFormInputSession>)> _willStartInputSessionHandler;
BlockPtr<void(WKWebView *, id <_WKFormInputSession>)> _didStartInputSessionHandler;
BlockPtr<_WKFocusStartsInputSessionPolicy(WKWebView *, id<_WKFocusedElementInfo>)> _focusStartsInputSessionPolicyHandler;
BlockPtr<void(WKWebView *, id<_WKFormInputSession>)> _willStartInputSessionHandler;
BlockPtr<void(WKWebView *, id<_WKFormInputSession>)> _didStartInputSessionHandler;
BlockPtr<NSDictionary<id, NSString *> *(WKWebView *)> _webViewAdditionalContextForStrongPasswordAssistanceHandler;
BlockPtr<BOOL(WKWebView *, id <_WKFocusedElementInfo>)> _focusRequiresStrongPasswordAssistanceHandler;
BlockPtr<BOOL(WKWebView *, id<_WKFocusedElementInfo>)> _focusRequiresStrongPasswordAssistanceHandler;
BlockPtr<void(WKWebView *, UITextSuggestion *, id<_WKFormInputSession>)> _insertTextSuggestionHandler;
}

- (void)setInsertTextSuggestionHandler:(void (^)(WKWebView *, UITextSuggestion *, id<_WKFormInputSession>))insertTextSuggestionHandler
{
_insertTextSuggestionHandler = makeBlockPtr(insertTextSuggestionHandler);
}

- (void(^)(WKWebView *, UITextSuggestion *, id<_WKFormInputSession>))insertTextSuggestionHandler
{
return _insertTextSuggestionHandler.get();
}

- (void)setFocusStartsInputSessionPolicyHandler:(_WKFocusStartsInputSessionPolicy (^)(WKWebView *, id <_WKFocusedElementInfo>))handler
Expand Down Expand Up @@ -119,6 +130,12 @@ - (BOOL)_webView:(WKWebView *)webView focusRequiresStrongPasswordAssistance:(id
return NO;
}

- (void)_webView:(WKWebView *)webView insertTextSuggestion:(UITextSuggestion *)suggestion inInputSession:(id<_WKFormInputSession>)inputSession
{
if (_insertTextSuggestionHandler)
_insertTextSuggestionHandler(webView, suggestion, inputSession);
}

@end

#endif // PLATFORM(IOS_FAMILY)
40 changes: 40 additions & 0 deletions Tools/TestWebKitAPI/Tests/ios/WKWebViewAutofillTests.mm
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,20 @@
#import "TestInputDelegate.h"
#import "TestWKWebView.h"
#import "UIKitSPIForTesting.h"
#import "WKSEDefinitions.h"
#import <WebKit/WKWebViewPrivate.h>
#import <wtf/BlockPtr.h>

#if SERVICE_EXTENSIONS_TEXT_INPUT_IS_AVAILABLE
#import <BrowserEngineKit/BETextSuggestion_Private.h>
#endif

@interface CustomTextSuggestion : UITextSuggestion
@end

@implementation CustomTextSuggestion
@end

@protocol WKTextInputSuggestionDelegate <UITextInputSuggestionDelegate>
- (NSArray<UITextSuggestion *> *)suggestions;
@end
Expand Down Expand Up @@ -232,6 +243,35 @@ static BOOL overrideIsInHardwareKeyboardMode()
EXPECT_FALSE([webView acceptsAutoFillLoginCredentials]);
}

#if SERVICE_EXTENSIONS_TEXT_INPUT_IS_AVAILABLE

TEST(WKWebViewAutoFillTests, AutoFillPreservesTextSuggestion)
{
__block bool doneFocusing = false;
auto webView = adoptNS([[AutoFillTestView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
auto inputDelegate = static_cast<TestInputDelegate *>([webView _inputDelegate]);
[inputDelegate setFocusStartsInputSessionPolicyHandler:^(WKWebView *, id<_WKFocusedElementInfo>) {
doneFocusing = true;
return _WKFocusStartsInputSessionPolicyAllow;
}];

__block bool insertedSuggestion = false;
RetainPtr customSuggestion = [CustomTextSuggestion textSuggestionWithInputText:@"foo"];
[inputDelegate setInsertTextSuggestionHandler:^(WKWebView *, UITextSuggestion *suggestion, id<_WKFormInputSession>) {
EXPECT_EQ(customSuggestion.get(), suggestion);
insertedSuggestion = true;
}];
[webView synchronouslyLoadHTMLString:@"<input id='user' type='email'>"];
[webView stringByEvaluatingJavaScript:@"user.focus()"];
Util::run(&doneFocusing);

auto suggestion = adoptNS([[BETextSuggestion alloc] _initWithUIKitTextSuggestion:customSuggestion.get()]);
[[webView asyncTextInput] insertTextSuggestion:suggestion.get()];
EXPECT_TRUE(insertedSuggestion);
}

#endif // SERVICE_EXTENSIONS_TEXT_INPUT_IS_AVAILABLE

#if PLATFORM(WATCHOS)

TEST(WKWebViewAutoFillTests, DoNotShowBlankTextSuggestions)
Expand Down

0 comments on commit 8feab81

Please sign in to comment.