- Revision
- 266051
- Author
- wenson_hs...@apple.com
- Date
- 2020-08-23 18:51:56 -0700 (Sun, 23 Aug 2020)
Log Message
Programmatic selection of text in a text field causes the highlight overlay to spill out
https://bugs.webkit.org/show_bug.cgi?id=215647
<rdar://problem/67404979>
Reviewed by Darin Adler.
Source/WebCore:
See WebKit ChangeLog for more detail.
* html/HTMLTextFormControlElement.h: Export a helper function.
Source/WebKit:
UIKit consults the SPI method `-_selectionClipRect` on WKContentView to determine the maximum bounds in which it
is allowed to show text selection UI (i.e. the text caret view, selection highlight views, and selection
grabbers). Normally, when contentEditable elements and editable text form controls are focused, we plumb the
bounds of the focused element to the UI process via `focusedElementRect` in editor state's post layout data.
However, in this case, the selection is inside a readonly text field that is *not* focused; this causes us to
return `CGRectNull` from `-_selectionClipRect`, which means that UIKit selection UI is not clipped at all and
instead overflows the bounds of the input element.
To fix this, rearrange some logic in `WebPage::getPlatformEditorState` in `WebPageIOS.mm` such that we compute
and send the selection clipping rect if the selection is inside a text form control, even if it is not the
focused element. In doing this, we also rename some confusingly-named members in `EditorState::PostLayoutData`
(see below for more detail).
Test: editing/selection/ios/select-all-in-readonly-input-does-not-overflow.html
* Platform/spi/ios/UIKitSPI.h:
Add a declaration for `-_selectionClipRect`.
* Shared/EditorState.cpp:
(WebKit::EditorState::PostLayoutData::encode const):
(WebKit::EditorState::PostLayoutData::decode):
(WebKit::operator<<):
* Shared/EditorState.h:
Split the existing rect member `focusedElementRect` into two: `selectionClipRect` on iOS, and
`selectionBoundingRect` on macOS. Previously, `focusedElementRect` was set to the focused element's bounding
rect on iOS (as expected), but on macOS, we set to the bounds of the ranged or caret selection; we proceed to
use this rect to mean the bounds of the selection anyways in macOS-specific code, so it makes more sense to just
move this into the macOS-specific section.
Additionally, after the below change in WebPageIOS, `focusedElementRect` is no longer specific to the focused
element, so rename it instead to `selectionClipRect` instead. In `WKContentViewInteraction.mm`, this rect is
effectively only used in two ways: as the selection clip rect for UIKit, and to determine if the selection would
be completely clipped anyways (in which case we suppress UIKit text interactions), so `selectionClipRect` is a
name that is suitable for both purposes.
* UIProcess/API/mac/WKWebViewTestingMac.mm:
(-[WKWebView _candidateRect]):
* UIProcess/Cocoa/WebViewImpl.mm:
Rename `focusedElementRect` to `selectionBoundingRect` on macOS.
(WebKit::WebViewImpl::handleRequestedCandidates):
* UIProcess/ios/WKContentViewInteraction.mm:
Rename `focusedElementRect` to `selectionClipRect` on iOS.
(WebKit::WKSelectionDrawingInfo::WKSelectionDrawingInfo):
(-[WKContentView _selectionClipRect]):
Change this to not require a focused element when returning the selection clip rect, and instead return the
editor state's selection clip rect as long as it is not empty.
(-[WKContentView _updateSelectionAssistantSuppressionState]):
* WebProcess/WebPage/ios/WebPageIOS.mm:
(WebKit::WebPage::getPlatformEditorState const):
Refactor this code to compute and send selectionClipRect as long as the selection is inside a form control or
editing host (not just when there is a focused element). Also leave a drive-by FIXME about using the focused
element (instead of the selection container node) when computing the caret color.
* WebProcess/WebPage/mac/WebPageMac.mm:
(WebKit::WebPage::getPlatformEditorState const):
Tools:
* WebKitTestRunner/ios/UIScriptControllerIOS.mm:
(WTR::clipSelectionViewRectToContentView):
Add a helper function to clip UIKit selection UI rects (for carets, selection grabbers, and ranged selection
highlights) to the content view's (WKContentView's) bounds, as well as the selection clip rect if it is nonnull.
(WTR::UIScriptControllerIOS::selectionStartGrabberViewRect const):
(WTR::UIScriptControllerIOS::selectionEndGrabberViewRect const):
(WTR::UIScriptControllerIOS::selectionCaretViewRect const):
(WTR::UIScriptControllerIOS::selectionRangeViewRects const):
LayoutTests:
* editing/selection/ios/select-all-in-readonly-input-does-not-overflow-expected.txt: Added.
* editing/selection/ios/select-all-in-readonly-input-does-not-overflow.html: Added.
Add a new layout test to verify that the width of the selection view does not exceed the width of the readonly
input containing the selected text.
* resources/ui-helper.js:
(window.UIHelper.async waitForSelectionToAppear):
Adjust this UIHelper function to additionally resolve to the selection rects, so that it won't be necessary for
callers to ask for the selection view rects separately after waiting for selection views to appear.
Modified Paths
Added Paths
Diff
Modified: trunk/LayoutTests/ChangeLog (266050 => 266051)
--- trunk/LayoutTests/ChangeLog 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/LayoutTests/ChangeLog 2020-08-24 01:51:56 UTC (rev 266051)
@@ -1,3 +1,23 @@
+2020-08-23 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Programmatic selection of text in a text field causes the highlight overlay to spill out
+ https://bugs.webkit.org/show_bug.cgi?id=215647
+ <rdar://problem/67404979>
+
+ Reviewed by Darin Adler.
+
+ * editing/selection/ios/select-all-in-readonly-input-does-not-overflow-expected.txt: Added.
+ * editing/selection/ios/select-all-in-readonly-input-does-not-overflow.html: Added.
+
+ Add a new layout test to verify that the width of the selection view does not exceed the width of the readonly
+ input containing the selected text.
+
+ * resources/ui-helper.js:
+ (window.UIHelper.async waitForSelectionToAppear):
+
+ Adjust this UIHelper function to additionally resolve to the selection rects, so that it won't be necessary for
+ callers to ask for the selection view rects separately after waiting for selection views to appear.
+
2020-08-23 Fujii Hironori <hironori.fu...@sony.com>
[WinCairo] Unreviewed test gardening
Added: trunk/LayoutTests/editing/selection/ios/select-all-in-readonly-input-does-not-overflow-expected.txt (0 => 266051)
--- trunk/LayoutTests/editing/selection/ios/select-all-in-readonly-input-does-not-overflow-expected.txt (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/select-all-in-readonly-input-does-not-overflow-expected.txt 2020-08-24 01:51:56 UTC (rev 266051)
@@ -0,0 +1,11 @@
+This test verifies that selection views are clipped to the bounds of a readonly input. To manually run the test, tap the 'Select' button, and then tap the input.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS selectionRects.length is 1
+PASS input.getBoundingClientRect().width is >= selectionRects[0].width
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/editing/selection/ios/select-all-in-readonly-input-does-not-overflow.html (0 => 266051)
--- trunk/LayoutTests/editing/selection/ios/select-all-in-readonly-input-does-not-overflow.html (rev 0)
+++ trunk/LayoutTests/editing/selection/ios/select-all-in-readonly-input-does-not-overflow.html 2020-08-24 01:51:56 UTC (rev 266051)
@@ -0,0 +1,48 @@
+<!DOCTYPE html> <!-- webkit-test-runner [ useFlexibleViewport=true ] -->
+<html>
+<head>
+<script src=""
+<script src=""
+<meta name="viewport" content="width=device-width, initial-scale=1">
+<style>
+input, button {
+ font-size: 16px;
+ width: 120px;
+ box-sizing: border-box;
+}
+</style>
+</head>
+<body>
+<div>
+ <input readonly value="Lorem ipsum dolor sit amet, consectetur adipiscing elit. Curabitur sed laoreet erat. In hac habitasse platea dictumst. Vestibulum tristique, lacus sed pellentesque vehicula, purus quam interdum sapien, pharetra molestie ante nibh ut velit. Vivamus eu lacus est. Proin at arcu vel urna iaculis commodo. Sed tristique nisl nec tortor molestie aliquet.">
+ <button>Select</button>
+</div>
+</body>
+<script>
+description("This test verifies that selection views are clipped to the bounds of a readonly input. To manually run the test, tap the 'Select' button, and then tap the input.");
+
+jsTestIsAsync = true;
+
+addEventListener("load", async () => {
+ input = document.querySelector("input");
+ button = document.querySelector("button");
+ button.addEventListener("click", () => {
+ input.select();
+ });
+
+ if (!window.testRunner)
+ return;
+
+ await UIHelper.activateElement(button);
+ await UIHelper.activateElement(input);
+ selectionRects = await UIHelper.waitForSelectionToAppear();
+
+ shouldBe("selectionRects.length", "1");
+ shouldBeGreaterThanOrEqual("input.getBoundingClientRect().width", "selectionRects[0].width");
+
+ input.remove();
+ button.remove();
+ finishJSTest();
+});
+</script>
+</html>
\ No newline at end of file
Modified: trunk/LayoutTests/resources/ui-helper.js (266050 => 266051)
--- trunk/LayoutTests/resources/ui-helper.js 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/LayoutTests/resources/ui-helper.js 2020-08-24 01:51:56 UTC (rev 266051)
@@ -1243,8 +1243,9 @@
static async waitForSelectionToAppear() {
while (true) {
- if ((await this.getUISelectionViewRects()).length > 0)
- break;
+ let selectionRects = await this.getUISelectionViewRects();
+ if (selectionRects.length > 0)
+ return selectionRects;
}
}
Modified: trunk/Source/WebCore/ChangeLog (266050 => 266051)
--- trunk/Source/WebCore/ChangeLog 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebCore/ChangeLog 2020-08-24 01:51:56 UTC (rev 266051)
@@ -1,3 +1,15 @@
+2020-08-23 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Programmatic selection of text in a text field causes the highlight overlay to spill out
+ https://bugs.webkit.org/show_bug.cgi?id=215647
+ <rdar://problem/67404979>
+
+ Reviewed by Darin Adler.
+
+ See WebKit ChangeLog for more detail.
+
+ * html/HTMLTextFormControlElement.h: Export a helper function.
+
2020-08-23 Zalan Bujtas <za...@apple.com>
[LFC][IFC] Introduce the root inline box
Modified: trunk/Source/WebCore/html/HTMLTextFormControlElement.h (266050 => 266051)
--- trunk/Source/WebCore/html/HTMLTextFormControlElement.h 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebCore/html/HTMLTextFormControlElement.h 2020-08-24 01:51:56 UTC (rev 266051)
@@ -173,7 +173,7 @@
int m_minLength { -1 };
};
-HTMLTextFormControlElement* enclosingTextFormControl(const Position&);
+WEBCORE_EXPORT HTMLTextFormControlElement* enclosingTextFormControl(const Position&);
} // namespace WebCore
Modified: trunk/Source/WebKit/ChangeLog (266050 => 266051)
--- trunk/Source/WebKit/ChangeLog 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/ChangeLog 2020-08-24 01:51:56 UTC (rev 266051)
@@ -1,3 +1,76 @@
+2020-08-23 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Programmatic selection of text in a text field causes the highlight overlay to spill out
+ https://bugs.webkit.org/show_bug.cgi?id=215647
+ <rdar://problem/67404979>
+
+ Reviewed by Darin Adler.
+
+ UIKit consults the SPI method `-_selectionClipRect` on WKContentView to determine the maximum bounds in which it
+ is allowed to show text selection UI (i.e. the text caret view, selection highlight views, and selection
+ grabbers). Normally, when contentEditable elements and editable text form controls are focused, we plumb the
+ bounds of the focused element to the UI process via `focusedElementRect` in editor state's post layout data.
+ However, in this case, the selection is inside a readonly text field that is *not* focused; this causes us to
+ return `CGRectNull` from `-_selectionClipRect`, which means that UIKit selection UI is not clipped at all and
+ instead overflows the bounds of the input element.
+
+ To fix this, rearrange some logic in `WebPage::getPlatformEditorState` in `WebPageIOS.mm` such that we compute
+ and send the selection clipping rect if the selection is inside a text form control, even if it is not the
+ focused element. In doing this, we also rename some confusingly-named members in `EditorState::PostLayoutData`
+ (see below for more detail).
+
+ Test: editing/selection/ios/select-all-in-readonly-input-does-not-overflow.html
+
+ * Platform/spi/ios/UIKitSPI.h:
+
+ Add a declaration for `-_selectionClipRect`.
+
+ * Shared/EditorState.cpp:
+ (WebKit::EditorState::PostLayoutData::encode const):
+ (WebKit::EditorState::PostLayoutData::decode):
+ (WebKit::operator<<):
+ * Shared/EditorState.h:
+
+ Split the existing rect member `focusedElementRect` into two: `selectionClipRect` on iOS, and
+ `selectionBoundingRect` on macOS. Previously, `focusedElementRect` was set to the focused element's bounding
+ rect on iOS (as expected), but on macOS, we set to the bounds of the ranged or caret selection; we proceed to
+ use this rect to mean the bounds of the selection anyways in macOS-specific code, so it makes more sense to just
+ move this into the macOS-specific section.
+
+ Additionally, after the below change in WebPageIOS, `focusedElementRect` is no longer specific to the focused
+ element, so rename it instead to `selectionClipRect` instead. In `WKContentViewInteraction.mm`, this rect is
+ effectively only used in two ways: as the selection clip rect for UIKit, and to determine if the selection would
+ be completely clipped anyways (in which case we suppress UIKit text interactions), so `selectionClipRect` is a
+ name that is suitable for both purposes.
+
+ * UIProcess/API/mac/WKWebViewTestingMac.mm:
+ (-[WKWebView _candidateRect]):
+ * UIProcess/Cocoa/WebViewImpl.mm:
+
+ Rename `focusedElementRect` to `selectionBoundingRect` on macOS.
+
+ (WebKit::WebViewImpl::handleRequestedCandidates):
+ * UIProcess/ios/WKContentViewInteraction.mm:
+
+ Rename `focusedElementRect` to `selectionClipRect` on iOS.
+
+ (WebKit::WKSelectionDrawingInfo::WKSelectionDrawingInfo):
+ (-[WKContentView _selectionClipRect]):
+
+ Change this to not require a focused element when returning the selection clip rect, and instead return the
+ editor state's selection clip rect as long as it is not empty.
+
+ (-[WKContentView _updateSelectionAssistantSuppressionState]):
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::WebPage::getPlatformEditorState const):
+
+ Refactor this code to compute and send selectionClipRect as long as the selection is inside a form control or
+ editing host (not just when there is a focused element). Also leave a drive-by FIXME about using the focused
+ element (instead of the selection container node) when computing the caret color.
+
+ * WebProcess/WebPage/mac/WebPageMac.mm:
+ (WebKit::WebPage::getPlatformEditorState const):
+
2020-08-19 Darin Adler <da...@apple.com>
Move node geometry functions from Range to RenderObject
Modified: trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h (266050 => 266051)
--- trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/Platform/spi/ios/UIKitSPI.h 2020-08-24 01:51:56 UTC (rev 266051)
@@ -487,6 +487,7 @@
- (void)modifierFlagsDidChangeFrom:(UIKeyModifierFlags)oldFlags to:(UIKeyModifierFlags)newFlags;
#endif
@property (nonatomic) UITextGranularity selectionGranularity;
+@property (nonatomic, readonly) CGRect _selectionClipRect;
@required
- (BOOL)hasContent;
- (BOOL)hasSelection;
Modified: trunk/Source/WebKit/Shared/EditorState.cpp (266050 => 266051)
--- trunk/Source/WebKit/Shared/EditorState.cpp 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/Shared/EditorState.cpp 2020-08-24 01:51:56 UTC (rev 266051)
@@ -95,7 +95,6 @@
encoder << caretRectAtStart;
#endif
#if PLATFORM(COCOA)
- encoder << focusedElementRect;
encoder << selectedTextLength;
encoder << textAlignment;
encoder << textColor;
@@ -103,6 +102,7 @@
encoder << baseWritingDirection;
#endif
#if PLATFORM(IOS_FAMILY)
+ encoder << selectionClipRect;
encoder << caretRectAtEnd;
encoder << selectionRects;
encoder << markedTextRects;
@@ -125,6 +125,7 @@
encoder << selectionEndIsAtParagraphBoundary;
#endif
#if PLATFORM(MAC)
+ encoder << selectionBoundingRect;
encoder << candidateRequestStartPosition;
encoder << paragraphContextForCandidateRequest;
encoder << stringForCandidateRequest;
@@ -149,8 +150,6 @@
return false;
#endif
#if PLATFORM(COCOA)
- if (!decoder.decode(result.focusedElementRect))
- return false;
if (!decoder.decode(result.selectedTextLength))
return false;
if (!decoder.decode(result.textAlignment))
@@ -163,6 +162,8 @@
return false;
#endif
#if PLATFORM(IOS_FAMILY)
+ if (!decoder.decode(result.selectionClipRect))
+ return false;
if (!decoder.decode(result.caretRectAtEnd))
return false;
if (!decoder.decode(result.selectionRects))
@@ -205,6 +206,9 @@
return false;
#endif
#if PLATFORM(MAC)
+ if (!decoder.decode(result.selectionBoundingRect))
+ return false;
+
if (!decoder.decode(result.candidateRequestStartPosition))
return false;
@@ -273,8 +277,6 @@
ts.dumpProperty("caretRectAtStart", editorState.postLayoutData().caretRectAtStart);
#endif
#if PLATFORM(COCOA)
- if (editorState.postLayoutData().focusedElementRect != IntRect())
- ts.dumpProperty("focusedElementRect", editorState.postLayoutData().focusedElementRect);
if (editorState.postLayoutData().selectedTextLength)
ts.dumpProperty("selectedTextLength", editorState.postLayoutData().selectedTextLength);
if (editorState.postLayoutData().textAlignment != NoAlignment)
@@ -287,6 +289,8 @@
ts.dumpProperty("baseWritingDirection", static_cast<uint8_t>(editorState.postLayoutData().baseWritingDirection));
#endif // PLATFORM(COCOA)
#if PLATFORM(IOS_FAMILY)
+ if (editorState.postLayoutData().selectionClipRect != IntRect())
+ ts.dumpProperty("selectionClipRect", editorState.postLayoutData().selectionClipRect);
if (editorState.postLayoutData().caretRectAtEnd != IntRect())
ts.dumpProperty("caretRectAtEnd", editorState.postLayoutData().caretRectAtEnd);
if (!editorState.postLayoutData().selectionRects.isEmpty())
@@ -319,6 +323,8 @@
ts.dumpProperty("caretColor", editorState.postLayoutData().caretColor);
#endif
#if PLATFORM(MAC)
+ if (editorState.postLayoutData().selectionBoundingRect != IntRect())
+ ts.dumpProperty("selectionBoundingRect", editorState.postLayoutData().selectionBoundingRect);
if (editorState.postLayoutData().candidateRequestStartPosition)
ts.dumpProperty("candidateRequestStartPosition", editorState.postLayoutData().candidateRequestStartPosition);
if (editorState.postLayoutData().paragraphContextForCandidateRequest.length())
Modified: trunk/Source/WebKit/Shared/EditorState.h (266050 => 266051)
--- trunk/Source/WebKit/Shared/EditorState.h 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/Shared/EditorState.h 2020-08-24 01:51:56 UTC (rev 266051)
@@ -82,7 +82,6 @@
WebCore::IntRect caretRectAtStart;
#endif
#if PLATFORM(COCOA)
- WebCore::IntRect focusedElementRect;
uint64_t selectedTextLength { 0 };
uint32_t textAlignment { NoAlignment };
WebCore::Color textColor { WebCore::Color::black };
@@ -90,6 +89,7 @@
WebCore::WritingDirection baseWritingDirection { WebCore::WritingDirection::Natural };
#endif
#if PLATFORM(IOS_FAMILY)
+ WebCore::IntRect selectionClipRect;
WebCore::IntRect caretRectAtEnd;
Vector<WebCore::SelectionRect> selectionRects;
Vector<WebCore::SelectionRect> markedTextRects;
@@ -112,6 +112,7 @@
bool selectionEndIsAtParagraphBoundary { false };
#endif
#if PLATFORM(MAC)
+ WebCore::IntRect selectionBoundingRect;
uint64_t candidateRequestStartPosition { 0 };
String paragraphContextForCandidateRequest;
String stringForCandidateRequest;
Modified: trunk/Source/WebKit/UIProcess/API/mac/WKWebViewTestingMac.mm (266050 => 266051)
--- trunk/Source/WebKit/UIProcess/API/mac/WKWebViewTestingMac.mm 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/UIProcess/API/mac/WKWebViewTestingMac.mm 2020-08-24 01:51:56 UTC (rev 266051)
@@ -80,7 +80,7 @@
- (NSRect)_candidateRect
{
- return _page->editorState().postLayoutData().focusedElementRect;
+ return _page->editorState().postLayoutData().selectionBoundingRect;
}
- (void)viewDidChangeEffectiveAppearance
Modified: trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm (266050 => 266051)
--- trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/UIProcess/Cocoa/WebViewImpl.mm 2020-08-24 01:51:56 UTC (rev 266051)
@@ -3381,7 +3381,7 @@
#if HAVE(TOUCH_BAR)
NSRange selectedRange = NSMakeRange(postLayoutData.candidateRequestStartPosition, postLayoutData.selectedTextLength);
- WebCore::IntRect offsetSelectionRect = postLayoutData.focusedElementRect;
+ WebCore::IntRect offsetSelectionRect = postLayoutData.selectionBoundingRect;
offsetSelectionRect.move(0, offsetSelectionRect.height());
[candidateListTouchBarItem() setCandidates:candidates forSelectedRange:selectedRange inString:postLayoutData.paragraphContextForCandidateRequest rect:offsetSelectionRect view:m_view.getAutoreleased() completionHandler:nil];
Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (266050 => 266051)
--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2020-08-24 01:51:56 UTC (rev 266051)
@@ -215,7 +215,7 @@
auto& postLayoutData = editorState.postLayoutData();
caretRect = postLayoutData.caretRectAtEnd;
selectionRects = postLayoutData.selectionRects;
- selectionClipRect = postLayoutData.focusedElementRect;
+ selectionClipRect = postLayoutData.selectionClipRect;
}
inline bool operator==(const WKSelectionDrawingInfo& a, const WKSelectionDrawingInfo& b)
@@ -2056,13 +2056,13 @@
- (CGRect)_selectionClipRect
{
- if (!self._hasFocusedElement)
- return CGRectNull;
-
if (_page->waitingForPostLayoutEditorStateUpdateAfterFocusingElement())
return _focusedElementInformation.interactionRect;
- return _page->editorState().postLayoutData().focusedElementRect;
+ if (!_page->editorState().postLayoutData().selectionClipRect.isEmpty())
+ return _page->editorState().postLayoutData().selectionClipRect;
+
+ return CGRectNull;
}
static BOOL isBuiltInScrollViewGestureRecognizer(UIGestureRecognizer *recognizer)
@@ -6586,7 +6586,7 @@
editableRootIsTransparentOrFullyClipped = YES;
if (self._hasFocusedElement) {
- auto elementArea = postLayoutData.focusedElementRect.area<RecordOverflow>();
+ auto elementArea = postLayoutData.selectionClipRect.area<RecordOverflow>();
if (!elementArea.hasOverflowed() && elementArea < minimumFocusedElementAreaForSuppressingSelectionAssistant)
focusedElementIsTooSmall = YES;
}
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (266050 => 266051)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2020-08-24 01:51:56 UTC (rev 266051)
@@ -312,16 +312,16 @@
postLayoutData.insideFixedPosition = startNodeIsInsideFixedPosition || endNodeIsInsideFixedPosition;
if (!selection.isNone()) {
if (m_focusedElement && m_focusedElement->renderer()) {
+ // FIXME: The caret color style should be computed using the selection caret's container
+ // rather than the focused element. This causes caret colors in editable children to be
+ // ignored in favor of the editing host's caret color.
auto& renderer = *m_focusedElement->renderer();
- postLayoutData.focusedElementRect = rootViewInteractionBoundsForElement(*m_focusedElement);
postLayoutData.caretColor = CaretBase::computeCaretColor(renderer.style(), renderer.element());
}
- if (result.isContentEditable) {
- if (auto editableRootOrFormControl = makeRefPtr(selection.rootEditableElement())) {
- if (is<HTMLTextFormControlElement>(editableRootOrFormControl->shadowHost()))
- editableRootOrFormControl = editableRootOrFormControl->shadowHost();
- postLayoutData.editableRootIsTransparentOrFullyClipped = isTransparentOrFullyClipped(*editableRootOrFormControl);
- }
+
+ if (auto editableRootOrFormControl = makeRefPtr(enclosingTextFormControl(selection.start()) ?: selection.rootEditableElement())) {
+ postLayoutData.selectionClipRect = rootViewInteractionBoundsForElement(*editableRootOrFormControl);
+ postLayoutData.editableRootIsTransparentOrFullyClipped = result.isContentEditable && isTransparentOrFullyClipped(*editableRootOrFormControl);
}
computeEditableRootHasContentAndPlainText(selection, postLayoutData);
postLayoutData.selectionStartIsAtParagraphBoundary = atBoundaryOfGranularity(selection.visibleStart(), TextGranularity::ParagraphGranularity, SelectionDirection::Backward);
Modified: trunk/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm (266050 => 266051)
--- trunk/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Source/WebKit/WebProcess/WebPage/mac/WebPageMac.mm 2020-08-24 01:51:56 UTC (rev 266051)
@@ -165,11 +165,10 @@
auto quads = RenderObject::absoluteTextQuads(*selectedRange);
if (!quads.isEmpty())
- postLayoutData.focusedElementRect = frame.view()->contentsToWindow(quads[0].enclosingBoundingBox());
- else {
+ postLayoutData.selectionBoundingRect = frame.view()->contentsToWindow(quads[0].enclosingBoundingBox());
+ else if (selection.isCaret()) {
// Quads will be empty at the start of a paragraph.
- if (selection.isCaret())
- postLayoutData.focusedElementRect = frame.view()->contentsToWindow(frame.selection().absoluteCaretBounds());
+ postLayoutData.selectionBoundingRect = frame.view()->contentsToWindow(frame.selection().absoluteCaretBounds());
}
}
Modified: trunk/Tools/ChangeLog (266050 => 266051)
--- trunk/Tools/ChangeLog 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Tools/ChangeLog 2020-08-24 01:51:56 UTC (rev 266051)
@@ -1,3 +1,22 @@
+2020-08-23 Wenson Hsieh <wenson_hs...@apple.com>
+
+ Programmatic selection of text in a text field causes the highlight overlay to spill out
+ https://bugs.webkit.org/show_bug.cgi?id=215647
+ <rdar://problem/67404979>
+
+ Reviewed by Darin Adler.
+
+ * WebKitTestRunner/ios/UIScriptControllerIOS.mm:
+ (WTR::clipSelectionViewRectToContentView):
+
+ Add a helper function to clip UIKit selection UI rects (for carets, selection grabbers, and ranged selection
+ highlights) to the content view's (WKContentView's) bounds, as well as the selection clip rect if it is nonnull.
+
+ (WTR::UIScriptControllerIOS::selectionStartGrabberViewRect const):
+ (WTR::UIScriptControllerIOS::selectionEndGrabberViewRect const):
+ (WTR::UIScriptControllerIOS::selectionCaretViewRect const):
+ (WTR::UIScriptControllerIOS::selectionRangeViewRects const):
+
2020-08-17 Darin Adler <da...@apple.com>
Create documentOrder function, start refactoring to use it instead of Range::compare functions
Modified: trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm (266050 => 266051)
--- trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm 2020-08-24 01:30:57 UTC (rev 266050)
+++ trunk/Tools/WebKitTestRunner/ios/UIScriptControllerIOS.mm 2020-08-24 01:51:56 UTC (rev 266051)
@@ -790,14 +790,24 @@
return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:toNSDictionary(webView()._uiTextCaretRect) inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
}
+static void clipSelectionViewRectToContentView(CGRect& rect, UIView *contentView)
+{
+ rect = CGRectIntersection(contentView.bounds, rect);
+ // The content view (a WKContentView in WebKit) is expected to implement the optional UITextInputPrivate method -_selectionClipRect.
+ ASSERT([contentView respondsToSelector:@selector(_selectionClipRect)]);
+ auto selectionClipRect = [(UIView <UITextInputPrivate> *)contentView _selectionClipRect];
+ if (!CGRectIsNull(selectionClipRect))
+ rect = CGRectIntersection(selectionClipRect, rect);
+}
+
JSObjectRef UIScriptControllerIOS::selectionStartGrabberViewRect() const
{
UIView *contentView = platformContentView();
UIView *selectionRangeView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.rangeView"];
- auto frameInContentCoordinates = [selectionRangeView convertRect:[[selectionRangeView valueForKeyPath:@"startGrabber"] frame] toView:contentView];
- frameInContentCoordinates = CGRectIntersection(contentView.bounds, frameInContentCoordinates);
+ auto frameInContentViewCoordinates = [selectionRangeView convertRect:[[selectionRangeView valueForKeyPath:@"startGrabber"] frame] toView:contentView];
+ clipSelectionViewRectToContentView(frameInContentViewCoordinates, contentView);
auto jsContext = m_context->jsContext();
- return JSValueToObject(jsContext, [JSValue valueWithObject:toNSDictionary(frameInContentCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:jsContext]].JSValueRef, nullptr);
+ return JSValueToObject(jsContext, [JSValue valueWithObject:toNSDictionary(frameInContentViewCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:jsContext]].JSValueRef, nullptr);
}
JSObjectRef UIScriptControllerIOS::selectionEndGrabberViewRect() const
@@ -804,10 +814,10 @@
{
UIView *contentView = platformContentView();
UIView *selectionRangeView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.rangeView"];
- auto frameInContentCoordinates = [selectionRangeView convertRect:[[selectionRangeView valueForKeyPath:@"endGrabber"] frame] toView:contentView];
- frameInContentCoordinates = CGRectIntersection(contentView.bounds, frameInContentCoordinates);
+ auto frameInContentViewCoordinates = [selectionRangeView convertRect:[[selectionRangeView valueForKeyPath:@"endGrabber"] frame] toView:contentView];
+ clipSelectionViewRectToContentView(frameInContentViewCoordinates, contentView);
auto jsContext = m_context->jsContext();
- return JSValueToObject(jsContext, [JSValue valueWithObject:toNSDictionary(frameInContentCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:jsContext]].JSValueRef, nullptr);
+ return JSValueToObject(jsContext, [JSValue valueWithObject:toNSDictionary(frameInContentViewCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:jsContext]].JSValueRef, nullptr);
}
JSObjectRef UIScriptControllerIOS::selectionCaretViewRect() const
@@ -815,6 +825,7 @@
UIView *contentView = platformContentView();
UIView *caretView = [contentView valueForKeyPath:@"interactionAssistant.selectionView.caretView"];
auto rectInContentViewCoordinates = CGRectIntersection([caretView convertRect:caretView.bounds toView:contentView], contentView.bounds);
+ clipSelectionViewRectToContentView(rectInContentViewCoordinates, contentView);
return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:toNSDictionary(rectInContentViewCoordinates) inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);
}
@@ -827,6 +838,7 @@
for (id textRectInfo in textRectInfoArray) {
NSValue *rectValue = [textRectInfo valueForKeyPath:@"rect"];
auto rangeRectInContentViewCoordinates = [rangeView convertRect:rectValue.CGRectValue toView:contentView];
+ clipSelectionViewRectToContentView(rangeRectInContentViewCoordinates, contentView);
[rectsAsDictionaries addObject:toNSDictionary(CGRectIntersection(rangeRectInContentViewCoordinates, contentView.bounds))];
}
return JSValueToObject(m_context->jsContext(), [JSValue valueWithObject:rectsAsDictionaries.get() inContext:[JSContext contextWithJSGlobalContextRef:m_context->jsContext()]].JSValueRef, nullptr);