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;