Changeset 260214 in webkit


Ignore:
Timestamp:
Apr 16, 2020, 12:48:02 PM (6 years ago)
Author:
dbates@webkit.org
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):

Location:
trunk
Files:
13 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/WebKit/ChangeLog

    r260200 r260214  
     12020-04-16  Daniel Bates  <dabates@apple.com>
     2
     3        [iOS] Add a way to focus a text input and place a caret
     4        https://bugs.webkit.org/show_bug.cgi?id=210611
     5        <rdar://problem/61893062>
     6
     7        Reviewed by Darin Adler.
     8
     9        Add some IPI that will be used by code in WebKitAdditions to focus a text input context
     10        and place the caret in it. This will replace the existing -focusTextInput SPI, which I
     11        will remove in a subsequent commit.
     12
     13        * UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h:
     14        * UIProcess/API/ios/WKWebViewTestingIOS.mm:
     15        (-[WKWebView _requestTextInputContextsInRect:completionHandler:]): Fix up code style
     16        of signature while I am here.
     17        (-[WKWebView _focusTextInputContext:placeCaretAt:completionHandler:]): Added.
     18        * UIProcess/WebPageProxy.h:
     19        * UIProcess/ios/WKContentViewInteraction.h:
     20        * UIProcess/ios/WKContentViewInteraction.mm:
     21        (-[WKContentView _isTextInputContextFocused:]): Added.
     22        (-[WKContentView _focusTextInputContext:placeCaretAt:completionHandler:]): Added.
     23        (-[WKContentView _requestTextInputContextsInRect:completionHandler:]): Fix up code style
     24        of signature while I am here.
     25        * UIProcess/ios/WebPageProxyIOS.mm:
     26        (WebKit::WebPageProxy::focusTextInputContextAndPlaceCaret):
     27        * WebProcess/WebPage/WebPage.cpp:
     28        (WebKit::WebPage::focusTextInputContext): Use auto now that elementForContext() returns a RefPtr.
     29        (WebKit::WebPage::elementForContext const): Have it return a RefPtr instead of a raw
     30        pointer so callers don't have to remember to take out a ref of otherwise be mindful
     31        of the element's lifetime.
     32        * WebProcess/WebPage/WebPage.h:
     33        * WebProcess/WebPage/WebPage.messages.in:
     34        * WebProcess/WebPage/ios/WebPageIOS.mm:
     35        (WebKit::WebPage::removeTextPlaceholder): Use auto now that elementForContext() returns a RefPtr.
     36        (WebKit::WebPage::requestDocumentEditingContext): Ditto.
     37        (WebKit::WebPage::focusTextInputContextAndPlaceCaret): Added.
     38
    1392020-04-16  Chris Dumez  <cdumez@apple.com>
    240
  • trunk/Source/WebKit/UIProcess/API/ios/WKWebViewPrivateForTestingIOS.h

    r260192 r260214  
    5050
    5151- (BOOL)_mayContainEditableElementsInRect:(CGRect)rect;
    52 - (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void(^)(NSArray<_WKTextInputContext *> *))completionHandler;
     52- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler;
     53- (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler;
    5354- (void)_requestDocumentContext:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler;
    5455- (void)_adjustSelectionWithDelta:(NSRange)deltaRange completionHandler:(void (^)(void))completionHandler;
  • trunk/Source/WebKit/UIProcess/API/ios/WKWebViewTestingIOS.mm

    r260192 r260214  
    4343@implementation WKWebView (WKTestingIOS)
    4444
    45 - (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void(^)(NSArray<_WKTextInputContext *> *))completionHandler
     45- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler
    4646{
    4747    // Adjust returned bounding rects to be in WKWebView coordinates.
     
    6363}
    6464
     65- (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler
     66{
     67    auto adjustedPoint = [self convertPoint:point toView:_contentView.get()];
     68    [_contentView _focusTextInputContext:context placeCaretAt:adjustedPoint completionHandler:completionHandler];
     69}
     70
    6571- (void)_requestDocumentContext:(UIWKDocumentRequest *)request completionHandler:(void (^)(UIWKDocumentContext *))completionHandler
    6672{
  • trunk/Source/WebKit/UIProcess/WebPageProxy.h

    r260182 r260214  
    714714
    715715#if PLATFORM(IOS_FAMILY)
     716    void focusTextInputContextAndPlaceCaret(const WebCore::ElementContext&, const WebCore::IntPoint&, CompletionHandler<void(bool)>&&);
     717
    716718    void setShouldRevealCurrentSelectionAfterInsertion(bool);
    717719
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h

    r260192 r260214  
    568568#endif
    569569
    570 - (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void(^)(NSArray<_WKTextInputContext *> *))completionHandler;
     570- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler;
     571- (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler;
    571572
    572573#if USE(TEXT_INTERACTION_ADDITIONS)
  • trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm

    r260193 r260214  
    50795079}
    50805080
    5081 - (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void(^)(NSArray<_WKTextInputContext *> *))completionHandler
     5081- (BOOL)_isTextInputContextFocused:(_WKTextInputContext *)context
     5082{
     5083    ASSERT(context);
     5084    // We ignore bounding rect changes as the bounding rect of the focused element is not kept up-to-date.
     5085    return self._hasFocusedElement && context._textInputContext.isSameElement(_focusedElementInformation.elementContext);
     5086}
     5087
     5088- (void)_focusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point completionHandler:(void (^)(UIResponder<UITextInput> *))completionHandler
     5089{
     5090    ASSERT(context);
     5091    if (![self becomeFirstResponder]) {
     5092        completionHandler(nil);
     5093        return;
     5094    }
     5095    if ([self _isTextInputContextFocused:context]) {
     5096        completionHandler(_focusedElementInformation.isReadOnly ? nil : self);
     5097        return;
     5098    }
     5099    _usingGestureForSelection = YES;
     5100    auto checkFocusedElement = [weakSelf = WeakObjCPtr<WKContentView> { self }, context = [context copy], completionHandler = makeBlockPtr(completionHandler)] (bool success) {
     5101        auto strongSelf = weakSelf.get();
     5102        if (strongSelf)
     5103            strongSelf->_usingGestureForSelection = NO;
     5104        if (!strongSelf || !success) {
     5105            completionHandler(nil);
     5106            return;
     5107        }
     5108        bool focusedAndEditable = [strongSelf _isTextInputContextFocused:context] && !strongSelf->_focusedElementInformation.isReadOnly;
     5109        completionHandler(focusedAndEditable ? strongSelf.autorelease() : nil);
     5110    };
     5111    _page->focusTextInputContextAndPlaceCaret(context._textInputContext, WebCore::IntPoint { point }, WTFMove(checkFocusedElement));
     5112}
     5113
     5114- (void)_requestTextInputContextsInRect:(CGRect)rect completionHandler:(void (^)(NSArray<_WKTextInputContext *> *))completionHandler
    50825115{
    50835116#if ENABLE(EDITABLE_REGION)
  • trunk/Source/WebKit/UIProcess/ios/WebPageProxyIOS.mm

    r260037 r260214  
    15401540}
    15411541
     1542void WebPageProxy::focusTextInputContextAndPlaceCaret(const ElementContext& context, const IntPoint& point, CompletionHandler<void(bool)>&& completionHandler)
     1543{
     1544    if (!hasRunningProcess()) {
     1545        completionHandler(false);
     1546        return;
     1547    }
     1548
     1549    sendWithAsyncReply(Messages::WebPage::FocusTextInputContextAndPlaceCaret(context, point), WTFMove(completionHandler));
     1550}
    15421551
    15431552void WebPageProxy::setShouldRevealCurrentSelectionAfterInsertion(bool shouldRevealCurrentSelectionAfterInsertion)
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.cpp

    r260182 r260214  
    69146914void WebPage::focusTextInputContext(const WebCore::ElementContext& textInputContext, CompletionHandler<void(bool)>&& completionHandler)
    69156915{
    6916     RefPtr<Element> element = elementForContext(textInputContext);
     6916    auto element = elementForContext(textInputContext);
    69176917
    69186918    if (element)
     
    69296929}
    69306930
    6931 Element* WebPage::elementForContext(const WebCore::ElementContext& elementContext) const
     6931RefPtr<Element> WebPage::elementForContext(const ElementContext& elementContext) const
    69326932{
    69336933    if (elementContext.webPageIdentifier != m_identifier)
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.h

    r260182 r260214  
    648648
    649649#if PLATFORM(IOS_FAMILY)
     650    void focusTextInputContextAndPlaceCaret(const WebCore::ElementContext&, const WebCore::IntPoint&, CompletionHandler<void(bool)>&&);
     651
    650652    bool shouldRevealCurrentSelectionAfterInsertion() const { return m_shouldRevealCurrentSelectionAfterInsertion; }
    651653    void setShouldRevealCurrentSelectionAfterInsertion(bool);
     
    12511253    void configureLoggingChannel(const String&, WTFLogChannelState, WTFLogLevel);
    12521254
    1253     WebCore::Element* elementForContext(const WebCore::ElementContext&) const;
     1255    RefPtr<WebCore::Element> elementForContext(const WebCore::ElementContext&) const;
    12541256    Optional<WebCore::ElementContext> contextForElement(WebCore::Element&) const;
    12551257
  • trunk/Source/WebKit/WebProcess/WebPage/WebPage.messages.in

    r260150 r260214  
    122122    InsertTextPlaceholder(WebCore::IntSize size) -> (Optional<WebCore::ElementContext> placeholder) Async
    123123    RemoveTextPlaceholder(struct WebCore::ElementContext placeholder) -> () Async
     124    FocusTextInputContextAndPlaceCaret(struct WebCore::ElementContext context, WebCore::IntPoint point) -> (bool success) Async
    124125#endif
    125126
  • trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm

    r260147 r260214  
    130130#import <WebCore/TextPlaceholderElement.h>
    131131#import <WebCore/UserAgent.h>
     132#import <WebCore/UserGestureIndicator.h>
    132133#import <WebCore/VisibleUnits.h>
    133134#import <WebCore/WebEvent.h>
     
    40464047}
    40474048
    4048 void WebPage::removeTextPlaceholder(const WebCore::ElementContext& placeholder, CompletionHandler<void()>&& completionHandler)
    4049 {
    4050     if (RefPtr<Element> element = elementForContext(placeholder)) {
     4049void WebPage::removeTextPlaceholder(const ElementContext& placeholder, CompletionHandler<void()>&& completionHandler)
     4050{
     4051    if (auto element = elementForContext(placeholder)) {
    40514052        RELEASE_ASSERT(is<TextPlaceholderElement>(element));
    40524053        if (RefPtr<Frame> frame = element->document().frame())
     
    41414142
    41424143    if (auto textInputContext = request.textInputContext) {
    4143         RefPtr<Element> element = elementForContext(*textInputContext);
     4144        auto element = elementForContext(*textInputContext);
    41444145        if (!element) {
    41454146            completionHandler({ });
     
    42974298}
    42984299
     4300void WebPage::focusTextInputContextAndPlaceCaret(const ElementContext& elementContext, const IntPoint& point, CompletionHandler<void(bool)>&& completionHandler)
     4301{
     4302    auto target = elementForContext(elementContext);
     4303    if (!target) {
     4304        completionHandler(false);
     4305        return;
     4306    }
     4307
     4308    ASSERT(target->document().frame());
     4309    auto targetFrame = makeRef(*target->document().frame());
     4310
     4311    targetFrame->document()->updateLayoutIgnorePendingStylesheets();
     4312
     4313    // Performing layout could have could torn down the element's renderer. Check that we still
     4314    // have one. Otherwise, bail out as this function only focuses elements that have a visual
     4315    // representation.
     4316    if (!target->renderer()) {
     4317        completionHandler(false);
     4318        return;
     4319    }
     4320
     4321    UserGestureIndicator gestureIndicator { ProcessingUserGesture, &target->document() };
     4322    SetForScope<bool> userIsInteractingChange { m_userIsInteracting, true };
     4323    bool didFocus = m_page->focusController().setFocusedElement(target.get(), targetFrame);
     4324
     4325    // Setting the focused element could tear down the element's renderer. Check that we still have one.
     4326    if (!didFocus || !target->renderer()) {
     4327        completionHandler(false);
     4328        return;
     4329    }
     4330    ASSERT(m_focusedElement == target);
     4331    // The function visiblePositionInFocusedNodeForPoint constrains the point to be inside
     4332    // the bounds of the target element.
     4333    auto position = visiblePositionInFocusedNodeForPoint(targetFrame, point, true /* isInteractingWithFocusedElement */);
     4334    targetFrame->selection().setSelectedRange(Range::create(*targetFrame->document(), position, position).ptr(), position.affinity(), WebCore::FrameSelection::ShouldCloseTyping::Yes, UserTriggered);
     4335    completionHandler(true);
     4336}
     4337
    42994338void WebPage::platformDidScalePage()
    43004339{
  • trunk/Tools/ChangeLog

    r260193 r260214  
     12020-04-16  Daniel Bates  <dabates@apple.com>
     2
     3        [iOS] Add a way to focus a text input and place a caret
     4        https://bugs.webkit.org/show_bug.cgi?id=210611
     5        <rdar://problem/61893062>
     6
     7        Reviewed by Darin Adler.
     8
     9        Add some tests.
     10
     11        * TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm:
     12        (-[TestWKWebView synchronouslyFocusTextInputContext:placeCaretAt:]): Added.
     13        (webViewLoadHTMLStringAndWaitForAllFramesToPaint): Use the bundle's TestWebKitAPI.resources directory
     14        as the base URL so that we have a valid file URL. Some of the tests will then
     15        call -_setAllowUniversalAccessFromFileURLs to allow the main frame access to
     16        the unique-origin child frame contents.
     17        (TEST):
     18
    1192020-04-16  Daniel Bates  <dabates@apple.com>
    220
  • trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/RequestTextInputContext.mm

    r260193 r260214  
    3333#import "TestWKWebView.h"
    3434#import <WebKit/WKPreferencesRefPrivate.h>
     35#import <WebKit/WKWebViewConfigurationPrivate.h>
    3536#import <WebKit/WKWebViewPrivateForTesting.h>
    3637#import <WebKit/_WKTextInputContext.h>
     
    6364}
    6465
     66- (UIResponder<UITextInput> *)synchronouslyFocusTextInputContext:(_WKTextInputContext *)context placeCaretAt:(CGPoint)point
     67{
     68    __block bool finished = false;
     69    __block UIResponder<UITextInput> *responder = nil;
     70    [self _focusTextInputContext:context placeCaretAt:point completionHandler:^(UIResponder<UITextInput> *textInputResponder) {
     71        responder = textInputResponder;
     72        finished = true;
     73    }];
     74    TestWebKitAPI::Util::run(&finished);
     75    return responder;
     76}
     77
    6578@end
    6679
     
    158171    bool didFireDOMLoadEvent = false;
    159172    [webView performAfterLoading:[&] { didFireDOMLoadEvent = true; }];
    160     [webView loadHTMLString:htmlString baseURL:nil];
     173    [webView loadHTMLString:htmlString baseURL:[NSBundle.mainBundle.bundleURL URLByAppendingPathComponent:@"TestWebKitAPI.resources"]];
    161174    TestWebKitAPI::Util::run(&didFireDOMLoadEvent);
    162175    [webView waitForNextPresentationUpdate];
     
    275288}
    276289
     290TEST(RequestTextInputContext, ReadOnlyField)
     291{
     292    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     293    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     294
     295    [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' value='hello world' style='width: 100px; height: 50px;' readonly>")];
     296    EXPECT_EQ(0UL, [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]].count);
     297}
     298
     299TEST(RequestTextInputContext, DisabledField)
     300{
     301    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     302    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     303
     304    [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' value='hello world' style='width: 100px; height: 50px;' disabled>")];
     305    EXPECT_EQ(0UL, [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]].count);
     306}
     307
     308TEST(RequestTextInputContext, FocusFieldAndPlaceCaretAtStart)
     309{
     310    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     311    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     312
     313    [webView synchronouslyLoadHTMLString:applyStyle(@"<input type='text' value='hello world' style='width: 100px; height: 50px;'>")];
     314    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
     315    EXPECT_EQ(1UL, contexts.count);
     316    RetainPtr<_WKTextInputContext> inputElement = contexts[0];
     317
     318    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:[inputElement boundingRect].origin]);
     319    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     320    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]);
     321    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]);
     322}
     323
     324TEST(RequestTextInputContext, FocusFieldAndPlaceCaretAtEnd)
     325{
     326    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     327    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     328
     329    constexpr char exampleText[] = "hello world";
     330    constexpr size_t exampleTextLength = sizeof(exampleText) - 1;
     331    [webView synchronouslyLoadHTMLString:applyStyle([NSString stringWithFormat:@"<input type='text' value='%s' style='width: 100px; height: 50px;'>", exampleText])];
     332    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
     333    EXPECT_EQ(1UL, contexts.count);
     334    RetainPtr<_WKTextInputContext> inputElement = contexts[0];
     335
     336    CGRect boundingRect = [inputElement boundingRect];
     337    CGPoint endPosition = CGPointMake(boundingRect.origin.x + boundingRect.size.width, boundingRect.origin.y + boundingRect.size.height / 2);
     338    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:endPosition]);
     339    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     340    EXPECT_EQ(static_cast<int>(exampleTextLength), [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]);
     341    EXPECT_EQ(static_cast<int>(exampleTextLength), [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]);
     342}
     343
     344TEST(RequestTextInputContext, FocusFieldAndPlaceCaretOutsideField)
     345{
     346    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     347    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     348
     349    constexpr char exampleText[] = "hello world";
     350    [webView synchronouslyLoadHTMLString:applyStyle([NSString stringWithFormat:@"<input type='text' value='%s' style='width: 100px; height: 50px;'>", exampleText])];
     351    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
     352    EXPECT_EQ(1UL, contexts.count);
     353    RetainPtr<_WKTextInputContext> inputElement = contexts[0];
     354
     355    auto resetTest = [&] {
     356        [webView stringByEvaluatingJavaScript:@"document.activeElement.blur()"];
     357        EXPECT_WK_STREQ("BODY", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     358    };
     359
     360    // Point before the field
     361    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:CGPointMake(-1000, -500)]);
     362    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     363    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]);
     364    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]);
     365    resetTest();
     366
     367    // Point after the field
     368    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:inputElement.get() placeCaretAt:CGPointMake(1000, 500)]);
     369    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     370    EXPECT_EQ(11, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionStart"] intValue]);
     371    EXPECT_EQ(11, [[webView objectByEvaluatingJavaScript:@"document.activeElement.selectionEnd"] intValue]);
     372    resetTest();
     373}
     374
     375TEST(RequestTextInputContext, FocusFieldInFrame)
     376{
     377    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     378    [configuration _setAllowUniversalAccessFromFileURLs:YES];
     379    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     380
     381    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;'>")]);
     382    webViewLoadHTMLStringAndWaitForAllFramesToPaint(webView.get(), testPage);
     383    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
     384    EXPECT_EQ(2UL, contexts.count);
     385    RetainPtr<_WKTextInputContext> frameBField = contexts[0];
     386
     387    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:frameBField.get() placeCaretAt:[frameBField boundingRect].origin]);
     388    EXPECT_WK_STREQ("IFRAME", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     389    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.tagName"]);
     390    EXPECT_WK_STREQ("iframeField", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.value"]);
     391    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.selectionStart"] intValue]);
     392    EXPECT_EQ(0, [[webView objectByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.selectionEnd"] intValue]);
     393}
     394
     395TEST(RequestTextInputContext, SwitchFocusBetweenFrames)
     396{
     397    auto configuration = adoptNS([[WKWebViewConfiguration alloc] init]);
     398    [configuration _setAllowUniversalAccessFromFileURLs:YES];
     399    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600) configuration:configuration.get()]);
     400
     401    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;'>")]);
     402    webViewLoadHTMLStringAndWaitForAllFramesToPaint(webView.get(), testPage);
     403    NSArray<_WKTextInputContext *> *contexts = [webView synchronouslyRequestTextInputContextsInRect:[webView bounds]];
     404    EXPECT_EQ(2UL, contexts.count);
     405    // Note that returned contexts are in hit-test order.
     406    RetainPtr<_WKTextInputContext> frameAField = contexts[1];
     407    RetainPtr<_WKTextInputContext> frameBField = contexts[0];
     408
     409    EXPECT_WK_STREQ("BODY", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     410    EXPECT_WK_STREQ("BODY", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.tagName"]);
     411    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:frameAField.get() placeCaretAt:[frameAField boundingRect].origin]);
     412    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     413    EXPECT_WK_STREQ("mainFrameField", [webView stringByEvaluatingJavaScript:@"document.activeElement.value"]);
     414    EXPECT_WK_STREQ("BODY", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.tagName"]);
     415
     416    EXPECT_EQ((UIResponder<UITextInput> *)[webView textInputContentView], [webView synchronouslyFocusTextInputContext:frameBField.get() placeCaretAt:[frameBField boundingRect].origin]);
     417    EXPECT_WK_STREQ("IFRAME", [webView stringByEvaluatingJavaScript:@"document.activeElement.tagName"]);
     418    EXPECT_WK_STREQ("INPUT", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.tagName"]);
     419    EXPECT_WK_STREQ("iframeField", [webView stringByEvaluatingJavaScript:@"document.querySelector('iframe').contentDocument.activeElement.value"]);
     420}
     421
    277422#endif // PLATFORM(IOS_FAMILY)
Note: See TracChangeset for help on using the changeset viewer.