diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog index f420c6b6b2ec..c35bc158b0a1 100644 --- a/Source/WebCore/ChangeLog +++ b/Source/WebCore/ChangeLog @@ -1,3 +1,18 @@ +2020-04-27 Daniel Bates + + Caret may be placed in the wrong spot for text input context that is a form control + https://bugs.webkit.org/show_bug.cgi?id=210939 + + + Reviewed by Darin Adler. + + Add a helper function that returns the closest editable position inside an element + for a given point (if any). + + * editing/Editing.cpp: + (WebCore::closestEditablePositionInElementForAbsolutePoint): Added. + * editing/Editing.h: + 2020-04-27 Alicia Boya GarcĂ­a [GStreamer] Rework WebKitWebSrc threading diff --git a/Source/WebCore/editing/Editing.cpp b/Source/WebCore/editing/Editing.cpp index 5ae7ab2dfa3b..c113c013d004 100644 --- a/Source/WebCore/editing/Editing.cpp +++ b/Source/WebCore/editing/Editing.cpp @@ -50,8 +50,10 @@ #include "RenderBlock.h" #include "RenderElement.h" #include "RenderTableCell.h" +#include "RenderTextControlSingleLine.h" #include "ShadowRoot.h" #include "Text.h" +#include "TextControlInnerElements.h" #include "TextIterator.h" #include "VisibleUnits.h" #include @@ -561,6 +563,33 @@ VisiblePosition visiblePositionAfterNode(Node& node) return positionInParentAfterNode(&node); } +VisiblePosition closestEditablePositionInElementForAbsolutePoint(const Element& element, const IntPoint& point) +{ + if (!element.isConnected() || !element.document().frame()) + return { }; + + Ref protectedElement { element }; + auto frame = makeRef(*element.document().frame()); + + element.document().updateLayoutIgnorePendingStylesheets(); + + RenderObject* renderer = element.renderer(); + // Look at the inner element of a form control, not the control itself, as it is the editable part. + if (is(element)) { + auto& formControlElement = downcast(element); + if (!formControlElement.isInnerTextElementEditable()) + return { }; + if (auto innerTextElement = formControlElement.innerTextElement()) + renderer = innerTextElement->renderer(); + } + if (!renderer) + return { }; + auto absoluteBoundingBox = renderer->absoluteBoundingBoxRect(); + auto constrainedPoint = point.constrainedBetween(absoluteBoundingBox.minXMinYCorner(), absoluteBoundingBox.maxXMaxYCorner()); + auto visiblePosition = frame->visiblePositionForPoint(constrainedPoint); + return isEditablePosition(visiblePosition.deepEquivalent()) ? visiblePosition : VisiblePosition { }; +} + bool isListHTMLElement(Node* node) { return node && (is(*node) || is(*node) || is(*node)); diff --git a/Source/WebCore/editing/Editing.h b/Source/WebCore/editing/Editing.h index 7f3ac62b37e1..6c0dbd75309c 100644 --- a/Source/WebCore/editing/Editing.h +++ b/Source/WebCore/editing/Editing.h @@ -156,6 +156,8 @@ WEBCORE_EXPORT VisiblePosition visiblePositionForPositionWithOffset(const Visibl WEBCORE_EXPORT VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope); VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node&, int index); // FIXME: Why do we need this version? +WEBCORE_EXPORT VisiblePosition closestEditablePositionInElementForAbsolutePoint(const Element&, const IntPoint&); + // ------------------------------------------------------------------------- // HTMLElement // ------------------------------------------------------------------------- diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog index 0fbbd919e6fe..f9576e5d1fac 100644 --- a/Source/WebKit/ChangeLog +++ b/Source/WebKit/ChangeLog @@ -1,3 +1,17 @@ +2020-04-27 Daniel Bates + + Caret may be placed in the wrong spot for text input context that is a form control + https://bugs.webkit.org/show_bug.cgi?id=210939 + + + Reviewed by Darin Adler. + + Find the closest editable position in the element for the point using the + newly introduced closestEditablePositionInElementForAbsolutePoint(). + + * WebProcess/WebPage/ios/WebPageIOS.mm: + (WebKit::WebPage::focusTextInputContextAndPlaceCaret): + 2020-04-27 Darin Adler Fix ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING) build diff --git a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm index fa73b61a79d6..3937c6454fc0 100644 --- a/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm +++ b/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm @@ -99,7 +99,6 @@ #import #import #import -#import #import #import #import @@ -4350,6 +4349,8 @@ static VisiblePosition visiblePositionForPointInRootViewCoordinates(Frame& frame return; } + // FIXME: Do not focus an element if it moved or the caret point is outside its bounds + // because we only want to do so if the caret can be placed. UserGestureIndicator gestureIndicator { ProcessingUserGesture, &target->document() }; SetForScope userIsInteractingChange { m_userIsInteracting, true }; m_page->focusController().setFocusedElement(target.get(), targetFrame); @@ -4359,9 +4360,13 @@ static VisiblePosition visiblePositionForPointInRootViewCoordinates(Frame& frame completionHandler(false); return; } - // The function visiblePositionInFocusedNodeForPoint constrains the point to be inside - // the bounds of the target element. - auto position = visiblePositionInFocusedNodeForPoint(targetFrame, point, true /* isInteractingWithFocusedElement */); + + ASSERT(targetFrame->view()); + auto position = closestEditablePositionInElementForAbsolutePoint(*target, targetFrame->view()->rootViewToContents(point)); + if (position.isNull()) { + completionHandler(false); + return; + } targetFrame->selection().setSelectedRange(Range::create(*targetFrame->document(), position, position).ptr(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered); completionHandler(true); } diff --git a/Tools/ChangeLog b/Tools/ChangeLog index 9edadfbeb85b..955a23e842eb 100644 --- a/Tools/ChangeLog +++ b/Tools/ChangeLog @@ -1,3 +1,16 @@ +2020-04-27 Daniel Bates + + Caret may be placed in the wrong spot for text input context that is a form control + https://bugs.webkit.org/show_bug.cgi?id=210939 + + + Reviewed by Darin Adler. + + Add a test. + + * TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm: + (TestWebKitAPI::TEST): + 2020-04-27 Alexey Proskuryakov Make run-safari --ios-simulator work again diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm b/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm index e824eb4fdbd9..5586c5a2150d 100644 --- a/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm +++ b/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm @@ -464,6 +464,26 @@ static CGRect squareCenteredAtPoint(float x, float y, float length) EXPECT_EQ(static_cast(exampleTextLength), [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]); } +TEST(RequestTextInputContext, FocusFieldWithPaddingAndPlaceCaretAtEnd) +{ + auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]); + auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]); + + constexpr char exampleText[] = "hello world"; + constexpr size_t exampleTextLength = sizeof(exampleText) - 1; + [webView synchronouslyLoadHTMLString:applyStyle([NSString stringWithFormat:@"", exampleText])]; + NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]]; + EXPECT_EQ(1UL, contexts.count); + RetainPtr<_WKTextInputContext> inputElement = contexts[0]; + + CGRect boundingRect = [inputElement boundingRect]; + CGPoint endPosition = CGPointMake(boundingRect.origin.x + boundingRect.size.width, boundingRect.origin.y); + EXPECT_EQ((UIResponder *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:endPosition]); + EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]); + EXPECT_EQ(static_cast(exampleTextLength), [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]); + EXPECT_EQ(static_cast(exampleTextLength), [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]); +} + TEST(RequestTextInputContext, FocusFieldAndPlaceCaretOutsideField) { auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);