Skip to content

Commit

Permalink
Cherry-pick 1cb03ba. rdar://120743391
Browse files Browse the repository at this point in the history
    [iOS] -[WKContentView hasText] is incorrect when initially focusing an editable element
    https://bugs.webkit.org/show_bug.cgi?id=267729
    rdar://120743391

    Reviewed by Aditya Keerthi.

    Currently, `-hasText` is based entirely on the last cached `EditorState`'s post layout data. In the
    case where we've just started showing the keyboard for a focused editable element in
    `-[WKContentView _elementDidFocus:…:userObject:]`, we're still waiting for a new `EditorState` to
    arrive, so this result ends up being invalid (either `false` if we haven't received any post-layout
    editor state yet, or stale editor state data).

    To avoid this, we add a `hasPlainText` flag to `FocusedElementInformation` that represents whether
    or not the focused element initially had non-empty text content upon focus; if we're currently in
    the process of focusing (or are waiting for post-layout data) in `-hasText`, then use this initial
    value instead of the `EditorState`.

    Test: KeyboardInputTests.HasTextAfterFocusingTextField

    * Source/WebKit/Shared/FocusedElementInformation.h:
    * Source/WebKit/Shared/FocusedElementInformation.serialization.in:
    * Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm:
    (-[WKContentView hasText]):
    * Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm:
    (WebKit::WebPage::focusedElementInformation):

    For text fields and text areas, instead of using `hasPlainText` (which relies on `TextIterator`),
    simply check whether the `value` is non-empty instead to avoid any extra performance cost.

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

    Canonical link: https://commits.webkit.org/273211@main

Identifier: 272448.528@safari-7618.1.15.10-branch
  • Loading branch information
whsieh authored and rjepstein committed Feb 8, 2024
1 parent 9c8f91f commit ea91750
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 0 deletions.
1 change: 1 addition & 0 deletions Source/WebKit/Shared/FocusedElementInformation.h
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ struct FocusedElementInformation {
bool allowsUserScaling { false };
bool allowsUserScalingIgnoringAlwaysScalable { false };
bool insideFixedPosition { false };
bool hasPlainText { false };
WebCore::AutocapitalizeType autocapitalizeType { WebCore::AutocapitalizeType::Default };
InputType elementType { InputType::None };
WebCore::InputMode inputMode { WebCore::InputMode::Unspecified };
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ enum class WebKit::InputType : uint8_t {
bool allowsUserScaling;
bool allowsUserScalingIgnoringAlwaysScalable;
bool insideFixedPosition;
bool hasPlainText;
WebCore::AutocapitalizeType autocapitalizeType;
WebKit::InputType elementType;
WebCore::InputMode inputMode;
Expand Down
3 changes: 3 additions & 0 deletions Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm
Original file line number Diff line number Diff line change
Expand Up @@ -6559,6 +6559,9 @@ - (void)insertText:(NSString *)aStringValue alternatives:(NSArray<NSString *> *)

- (BOOL)hasText
{
if (_isFocusingElementWithKeyboard || _page->waitingForPostLayoutEditorStateUpdateAfterFocusingElement())
return _focusedElementInformation.hasPlainText;

auto& editorState = _page->editorState();
return editorState.hasPostLayoutData() && editorState.postLayoutData->hasPlainText;
}
Expand Down
3 changes: 3 additions & 0 deletions Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -3646,6 +3646,7 @@ static void handleAnimationActions(Element& element, uint32_t action)
information.elementType = InputType::TextArea;
information.isReadOnly = element.isReadOnly();
information.value = element.value();
information.hasPlainText = !information.value.isEmpty();
information.autofillFieldName = WebCore::toAutofillFieldName(element.autofillData().fieldName);
information.nonAutofillCredentialType = element.autofillData().nonAutofillCredentialType;
information.placeholder = element.attributeWithoutSynchronization(HTMLNames::placeholderAttr);
Expand Down Expand Up @@ -3716,6 +3717,7 @@ static void handleAnimationActions(Element& element, uint32_t action)
information.enterKeyHint = element.canonicalEnterKeyHint();
information.isReadOnly = element.isReadOnly();
information.value = element.value();
information.hasPlainText = !information.value.isEmpty();
information.valueAsNumber = element.valueAsNumber();
information.autofillFieldName = WebCore::toAutofillFieldName(element.autofillData().fieldName);
information.nonAutofillCredentialType = element.autofillData().nonAutofillCredentialType;
Expand All @@ -3733,6 +3735,7 @@ static void handleAnimationActions(Element& element, uint32_t action)
information.autocapitalizeType = WebCore::AutocapitalizeType::Default;
}
information.isReadOnly = false;
information.hasPlainText = hasAnyPlainText(makeRangeSelectingNodeContents(*focusedElement));
}

if (focusedElement->document().quirks().shouldSuppressAutocorrectionAndAutocapitalizationInHiddenEditableAreas() && isTransparentOrFullyClipped(*focusedElement)) {
Expand Down
50 changes: 50 additions & 0 deletions Tools/TestWebKitAPI/Tests/ios/KeyboardInputTestsIOS.mm
Original file line number Diff line number Diff line change
Expand Up @@ -1134,6 +1134,56 @@ static BOOL shouldSimulateKeyboardInputOnTextInsertionOverride(id, SEL)
EXPECT_GT(numberOfDifferentPixels, 0U);
}

TEST(KeyboardInputTests, HasTextAfterFocusingTextField)
{
auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 600)]);

enum class HasText : bool { No, Yes };
__block Vector<std::pair<WKInputType, HasText>, 3> results;
__block bool doneWaitingForInputSession = false;

auto inputDelegate = adoptNS([[TestInputDelegate alloc] init]);
[inputDelegate setDidStartInputSessionHandler:^(WKWebView *webView, id<_WKFormInputSession> session) {
results.append({
session.focusedElementInfo.type,
webView.textInputContentView.hasText ? HasText::Yes : HasText::No
});
doneWaitingForInputSession = true;
}];

[inputDelegate setFocusStartsInputSessionPolicyHandler:^(WKWebView *, id<_WKFocusedElementInfo>) {
return _WKFocusStartsInputSessionPolicyAllow;
}];
[webView _setInputDelegate:inputDelegate.get()];
[webView synchronouslyLoadHTMLString:@"<!DOCTYPE html>"
"<html>"
"<meta name='viewport' content='width=device-width'>"
"<body>"
" <input id='emailField' type='email' value='foo@bar.com'>"
" <input id='passwordField' type='password'>"
" <div id='richTextEditor' contentEditable='true'><span>Hello world</span></div>"
"</body>"
"</html>"];

auto waitForInputSessionAfterFocusing = ^(NSString *elementID) {
doneWaitingForInputSession = false;
[webView objectByEvaluatingJavaScript:[NSString stringWithFormat:@"%@.focus()", elementID]];
Util::run(&doneWaitingForInputSession);
};

waitForInputSessionAfterFocusing(@"emailField");
waitForInputSessionAfterFocusing(@"passwordField");
waitForInputSessionAfterFocusing(@"richTextEditor");

EXPECT_EQ(results.size(), 3U);
EXPECT_EQ(results[0].first, WKInputTypeEmail);
EXPECT_EQ(results[0].second, HasText::Yes);
EXPECT_EQ(results[1].first, WKInputTypePassword);
EXPECT_EQ(results[1].second, HasText::No);
EXPECT_EQ(results[2].first, WKInputTypeContentEditable);
EXPECT_EQ(results[2].second, HasText::Yes);
}

#if HAVE(AUTOCORRECTION_ENHANCEMENTS)
TEST(KeyboardInputTests, AutocorrectionIndicatorColorNotAffectedByAuthorDefinedAncestorColorProperty)
{
Expand Down

0 comments on commit ea91750

Please sign in to comment.