Title: [260214] trunk
Revision
260214
Author
[email protected]
Date
2020-04-16 12:48:02 -0700 (Thu, 16 Apr 2020)

Log Message

[iOS] Add a way to focus a text input and place a caret
https://bugs.webkit.org/show_bug.cgi?id=210611
<rdar://problem/61893062>

Reviewed by Darin Adler.

Source/WebKit:

Add some IPI that will be used by code in WebKitAdditions to focus a text input context
and place the caret in it. This will replace the existing -focusTextInput SPI, which I
will remove in a subsequent commit.

* UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h:
* UIProcess/API/ios/WKWebViewTestingIOS.mm:
(-[WKWebView _requestTextInputContextsInRect:completionHandler:]): Fix up code style
of signature while I am here.
(-[WKWebView _focusTextInputContext:placeCaretAt:completionHandler:]): Added.
* UIProcess/WebPageProxy.h:
* UIProcess/ios/WKContentViewInteraction.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView _isTextInputContextFocused:]): Added.
(-[WKContentView _focusTextInputContext:placeCaretAt:completionHandler:]): Added.
(-[WKContentView _requestTextInputContextsInRect:completionHandler:]): Fix up code style
of signature while I am here.
* UIProcess/ios/WebPageProxyIOS.mm:
(WebKit::WebPageProxy::focusTextInputContextAndPlaceCaret):
* WebProcess/WebPage/WebPage.cpp:
(WebKit::WebPage::focusTextInputContext): Use auto now that elementForContext() returns a RefPtr.
(WebKit::WebPage::elementForContext const): Have it return a RefPtr instead of a raw
pointer so callers don't have to remember to take out a ref of otherwise be mindful
of the element's lifetime.
* WebProcess/WebPage/WebPage.h:
* WebProcess/WebPage/WebPage.messages.in:
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::removeTextPlaceholder): Use auto now that elementForContext() returns a RefPtr.
(WebKit::WebPage::requestDocumentEditingContext): Ditto.
(WebKit::WebPage::focusTextInputContextAndPlaceCaret): Added.

Tools:

Add some tests.

* TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm:
(-[TestWKWebView synchronouslyFocusTextInputContext:placeCaretAt:]): Added.
(webViewLoadHTMLStringAndWaitForAllFramesToPaint): Use the bundle's TestWebKitAPI.resources directory
as the base URL so that we have a valid file URL. Some of the tests will then
call -_setAllowUniversalAccessFromFileURLs to allow the main frame access to
the unique-origin child frame contents.
(TEST):

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (260213 => 260214)


--- trunk/Source/WebKit/ChangeLog	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/ChangeLog	2020-04-16 19:48:02 UTC (rev 260214)
@@ -1,3 +1,41 @@
+2020-04-16  Daniel Bates  <[email protected]>
+
+        [iOS] Add a way to focus a text input and place a caret
+        https://bugs.webkit.org/show_bug.cgi?id=210611
+        <rdar://problem/61893062>
+
+        Reviewed by Darin Adler.
+
+        Add some IPI that will be used by code in WebKitAdditions to focus a text input context
+        and place the caret in it. This will replace the existing -focusTextInput SPI, which I
+        will remove in a subsequent commit.
+
+        * UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h:
+        * UIProcess/API/ios/WKWebViewTestingIOS.mm:
+        (-[WKWebView _requestTextInputContextsInRect:completionHandler:]): Fix up code style
+        of signature while I am here.
+        (-[WKWebView _focusTextInputContext:placeCaretAt:completionHandler:]): Added.
+        * UIProcess/WebPageProxy.h:
+        * UIProcess/ios/WKContentViewInteraction.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView _isTextInputContextFocused:]): Added.
+        (-[WKContentView _focusTextInputContext:placeCaretAt:completionHandler:]): Added.
+        (-[WKContentView _requestTextInputContextsInRect:completionHandler:]): Fix up code style
+        of signature while I am here.
+        * UIProcess/ios/WebPageProxyIOS.mm:
+        (WebKit::WebPageProxy::focusTextInputContextAndPlaceCaret):
+        * WebProcess/WebPage/WebPage.cpp:
+        (WebKit::WebPage::focusTextInputContext): Use auto now that elementForContext() returns a RefPtr.
+        (WebKit::WebPage::elementForContext const): Have it return a RefPtr instead of a raw
+        pointer so callers don't have to remember to take out a ref of otherwise be mindful
+        of the element's lifetime.
+        * WebProcess/WebPage/WebPage.h:
+        * WebProcess/WebPage/WebPage.messages.in:
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::removeTextPlaceholder): Use auto now that elementForContext() returns a RefPtr.
+        (WebKit::WebPage::requestDocumentEditingContext): Ditto.
+        (WebKit::WebPage::focusTextInputContextAndPlaceCaret): Added.
+
 2020-04-16  Chris Dumez  <[email protected]>
 
         Use safeRoundPage() instead of round_page() in SharedMemory

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h (260213 => 260214)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h	2020-04-16 19:48:02 UTC (rev 260214)
@@ -49,7 +49,8 @@
 - (void)selectFormAccessoryPickerRow:(int)rowIndex;
 
 - (BOOL)_mayContainEditableElementsInRect:(CGRect)rect;
-- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void(^)(NSArray<_WKTextInputContext *> *))completionHandler;
+- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler;
+- (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler;
 - (void)_requestDocumentContext:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler;
 - (void)_adjustSelectionWithDelta:(NSRange)deltaRange completionHandler:(void (^)(void))completionHandler;
 

Modified: trunk/Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm (260213 => 260214)


--- trunk/Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm	2020-04-16 19:48:02 UTC (rev 260214)
@@ -42,7 +42,7 @@
 
 @implementation WKWebView (WKTestingIOS)
 
-- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void(^)(NSArray<_WKTextInputContext *> *))completionHandler
+- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler
 {
     // Adjust returned bounding rects to be in WKWebView coordinates.
     auto adjustedRect = [self convertRect:rect toView:_contentView.get()];
@@ -62,6 +62,12 @@
     }];
 }
 
+- (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler
+{
+    auto adjustedPoint = [self convertPoint:point toView:_contentView.get()];
+    [_contentView _focusTextInputContext:context placeCaretAt:adjustedPoint completionHandler:completionHandler];
+}
+
 - (void)_requestDocumentContext:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler
 {
     [_contentView requestDocumentContext:request completionHandler:completionHandler];

Modified: trunk/Source/WebKit/UIProcess/WebPageProxy.h (260213 => 260214)


--- trunk/Source/WebKit/UIProcess/WebPageProxy.h	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/UIProcess/WebPageProxy.h	2020-04-16 19:48:02 UTC (rev 260214)
@@ -713,6 +713,8 @@
 #endif
 
 #if PLATFORM(IOS_FAMILY)
+    void focusTextInputContextAndPlaceCaret(const WebCore::ElementContext&, const WebCore::IntPoint&, CompletionHandler<void(bool)>&&);
+
     void setShouldRevealCurrentSelectionAfterInsertion(bool);
 
     void insertTextPlaceholder(const WebCore::IntSize&, CompletionHandler<void(const Optional<WebCore::ElementContext>&)>&&);

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (260213 => 260214)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h	2020-04-16 19:48:02 UTC (rev 260214)
@@ -567,7 +567,8 @@
 - (WKDrawingCoordinator *)_drawingCoordinator;
 #endif
 
-- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void(^)(NSArray<_WKTextInputContext *> *))completionHandler;
+- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler;
+- (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler;
 
 #if USE(TEXT_INTERACTION_ADDITIONS)
 - (void)_willBeginTextInteractionInTextInputContext:(_WKTextInputContext *)context;

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (260213 => 260214)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2020-04-16 19:48:02 UTC (rev 260214)
@@ -5078,8 +5078,41 @@
     return CGRectZero;
 }
 
-- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void(^)(NSArray<_WKTextInputContext *> *))completionHandler
+- (BOOL)_isTextInputContextFocused:(_WKTextInputContext *)context
 {
+    ASSERT(context);
+    // We ignore bounding rect changes as the bounding rect of the focused element is not kept up-to-date.
+    return self._hasFocusedElement && context._textInputContext.isSameElement(_focusedElementInformation.elementContext);
+}
+
+- (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler
+{
+    ASSERT(context);
+    if (![self becomeFirstResponder]) {
+        completionHandler(nil);
+        return;
+    }
+    if ([self _isTextInputContextFocused:context]) {
+        completionHandler(_focusedElementInformation.isReadOnly ? nil : self);
+        return;
+    }
+    _usingGestureForSelection = YES;
+    auto checkFocusedElement = [weakSelf = WeakObjCPtr<WKContentView> { self }, context = [context copy], completionHandler = makeBlockPtr(completionHandler)] (bool success) {
+        auto strongSelf = weakSelf.get();
+        if (strongSelf)
+            strongSelf->_usingGestureForSelection = NO;
+        if (!strongSelf || !success) {
+            completionHandler(nil);
+            return;
+        }
+        bool focusedAndEditable = [strongSelf _isTextInputContextFocused:context] && !strongSelf->_focusedElementInformation.isReadOnly;
+        completionHandler(focusedAndEditable ? strongSelf.autorelease() : nil);
+    };
+    _page->focusTextInputContextAndPlaceCaret(context._textInputContext, WebCore::IntPoint { point }, WTFMove(checkFocusedElement));
+}
+
+- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler
+{
 #if ENABLE(EDITABLE_REGION)
     if (!self.webView._editable && !WebKit::mayContainEditableElementsInRect(self, rect)) {
         completionHandler(@[ ]);

Modified: trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm (260213 => 260214)


--- trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm	2020-04-16 19:48:02 UTC (rev 260214)
@@ -1539,7 +1539,16 @@
     return pageClient().isApplicationVisible();
 }
 
+void WebPageProxy::focusTextInputContextAndPlaceCaret(const ElementContext& context, const IntPoint& point, CompletionHandler<void(bool)>&& completionHandler)
+{
+    if (!hasRunningProcess()) {
+        completionHandler(false);
+        return;
+    }
 
+    sendWithAsyncReply(Messages::WebPage::FocusTextInputContextAndPlaceCaret(context, point), WTFMove(completionHandler));
+}
+
 void WebPageProxy::setShouldRevealCurrentSelectionAfterInsertion(bool shouldRevealCurrentSelectionAfterInsertion)
 {
     if (hasRunningProcess())

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp (260213 => 260214)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp	2020-04-16 19:48:02 UTC (rev 260214)
@@ -6913,7 +6913,7 @@
 
 void WebPage::focusTextInputContext(const WebCore::ElementContext& textInputContext, CompletionHandler<void(bool)>&& completionHandler)
 {
-    RefPtr<Element> element = elementForContext(textInputContext);
+    auto element = elementForContext(textInputContext);
 
     if (element)
         element->focus();
@@ -6928,7 +6928,7 @@
         downcast<HTMLTextFormControlElement>(*element).setCanShowPlaceholder(canShowPlaceholder);
 }
 
-Element* WebPage::elementForContext(const WebCore::ElementContext& elementContext) const
+RefPtr<Element> WebPage::elementForContext(const ElementContext& elementContext) const
 {
     if (elementContext.webPageIdentifier != m_identifier)
         return nullptr;

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.h (260213 => 260214)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.h	2020-04-16 19:48:02 UTC (rev 260214)
@@ -647,6 +647,8 @@
     void setCanShowPlaceholder(const WebCore::ElementContext&, bool);
 
 #if PLATFORM(IOS_FAMILY)
+    void focusTextInputContextAndPlaceCaret(const WebCore::ElementContext&, const WebCore::IntPoint&, CompletionHandler<void(bool)>&&);
+
     bool shouldRevealCurrentSelectionAfterInsertion() const { return m_shouldRevealCurrentSelectionAfterInsertion; }
     void setShouldRevealCurrentSelectionAfterInsertion(bool);
 
@@ -1250,7 +1252,7 @@
 
     void configureLoggingChannel(const String&, WTFLogChannelState, WTFLogLevel);
 
-    WebCore::Element* elementForContext(const WebCore::ElementContext&) const;
+    RefPtr<WebCore::Element> elementForContext(const WebCore::ElementContext&) const;
     Optional<WebCore::ElementContext> contextForElement(WebCore::Element&) const;
 
     void startTextManipulations(Vector<WebCore::TextManipulationController::ExclusionRule>&&, CompletionHandler<void()>&&);

Modified: trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in (260213 => 260214)


--- trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in	2020-04-16 19:48:02 UTC (rev 260214)
@@ -121,6 +121,7 @@
     SetShouldRevealCurrentSelectionAfterInsertion(bool shouldRevealCurrentSelectionAfterInsertion)
     InsertTextPlaceholder(WebCore::IntSize size) -> (Optional<WebCore::ElementContext> placeholder) Async
     RemoveTextPlaceholder(struct WebCore::ElementContext placeholder) -> () Async
+    FocusTextInputContextAndPlaceCaret(struct WebCore::ElementContext context, WebCore::IntPoint point) -> (bool success) Async
 #endif
 
     SetControlledByAutomation(bool controlled)

Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (260213 => 260214)


--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2020-04-16 19:48:02 UTC (rev 260214)
@@ -129,6 +129,7 @@
 #import <WebCore/TextIterator.h>
 #import <WebCore/TextPlaceholderElement.h>
 #import <WebCore/UserAgent.h>
+#import <WebCore/UserGestureIndicator.h>
 #import <WebCore/VisibleUnits.h>
 #import <WebCore/WebEvent.h>
 #import <wtf/MathExtras.h>
@@ -4045,9 +4046,9 @@
     completionHandler(placeholder ? contextForElement(*placeholder) : WTF::nullopt);
 }
 
-void WebPage::removeTextPlaceholder(const WebCore::ElementContext& placeholder, CompletionHandler<void()>&& completionHandler)
+void WebPage::removeTextPlaceholder(const ElementContext& placeholder, CompletionHandler<void()>&& completionHandler)
 {
-    if (RefPtr<Element> element = elementForContext(placeholder)) {
+    if (auto element = elementForContext(placeholder)) {
         RELEASE_ASSERT(is<TextPlaceholderElement>(element));
         if (RefPtr<Frame> frame = element->document().frame())
             frame->editor().removeTextPlaceholder(downcast<TextPlaceholderElement>(*element));
@@ -4140,7 +4141,7 @@
     bool wantsMarkedTextRects = request.options.contains(DocumentEditingContextRequest::Options::MarkedTextRects);
 
     if (auto textInputContext = request.textInputContext) {
-        RefPtr<Element> element = elementForContext(*textInputContext);
+        auto element = elementForContext(*textInputContext);
         if (!element) {
             completionHandler({ });
             return;
@@ -4296,6 +4297,44 @@
     scheduleFullEditorStateUpdate();
 }
 
+void WebPage::focusTextInputContextAndPlaceCaret(const ElementContext& elementContext, const IntPoint& point, CompletionHandler<void(bool)>&& completionHandler)
+{
+    auto target = elementForContext(elementContext);
+    if (!target) {
+        completionHandler(false);
+        return;
+    }
+
+    ASSERT(target->document().frame());
+    auto targetFrame = makeRef(*target->document().frame());
+
+    targetFrame->document()->updateLayoutIgnorePendingStylesheets();
+
+    // Performing layout could have could torn down the element's renderer. Check that we still
+    // have one. Otherwise, bail out as this function only focuses elements that have a visual
+    // representation.
+    if (!target->renderer()) {
+        completionHandler(false);
+        return;
+    }
+
+    UserGestureIndicator gestureIndicator { ProcessingUserGesture, &target->document() };
+    SetForScope<bool> userIsInteractingChange { m_userIsInteracting, true };
+    bool didFocus = m_page->focusController().setFocusedElement(target.get(), targetFrame);
+
+    // Setting the focused element could tear down the element's renderer. Check that we still have one.
+    if (!didFocus || !target->renderer()) {
+        completionHandler(false);
+        return;
+    }
+    ASSERT(m_focusedElement == target);
+    // The function visiblePositionInFocusedNodeForPoint constrains the point to be inside
+    // the bounds of the target element.
+    auto position = visiblePositionInFocusedNodeForPoint(targetFrame, point, true /* isInteractingWithFocusedElement */);
+    targetFrame->selection().setSelectedRange(Range::create(*targetFrame->document(), position, position).ptr(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
+    completionHandler(true);
+}
+
 void WebPage::platformDidScalePage()
 {
     auto transactionID = downcast<RemoteLayerTreeDrawingArea>(*m_drawingArea).lastCommittedTransactionID();

Modified: trunk/Tools/ChangeLog (260213 => 260214)


--- trunk/Tools/ChangeLog	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Tools/ChangeLog	2020-04-16 19:48:02 UTC (rev 260214)
@@ -1,5 +1,23 @@
 2020-04-16  Daniel Bates  <[email protected]>
 
+        [iOS] Add a way to focus a text input and place a caret
+        https://bugs.webkit.org/show_bug.cgi?id=210611
+        <rdar://problem/61893062>
+
+        Reviewed by Darin Adler.
+
+        Add some tests.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm:
+        (-[TestWKWebView synchronouslyFocusTextInputContext:placeCaretAt:]): Added.
+        (webViewLoadHTMLStringAndWaitForAllFramesToPaint): Use the bundle's TestWebKitAPI.resources directory
+        as the base URL so that we have a valid file URL. Some of the tests will then
+        call -_setAllowUniversalAccessFromFileURLs to allow the main frame access to
+        the unique-origin child frame contents.
+        (TEST):
+
+2020-04-16  Daniel Bates  <[email protected]>
+
         REGRESSION (r259762): Should always hit test for editable elements if the WKWebView is fully editable
         https://bugs.webkit.org/show_bug.cgi?id=210558
         <rdar://problem/61798347>

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm (260213 => 260214)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm	2020-04-16 19:44:10 UTC (rev 260213)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm	2020-04-16 19:48:02 UTC (rev 260214)
@@ -32,6 +32,7 @@
 #import "TestNavigationDelegate.h"
 #import "TestWKWebView.h"
 #import <WebKit/WKPreferencesRefPrivate.h>
+#import <WebKit/WKWebViewConfigurationPrivate.h>
 #import <WebKit/WKWebViewPrivateForTesting.h>
 #import <WebKit/_WKTextInputContext.h>
 #import <wtf/RetainPtr.h>
@@ -62,6 +63,18 @@
     return success;
 }
 
+- (UIResponder<UITextInput> *)synchronouslyFocusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point
+{
+    __block bool finished = false;
+    __block UIResponder<UITextInput> *responder = nil;
+    [self _focusTextInputContext:context placeCaretAt:point completionHandler:^(UIResponder<UITextInput> *textInputResponder) {
+        responder = textInputResponder;
+        finished = true;
+    }];
+    TestWebKitAPI::Util::run(&finished);
+    return responder;
+}
+
 @end
 
 static NSString *applyStyle(NSString *HTMLString)
@@ -157,7 +170,7 @@
     ASSERT(webView); // Make passing a nil web view a more obvious failure than a hang.
     bool didFireDOMLoadEvent = false;
     [webView performAfterLoading:[&] { didFireDOMLoadEvent = true; }];
-    [webView loadHTMLString:htmlString baseURL:nil];
+    [webView loadHTMLString:htmlString baseURL:[NSBundle.mainBundle.bundleURL URLByAppendingPathComponent:@"TestWebKitAPI.resources"]];
     TestWebKitAPI::Util::run(&didFireDOMLoadEvent);
     [webView waitForNextPresentationUpdate];
 }
@@ -274,4 +287,136 @@
     EXPECT_FALSE([webView synchronouslyFocusTextInputContext:textArea.get()]);
 }
 
+TEST(RequestTextInputContext, ReadOnlyField)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' value='hello world' style='width: 100px; height: 50px;' readonly>")];
+    EXPECT_EQ(0UL, [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]].count);
+}
+
+TEST(RequestTextInputContext, DisabledField)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' value='hello world' style='width: 100px; height: 50px;' disabled>")];
+    EXPECT_EQ(0UL, [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]].count);
+}
+
+TEST(RequestTextInputContext, FocusFieldAndPlaceCaretAtStart)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' value='hello world' style='width: 100px; height: 50px;'>")];
+    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
+    EXPECT_EQ(1UL, contexts.count);
+    RetainPtr<_WKTextInputContext> inputElement = contexts[0];
+
+    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:[inputElement boundingRect].origin]);
+    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
+    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]);
+    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]);
+}
+
+TEST(RequestTextInputContext, FocusFieldAndPlaceCaretAtEnd)
+{
+    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;'>", 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 + boundingRect.size.height / 2);
+    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]);
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    constexpr char exampleText[] = "hello world";
+    [webView synchronouslyLoadHTMLString:applyStyle([NSString stringWithFormat:@"<input type='text' value='%s' style='width: 100px; height: 50px;'>", exampleText])];
+    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
+    EXPECT_EQ(1UL, contexts.count);
+    RetainPtr<_WKTextInputContext> inputElement = contexts[0];
+
+    auto resetTest = [&] {
+        [webView stringByEvaluatingJavaScript:@"document.activeElement.blur()"];
+        EXPECT_WK_STREQ("BODY", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
+    };
+
+    // Point before the field
+    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:CGPointMake(-1000, -500)]);
+    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
+    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]);
+    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]);
+    resetTest();
+
+    // Point after the field
+    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:CGPointMake(1000, 500)]);
+    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
+    EXPECT_EQ(11, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]);
+    EXPECT_EQ(11, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]);
+    resetTest();
+}
+
+TEST(RequestTextInputContext, FocusFieldInFrame)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [configuration _setAllowUniversalAccessFromFileURLs:YES];
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    auto testPage = applyStyle([NSString stringWithFormat:@"<input type='text' value='mainFrameField' style='width: 100px; height: 50px;'>%@", applyIframe(@"<input type='text' value='iframeField' style='width: 120px; height: 70px;'>")]);
+    webViewLoadHTMLStringAndWaitForAllFramesToPaint(webView.get(), testPage);
+    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
+    EXPECT_EQ(2UL, contexts.count);
+    RetainPtr<_WKTextInputContext> frameBField = contexts[0];
+
+    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:frameBField.get() placeCaretAt:[frameBField boundingRect].origin]);
+    EXPECT_WK_STREQ("IFRAME", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
+    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.tagName"]);
+    EXPECT_WK_STREQ("iframeField", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.value"]);
+    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.selectionStart"] intValue]);
+    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.selectionEnd"] intValue]);
+}
+
+TEST(RequestTextInputContext, SwitchFocusBetweenFrames)
+{
+    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
+    [configuration _setAllowUniversalAccessFromFileURLs:YES];
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
+
+    auto testPage = applyStyle([NSString stringWithFormat:@"<input type='text' value='mainFrameField' style='width: 100px; height: 50px;'>%@", applyIframe(@"<input type='text' value='iframeField' style='width: 100px; height: 50px;'>")]);
+    webViewLoadHTMLStringAndWaitForAllFramesToPaint(webView.get(), testPage);
+    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
+    EXPECT_EQ(2UL, contexts.count);
+    // Note that returned contexts are in hit-test order.
+    RetainPtr<_WKTextInputContext> frameAField = contexts[1];
+    RetainPtr<_WKTextInputContext> frameBField = contexts[0];
+
+    EXPECT_WK_STREQ("BODY", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
+    EXPECT_WK_STREQ("BODY", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.tagName"]);
+    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:frameAField.get() placeCaretAt:[frameAField boundingRect].origin]);
+    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
+    EXPECT_WK_STREQ("mainFrameField", [webView stringByEvaluatingJavaScript:@"document.activeElement.value"]);
+    EXPECT_WK_STREQ("BODY", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.tagName"]);
+
+    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:frameBField.get() placeCaretAt:[frameBField boundingRect].origin]);
+    EXPECT_WK_STREQ("IFRAME", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
+    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.tagName"]);
+    EXPECT_WK_STREQ("iframeField", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.value"]);
+}
+
 #endif // PLATFORM(IOS_FAMILY)
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to