Skip to content

Commit

Permalink
Caret may be placed in the wrong spot for text input context that is …
Browse files Browse the repository at this point in the history
…a form control

https://bugs.webkit.org/show_bug.cgi?id=210939
<rdar://problem/61943089>

Reviewed by Darin Adler.

Source/WebCore:

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:

Source/WebKit:

Find the closest editable position in the element for the point using the
newly introduced closestEditablePositionInElementForAbsolutePoint().

* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::focusTextInputContextAndPlaceCaret):

Tools:

Add a test.

* TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm:
(TestWebKitAPI::TEST):

Canonical link: https://commits.webkit.org/223960@main
git-svn-id: https://svn.webkit.org/repository/webkit/trunk@260759 268f45cc-cd09-0410-ab3c-d52691b4dbfc
  • Loading branch information
dydz committed Apr 27, 2020
1 parent 18a78c9 commit cf98e83
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 4 deletions.
15 changes: 15 additions & 0 deletions Source/WebCore/ChangeLog
@@ -1,3 +1,18 @@
2020-04-27 Daniel Bates <dabates@apple.com>

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
<rdar://problem/61943089>

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 <aboya@igalia.com>

[GStreamer] Rework WebKitWebSrc threading
Expand Down
29 changes: 29 additions & 0 deletions Source/WebCore/editing/Editing.cpp
Expand Up @@ -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 <wtf/Assertions.h>
Expand Down Expand Up @@ -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<const Element> 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<HTMLTextFormControlElement>(element)) {
auto& formControlElement = downcast<HTMLTextFormControlElement>(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<HTMLUListElement>(*node) || is<HTMLOListElement>(*node) || is<HTMLDListElement>(*node));
Expand Down
2 changes: 2 additions & 0 deletions Source/WebCore/editing/Editing.h
Expand Up @@ -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
// -------------------------------------------------------------------------
Expand Down
14 changes: 14 additions & 0 deletions Source/WebKit/ChangeLog
@@ -1,3 +1,17 @@
2020-04-27 Daniel Bates <dabates@apple.com>

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
<rdar://problem/61943089>

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 <darin@apple.com>

Fix ENABLE(PLATFORM_DRIVEN_TEXT_CHECKING) build
Expand Down
13 changes: 9 additions & 4 deletions Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm
Expand Up @@ -99,7 +99,6 @@
#import <WebCore/HTMLSelectElement.h>
#import <WebCore/HTMLSummaryElement.h>
#import <WebCore/HTMLTextAreaElement.h>
#import <WebCore/HTMLTextFormControlElement.h>
#import <WebCore/HistoryItem.h>
#import <WebCore/HitTestResult.h>
#import <WebCore/InputMode.h>
Expand Down Expand Up @@ -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<bool> userIsInteractingChange { m_userIsInteracting, true };
m_page->focusController().setFocusedElement(target.get(), targetFrame);
Expand All @@ -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);
}
Expand Down
13 changes: 13 additions & 0 deletions Tools/ChangeLog
@@ -1,3 +1,16 @@
2020-04-27 Daniel Bates <dabates@apple.com>

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
<rdar://problem/61943089>

Reviewed by Darin Adler.

Add a test.

* TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm:
(TestWebKitAPI::TEST):

2020-04-27 Alexey Proskuryakov <ap@apple.com>

Make run-safari --ios-simulator work again
Expand Down
20 changes: 20 additions & 0 deletions Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm
Expand Up @@ -464,6 +464,26 @@ static CGRect squareCenteredAtPoint(float x, float y, float length)
EXPECT_EQ(static_cast<int>(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:@"<input type='text' value='%s' style='width: 100px; height: 50px; padding: 20px'>", 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<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:endPosition]);
EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
EXPECT_EQ(static_cast<int>(exampleTextLength), [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]);
EXPECT_EQ(static_cast<int>(exampleTextLength), [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]);
}

TEST(RequestTextInputContext, FocusFieldAndPlaceCaretOutsideField)
{
auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
Expand Down

0 comments on commit cf98e83

Please sign in to comment.