Title: [260952] trunk
Revision
260952
Author
[email protected]
Date
2020-04-30 09:01:11 -0700 (Thu, 30 Apr 2020)

Log Message

[iOS] Implement -markedTextRange
https://bugs.webkit.org/show_bug.cgi?id=211148
<rdar://problem/57865890>

Reviewed by Wenson Hsieh.

Source/WebKit:

Return the UITextRange-like object for the marked text range. Clients can use this range to
access the selection rects or query for the caret rect at the start or end of the range,
if desired.

* Shared/EditorState.cpp:
(WebKit::EditorState::PostLayoutData::encode const):
(WebKit::EditorState::PostLayoutData::decode):
Encode and decode the marked text selection rects and caret rects at the state and end
of the marked text range. This is the same information that we compute in order to
implement -selectedTextRange. I thought about collecting these details into class and
having editor state hold two instances of it: one for selected text and one for marked text.
However I decided against it because this class is unlikely to find use outside of what is
needed to implement -selectedTextRange and -markedTextRange. This is because only those
functions require such details so as to return an opaque UITextRange-derived object that
can be passed to- or its sub-objects can be passed to WKContentView's -caretRectForPosition
and -selectionRectsForRange. Literally, WKContentView does not support other UITextInput
functions that operate on UITextRange or UITextPosition. I didn't pursue fixing this in
general because 1) I don't need it to solve my problem and 2) UIWKDocumentContext SPI seems
to be the new general-purpose solution.

(WebKit::operator<<): Pretty print all the new marked text details we collect. While I am here,
use Vector::isEmpty() instead of Vector::size() to check if there are selection rects.
* Shared/EditorState.h:
* UIProcess/ios/WKContentViewInteraction.mm:
(-[WKContentView textFirstRect]):
(-[WKContentView textLastRect]):
Update code now that the marked text rects are in the post layout data sub-object.

(-[WKContentView _scaledCaretRectForSelectionStart:]): Extracted from -selectedTextRange.
(-[WKContentView _scaledCaretRectForSelectionEnd:]): Ditto.
(-[WKContentView selectedTextRange]): Write in terms of -_scaledCaretRectForSelectionStart
and -_scaledCaretRectForSelectionEnd. Also modernize the code while I am here.
(-[WKContentView markedTextRange]): Added.
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::getPlatformEditorState const): Collect the marked text rects and the caret
rects at the start and end of the range.

Tools:

Add some tests.

* TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm:
(TestWebKitAPI::applyAhemStyle): Added.
(TestWebKitAPI::TEST):
* TestWebKitAPI/cocoa/TestWKWebView.h:
* TestWebKitAPI/cocoa/TestWKWebView.mm:
(-[TestWKWebView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:]): Added.

Modified Paths

Diff

Modified: trunk/Source/WebKit/ChangeLog (260951 => 260952)


--- trunk/Source/WebKit/ChangeLog	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Source/WebKit/ChangeLog	2020-04-30 16:01:11 UTC (rev 260952)
@@ -1,3 +1,48 @@
+2020-04-30  Daniel Bates  <[email protected]>
+
+        [iOS] Implement -markedTextRange
+        https://bugs.webkit.org/show_bug.cgi?id=211148
+        <rdar://problem/57865890>
+
+        Reviewed by Wenson Hsieh.
+
+        Return the UITextRange-like object for the marked text range. Clients can use this range to
+        access the selection rects or query for the caret rect at the start or end of the range,
+        if desired.
+
+        * Shared/EditorState.cpp:
+        (WebKit::EditorState::PostLayoutData::encode const):
+        (WebKit::EditorState::PostLayoutData::decode):
+        Encode and decode the marked text selection rects and caret rects at the state and end
+        of the marked text range. This is the same information that we compute in order to
+        implement -selectedTextRange. I thought about collecting these details into class and
+        having editor state hold two instances of it: one for selected text and one for marked text.
+        However I decided against it because this class is unlikely to find use outside of what is
+        needed to implement -selectedTextRange and -markedTextRange. This is because only those
+        functions require such details so as to return an opaque UITextRange-derived object that
+        can be passed to- or its sub-objects can be passed to WKContentView's -caretRectForPosition
+        and -selectionRectsForRange. Literally, WKContentView does not support other UITextInput
+        functions that operate on UITextRange or UITextPosition. I didn't pursue fixing this in
+        general because 1) I don't need it to solve my problem and 2) UIWKDocumentContext SPI seems
+        to be the new general-purpose solution.
+
+        (WebKit::operator<<): Pretty print all the new marked text details we collect. While I am here,
+        use Vector::isEmpty() instead of Vector::size() to check if there are selection rects.
+        * Shared/EditorState.h:
+        * UIProcess/ios/WKContentViewInteraction.mm:
+        (-[WKContentView textFirstRect]):
+        (-[WKContentView textLastRect]):
+        Update code now that the marked text rects are in the post layout data sub-object.
+
+        (-[WKContentView _scaledCaretRectForSelectionStart:]): Extracted from -selectedTextRange.
+        (-[WKContentView _scaledCaretRectForSelectionEnd:]): Ditto.
+        (-[WKContentView selectedTextRange]): Write in terms of -_scaledCaretRectForSelectionStart
+        and -_scaledCaretRectForSelectionEnd. Also modernize the code while I am here.
+        (-[WKContentView markedTextRange]): Added.
+        * WebProcess/WebPage/ios/WebPageIOS.mm:
+        (WebKit::WebPage::getPlatformEditorState const): Collect the marked text rects and the caret
+        rects at the start and end of the range.
+
 2020-04-30  Carlos Garcia Campos  <[email protected]>
 
         [GTK4][X11] Add support for rendering web view contents

Modified: trunk/Source/WebKit/Shared/EditorState.cpp (260951 => 260952)


--- trunk/Source/WebKit/Shared/EditorState.cpp	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Source/WebKit/Shared/EditorState.cpp	2020-04-30 16:01:11 UTC (rev 260952)
@@ -105,6 +105,10 @@
 #if PLATFORM(IOS_FAMILY)
     encoder << caretRectAtEnd;
     encoder << selectionRects;
+    encoder << markedTextRects;
+    encoder << markedText;
+    encoder << markedTextCaretRectAtStart;
+    encoder << markedTextCaretRectAtEnd;
     encoder << wordAtSelection;
     encoder << characterAfterSelection;
     encoder << characterBeforeSelection;
@@ -119,9 +123,6 @@
     encoder << atStartOfSentence;
     encoder << selectionStartIsAtParagraphBoundary;
     encoder << selectionEndIsAtParagraphBoundary;
-    encoder << firstMarkedRect;
-    encoder << lastMarkedRect;
-    encoder << markedText;
 #endif
 #if PLATFORM(MAC)
     encoder << candidateRequestStartPosition;
@@ -166,6 +167,14 @@
         return false;
     if (!decoder.decode(result.selectionRects))
         return false;
+    if (!decoder.decode(result.markedTextRects))
+        return false;
+    if (!decoder.decode(result.markedText))
+        return false;
+    if (!decoder.decode(result.markedTextCaretRectAtStart))
+        return false;
+    if (!decoder.decode(result.markedTextCaretRectAtEnd))
+        return false;
     if (!decoder.decode(result.wordAtSelection))
         return false;
     if (!decoder.decode(result.characterAfterSelection))
@@ -194,12 +203,6 @@
         return false;
     if (!decoder.decode(result.selectionEndIsAtParagraphBoundary))
         return false;
-    if (!decoder.decode(result.firstMarkedRect))
-        return false;
-    if (!decoder.decode(result.lastMarkedRect))
-        return false;
-    if (!decoder.decode(result.markedText))
-        return false;
 #endif
 #if PLATFORM(MAC)
     if (!decoder.decode(result.candidateRequestStartPosition))
@@ -284,16 +287,18 @@
         ts.dumpProperty("baseWritingDirection", static_cast<uint8_t>(editorState.postLayoutData().baseWritingDirection));
 #endif // PLATFORM(COCOA)
 #if PLATFORM(IOS_FAMILY)
-    if (editorState.postLayoutData().firstMarkedRect != IntRect())
-        ts.dumpProperty("firstMarkedRect", editorState.postLayoutData().firstMarkedRect);
-    if (editorState.postLayoutData().lastMarkedRect != IntRect())
-        ts.dumpProperty("lastMarkedRect", editorState.postLayoutData().lastMarkedRect);
-    if (editorState.postLayoutData().markedText.length())
-        ts.dumpProperty("markedText", editorState.postLayoutData().markedText);
     if (editorState.postLayoutData().caretRectAtEnd != IntRect())
         ts.dumpProperty("caretRectAtEnd", editorState.postLayoutData().caretRectAtEnd);
-    if (editorState.postLayoutData().selectionRects.size())
+    if (!editorState.postLayoutData().selectionRects.isEmpty())
         ts.dumpProperty("selectionRects", editorState.postLayoutData().selectionRects);
+    if (!editorState.postLayoutData().markedTextRects.isEmpty())
+        ts.dumpProperty("markedTextRects", editorState.postLayoutData().markedTextRects);
+    if (editorState.postLayoutData().markedText.length())
+        ts.dumpProperty("markedText", editorState.postLayoutData().markedText);
+    if (editorState.postLayoutData().markedTextCaretRectAtStart != IntRect())
+        ts.dumpProperty("markedTextCaretRectAtStart", editorState.postLayoutData().markedTextCaretRectAtStart);
+    if (editorState.postLayoutData().markedTextCaretRectAtEnd != IntRect())
+        ts.dumpProperty("markedTextCaretRectAtEnd", editorState.postLayoutData().markedTextCaretRectAtEnd);
     if (editorState.postLayoutData().wordAtSelection.length())
         ts.dumpProperty("wordAtSelection", editorState.postLayoutData().wordAtSelection);
     if (editorState.postLayoutData().characterAfterSelection)

Modified: trunk/Source/WebKit/Shared/EditorState.h (260951 => 260952)


--- trunk/Source/WebKit/Shared/EditorState.h	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Source/WebKit/Shared/EditorState.h	2020-04-30 16:01:11 UTC (rev 260952)
@@ -92,6 +92,10 @@
 #if PLATFORM(IOS_FAMILY)
         WebCore::IntRect caretRectAtEnd;
         Vector<WebCore::SelectionRect> selectionRects;
+        Vector<WebCore::SelectionRect> markedTextRects;
+        String markedText;
+        WebCore::IntRect markedTextCaretRectAtStart;
+        WebCore::IntRect markedTextCaretRectAtEnd;
         String wordAtSelection;
         UChar32 characterAfterSelection { 0 };
         UChar32 characterBeforeSelection { 0 };
@@ -106,9 +110,6 @@
         bool atStartOfSentence { false };
         bool selectionStartIsAtParagraphBoundary { false };
         bool selectionEndIsAtParagraphBoundary { false };
-        WebCore::IntRect firstMarkedRect;
-        WebCore::IntRect lastMarkedRect;
-        String markedText;
 #endif
 #if PLATFORM(MAC)
         uint64_t candidateRequestStartPosition { 0 };

Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (260951 => 260952)


--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm	2020-04-30 16:01:11 UTC (rev 260952)
@@ -4118,12 +4118,22 @@
 
 - (CGRect)textFirstRect
 {
-    return _page->editorState().hasComposition ? _page->editorState().postLayoutData().firstMarkedRect : _autocorrectionData.textFirstRect;
+    auto& editorState = _page->editorState();
+    if (editorState.hasComposition) {
+        auto& markedTextRects = editorState.postLayoutData().markedTextRects;
+        return markedTextRects.isEmpty() ? CGRectZero : markedTextRects.first().rect();
+    }
+    return _autocorrectionData.textFirstRect;
 }
 
 - (CGRect)textLastRect
 {
-    return _page->editorState().hasComposition ? _page->editorState().postLayoutData().lastMarkedRect : _autocorrectionData.textLastRect;
+    auto& editorState = _page->editorState();
+    if (editorState.hasComposition) {
+        auto& markedTextRects = editorState.postLayoutData().markedTextRects;
+        return markedTextRects.isEmpty() ? CGRectZero : markedTextRects.last().rect();
+    }
+    return _autocorrectionData.textLastRect;
 }
 
 - (void)replaceDictatedText:(NSString*)oldText withText:(NSString *)newText
@@ -4466,47 +4476,57 @@
     }).autorelease();
 }
 
-- (UITextRange *)selectedTextRange
+- (WebCore::FloatRect)_scaledCaretRectForSelectionStart:(WebCore::FloatRect)caretRect
 {
-    if (_page->editorState().selectionIsNone || _page->editorState().isMissingPostLayoutData)
-        return nil;
-    // UIKit does not expect caret selections in noneditable content.
-    if (!_page->editorState().isContentEditable && !_page->editorState().selectionIsRange)
-        return nil;
-    
-    auto& postLayoutEditorStateData = _page->editorState().postLayoutData();
-    WebCore::FloatRect startRect = postLayoutEditorStateData.caretRectAtStart;
-    WebCore::FloatRect endRect = postLayoutEditorStateData.caretRectAtEnd;
+    // The logical height of the caret is scaled inversely by the view's zoom scale
+    // to achieve the visual effect that the caret is narrow when zoomed in and wide
+    // when zoomed out.
     double inverseScale = [self inverseScale];
-    // We want to keep the original caret width, while the height scales with
-    // the content taking orientation into account.
-    // We achieve this by scaling the width with the inverse
-    // scale factor. This way, when it is converted from the content view
-    // the width remains unchanged.
-    if (startRect.width() < startRect.height())
-        startRect.setWidth(startRect.width() * inverseScale);
+    if (bool isHorizontalCaret = caretRect.width() < caretRect.height())
+        caretRect.setWidth(caretRect.width() * inverseScale);
     else
-        startRect.setHeight(startRect.height() * inverseScale);
-    if (endRect.width() < endRect.height()) {
-        double delta = endRect.width();
-        endRect.setWidth(endRect.width() * inverseScale);
-        delta = endRect.width() - delta;
-        endRect.move(delta, 0);
+        caretRect.setHeight(caretRect.height() * inverseScale);
+    return caretRect;
+}
+
+- (WebCore::FloatRect)_scaledCaretRectForSelectionEnd:(WebCore::FloatRect)caretRect
+{
+    // The logical height of the caret is scaled inversely by the view's zoom scale
+    // to achieve the visual effect that the caret is narrow when zoomed in and wide
+    // when zoomed out.
+    double inverseScale = [self inverseScale];
+    if (bool isHorizontalCaret = caretRect.width() < caretRect.height()) {
+        float originalWidth = caretRect.width();
+        caretRect.setWidth(originalWidth * inverseScale);
+        caretRect.move(caretRect.width() - originalWidth, 0);
     } else {
-        double delta = endRect.height();
-        endRect.setHeight(endRect.height() * inverseScale);
-        delta = endRect.height() - delta;
-        endRect.move(0, delta);
+        float originalHeight = caretRect.height();
+        caretRect.setHeight(caretRect.height() * inverseScale);
+        caretRect.move(0, caretRect.height() - originalHeight);
     }
-    return [WKTextRange textRangeWithState:_page->editorState().selectionIsNone
-                                   isRange:_page->editorState().selectionIsRange
-                                isEditable:_page->editorState().isContentEditable
-                                 startRect:startRect
-                                   endRect:endRect
-                            selectionRects:wkTextSelectionRects(_page->editorState().postLayoutData().selectionRects)
-                        selectedTextLength:postLayoutEditorStateData.selectedTextLength];
+    return caretRect;
 }
 
+- (UITextRange *)selectedTextRange
+{
+    auto& editorState = _page->editorState();
+    auto hasSelection = !editorState.selectionIsNone;
+    if (!hasSelection || editorState.isMissingPostLayoutData)
+        return nil;
+
+    auto isRange = editorState.selectionIsRange;
+    auto isContentEditable = editorState.isContentEditable;
+    // UIKit does not expect caret selections in non-editable content.
+    if (!isContentEditable && !isRange)
+        return nil;
+
+    auto caretStartRect = [self _scaledCaretRectForSelectionStart:_page->editorState().postLayoutData().caretRectAtStart];
+    auto caretEndRect = [self _scaledCaretRectForSelectionEnd:_page->editorState().postLayoutData().caretRectAtEnd];
+    auto selectionRects = wkTextSelectionRects(_page->editorState().postLayoutData().selectionRects);
+    auto selectedTextLength = editorState.postLayoutData().selectedTextLength;
+    return [WKTextRange textRangeWithState:!hasSelection isRange:isRange isEditable:isContentEditable startRect:caretStartRect endRect:caretEndRect selectionRects:selectionRects selectedTextLength:selectedTextLength];
+}
+
 - (CGRect)caretRectForPosition:(UITextPosition *)position
 {
     return ((WKTextPosition *)position).positionRect;
@@ -4540,7 +4560,20 @@
 
 - (UITextRange *)markedTextRange
 {
-    return nil;
+    auto& editorState = _page->editorState();
+    bool hasComposition = editorState.hasComposition;
+    if (!hasComposition || editorState.isMissingPostLayoutData)
+        return nil;
+    auto& postLayoutData = editorState.postLayoutData();
+    auto unscaledCaretRectAtStart = postLayoutData.markedTextCaretRectAtStart;
+    auto unscaledCaretRectAtEnd = postLayoutData.markedTextCaretRectAtEnd;
+    auto isRange = unscaledCaretRectAtStart != unscaledCaretRectAtEnd;
+    auto isContentEditable = editorState.isContentEditable;
+    auto caretStartRect = [self _scaledCaretRectForSelectionStart:unscaledCaretRectAtStart];
+    auto caretEndRect = [self _scaledCaretRectForSelectionEnd:unscaledCaretRectAtEnd];
+    auto selectionRects = wkTextSelectionRects(postLayoutData.markedTextRects);
+    auto selectedTextLength = postLayoutData.markedText.length();
+    return [WKTextRange textRangeWithState:!hasComposition isRange:isRange isEditable:isContentEditable startRect:caretStartRect endRect:caretEndRect selectionRects:selectionRects selectedTextLength:selectedTextLength];
 }
 
 - (NSDictionary *)markedTextStyle

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


--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm	2020-04-30 16:01:11 UTC (rev 260952)
@@ -262,17 +262,15 @@
     auto view = makeRef(*frame.view());
 
     if (frame.editor().hasComposition()) {
-        auto compositionRange = frame.editor().compositionRange();
-        Vector<WebCore::SelectionRect> compositionRects;
-        if (compositionRange) {
-            compositionRange->collectSelectionRects(compositionRects);
-            if (compositionRects.size())
-                postLayoutData.firstMarkedRect = view->contentsToRootView(compositionRects[0].rect());
-            if (compositionRects.size() > 1)
-                postLayoutData.lastMarkedRect = view->contentsToRootView(compositionRects.last().rect());
-            else
-                postLayoutData.lastMarkedRect = postLayoutData.firstMarkedRect;
+        if (auto compositionRange = frame.editor().compositionRange()) {
+            compositionRange->collectSelectionRects(postLayoutData.markedTextRects);
+            convertContentToRootViewSelectionRects(view, postLayoutData.markedTextRects);
+
             postLayoutData.markedText = plainTextForContext(compositionRange.get());
+            VisibleSelection compositionSelection(*compositionRange);
+            postLayoutData.markedTextCaretRectAtStart = view->contentsToRootView(compositionSelection.visibleStart().absoluteCaretBounds(nullptr /* insideFixed */));
+            postLayoutData.markedTextCaretRectAtEnd = view->contentsToRootView(compositionSelection.visibleEnd().absoluteCaretBounds(nullptr /* insideFixed */));
+
         }
     }
 

Modified: trunk/Tools/ChangeLog (260951 => 260952)


--- trunk/Tools/ChangeLog	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Tools/ChangeLog	2020-04-30 16:01:11 UTC (rev 260952)
@@ -1,3 +1,20 @@
+2020-04-30  Daniel Bates  <[email protected]>
+
+        [iOS] Implement -markedTextRange
+        https://bugs.webkit.org/show_bug.cgi?id=211148
+        <rdar://problem/57865890>
+
+        Reviewed by Wenson Hsieh.
+
+        Add some tests.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm:
+        (TestWebKitAPI::applyAhemStyle): Added.
+        (TestWebKitAPI::TEST):
+        * TestWebKitAPI/cocoa/TestWKWebView.h:
+        * TestWebKitAPI/cocoa/TestWKWebView.mm:
+        (-[TestWKWebView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:]): Added.
+
 2020-04-30  Claudio Saavedra  <[email protected]>
 
         [GTK4] Add navigation and reload buttons to MiniBrowser

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm (260951 => 260952)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/EditorStateTests.mm	2020-04-30 16:01:11 UTC (rev 260952)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2017 Apple Inc. All rights reserved.
+ * Copyright (C) 2017-2020 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,13 +27,15 @@
 
 #import "EditingTestHarness.h"
 #import "PlatformUtilities.h"
+#import "TestCocoa.h"
+#import "TestInputDelegate.h"
 #import "TestWKWebView.h"
+#import "UserInterfaceSwizzler.h"
 #import <WebKit/WKWebViewPrivate.h>
 #import <wtf/Vector.h>
 
 #if PLATFORM(IOS_FAMILY)
 #import "UIKitSPI.h"
-#import <UIKit/UIKit.h>
 #endif
 
 static void* const SelectionAttributesObservationContext = (void*)&SelectionAttributesObservationContext;
@@ -429,6 +431,101 @@
     EXPECT_GT([[webView textInputContentView] selectedText].length, 0U);
 }
 
+constexpr unsigned glyphWidth { 25 }; // pixels
+
+static NSString *applyAhemStyle(NSString *htmlString)
+{
+    return [NSString stringWithFormat:@"<style>@font-face { font-family: Ahem; src: url(Ahem.ttf); } body { margin: 0; } * { font: %upx/1 Ahem; -webkit-text-size-adjust: none; }</style><meta name='viewport' content='width=980, initial-scale=1.0'>%@", glyphWidth, htmlString];
+}
+
+TEST(EditorStateTests, MarkedTextRange_HorizontalCaretSelection)
+{
+    IPhoneUserInterfaceSwizzler userInterfaceSwizzler;
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:applyAhemStyle(@"<body contenteditable='true'>.</body>")]; // . is dummy to force Ahem to load
+    [webView stringByEvaluatingJavaScript:@"document.body.focus()"];
+
+    auto *contentView = [webView textInputContentView];
+    [contentView setMarkedText:@"hello" selectedRange:NSMakeRange(0, 0)];
+    [webView waitForNextPresentationUpdate];
+
+    UITextRange *markedTextRange = [contentView markedTextRange];
+    NSArray<UITextSelectionRect *> *rects = [contentView selectionRectsForRange:markedTextRange];
+    EXPECT_EQ(1U, rects.count);
+    EXPECT_EQ(CGRectMake(0, 0, 5 * glyphWidth, glyphWidth), rects[0].rect);
+    EXPECT_EQ(CGRectMake(0, 0, 2, glyphWidth), [contentView caretRectForPosition:markedTextRange.start]);
+    EXPECT_EQ(CGRectMake(124, 0, 2, glyphWidth), [contentView caretRectForPosition:markedTextRange.end]);
+    EXPECT_FALSE(rects[0].isVertical);
+}
+
+TEST(EditorStateTests, MarkedTextRange_HorizontalRangeSelection)
+{
+    IPhoneUserInterfaceSwizzler userInterfaceSwizzler;
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:applyAhemStyle(@"<body contenteditable='true'>.</body>")]; // . is dummy to force Ahem to load
+    [webView stringByEvaluatingJavaScript:@"document.body.focus()"];
+    [webView _synchronouslyExecuteEditCommand:@"InsertText" argument:@"Hello world"];
+
+    auto *contentView = [webView textInputContentView];
+    [contentView selectWordBackward];
+    [contentView setMarkedText:@"world" selectedRange:NSMakeRange(0, 5)];
+    [webView waitForNextPresentationUpdate];
+
+    UITextRange *markedTextRange = [contentView markedTextRange];
+    NSArray<UITextSelectionRect *> *rects = [contentView selectionRectsForRange:markedTextRange];
+    EXPECT_EQ(1U, rects.count);
+    EXPECT_EQ(CGRectMake(150, 0, 5 * glyphWidth, glyphWidth), rects[0].rect);
+    EXPECT_EQ(CGRectMake(149, 0, 2, glyphWidth), [contentView caretRectForPosition:markedTextRange.start]);
+    EXPECT_EQ(CGRectMake(274, 0, 2, glyphWidth), [contentView caretRectForPosition:markedTextRange.end]);
+    EXPECT_FALSE(rects[0].isVertical);
+}
+
+TEST(EditorStateTests, MarkedTextRange_VerticalCaretSelection)
+{
+    IPhoneUserInterfaceSwizzler userInterfaceSwizzler;
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    [webView synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:applyAhemStyle(@"<body style='writing-mode: vertical-lr' contenteditable='true'>.</body>")]; // . is dummy to force Ahem to load
+    [webView stringByEvaluatingJavaScript:@"document.body.focus()"];
+
+    auto *contentView = [webView textInputContentView];
+    [contentView setMarkedText:@"hello" selectedRange:NSMakeRange(0, 0)];
+    [webView waitForNextPresentationUpdate];
+
+    UITextRange *markedTextRange = [contentView markedTextRange];
+    NSArray<UITextSelectionRect *> *rects = [contentView selectionRectsForRange:markedTextRange];
+    EXPECT_EQ(1U, rects.count);
+    EXPECT_EQ(CGRectMake(0, 0, glyphWidth, 5 * glyphWidth), rects[0].rect);
+    EXPECT_EQ(CGRectMake(0, 0, glyphWidth, 2), [contentView caretRectForPosition:markedTextRange.start]);
+    EXPECT_EQ(CGRectMake(0, 124, glyphWidth, 2), [contentView caretRectForPosition:markedTextRange.end]);
+    EXPECT_TRUE(rects[0].isVertical);
+}
+
+TEST(EditorStateTests, MarkedTextRange_VerticalRangeSelection)
+{
+    IPhoneUserInterfaceSwizzler userInterfaceSwizzler;
+
+    auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:NSMakeRect(0, 0, 800, 600)]);
+    [webView synchronouslyLoadHTMLString:applyAhemStyle(@"<body style='writing-mode: vertical-lr' contenteditable='true'>.</body>")]; // . is dummy to force Ahem to load
+    [webView stringByEvaluatingJavaScript:@"document.body.focus()"];
+    [webView _synchronouslyExecuteEditCommand:@"InsertText" argument:@"Hello world"];
+
+    auto *contentView = [webView textInputContentView];
+    [contentView selectWordBackward];
+    [contentView setMarkedText:@"world" selectedRange:NSMakeRange(0, 5)];
+    [webView waitForNextPresentationUpdate];
+
+    UITextRange *markedTextRange = [contentView markedTextRange];
+    NSArray<UITextSelectionRect *> *rects = [contentView selectionRectsForRange:markedTextRange];
+    EXPECT_EQ(1U, rects.count);
+    EXPECT_EQ(CGRectMake(0, 150, glyphWidth, 5 * glyphWidth), rects[0].rect);
+    EXPECT_EQ(CGRectMake(0, 149, glyphWidth, 2), [contentView caretRectForPosition:markedTextRange.start]);
+    EXPECT_EQ(CGRectMake(0, 274, glyphWidth, 2), [contentView caretRectForPosition:markedTextRange.end]);
+    EXPECT_TRUE(rects[0].isVertical);
+}
+
 #endif // PLATFORM(IOS_FAMILY)
 
 } // namespace TestWebKitAPI

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h (260951 => 260952)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.h	2020-04-30 16:01:11 UTC (rev 260952)
@@ -75,6 +75,7 @@
 @interface TestWKWebView : WKWebView
 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration processPoolConfiguration:(_WKProcessPoolConfiguration *)processPoolConfiguration;
 - (instancetype)initWithFrame:(CGRect)frame configuration:(WKWebViewConfiguration *)configuration addToWindow:(BOOL)addToWindow;
+- (void)synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:(NSString *)html;
 - (void)clearMessageHandlers:(NSArray *)messageNames;
 - (void)performAfterReceivingMessage:(NSString *)message action:(dispatch_block_t)action;
 - (void)waitForMessage:(NSString *)message;

Modified: trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm (260951 => 260952)


--- trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2020-04-30 15:12:36 UTC (rev 260951)
+++ trunk/Tools/TestWebKitAPI/cocoa/TestWKWebView.mm	2020-04-30 16:01:11 UTC (rev 260952)
@@ -475,6 +475,15 @@
     [_testHandler addMessage:message withHandler:action];
 }
 
+- (void)synchronouslyLoadHTMLStringAndWaitUntilAllImmediateChildFramesPaint:(NSString *)html
+{
+    bool didFireDOMLoadEvent = false;
+    [self performAfterLoading:[&] { didFireDOMLoadEvent = true; }];
+    [self loadHTMLString:html baseURL:[NSBundle.mainBundle.bundleURL URLByAppendingPathComponent:@"TestWebKitAPI.resources"]];
+    TestWebKitAPI::Util::run(&didFireDOMLoadEvent);
+    [self waitForNextPresentationUpdate];
+}
+
 - (void)waitForMessage:(NSString *)message
 {
     __block bool isDoneWaiting = false;
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to