Diff
Modified: trunk/Source/WebCore/ChangeLog (221232 => 221233)
--- trunk/Source/WebCore/ChangeLog 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebCore/ChangeLog 2017-08-28 01:22:55 UTC (rev 221233)
@@ -1,3 +1,33 @@
+2017-08-27 Wenson Hsieh <[email protected]>
+
+ [iOS WK2] Web process crashes after changing selection to the end of the document when speaking a selection
+ https://bugs.webkit.org/show_bug.cgi?id=176011
+ <rdar://problem/32614095>
+
+ Reviewed by Ryosuke Niwa.
+
+ Adds a null check to visiblePositionForPositionWithOffset. This is a crash point for accessibility codepaths,
+ since indexForVisiblePosition is not guaranteed to set the given `root` outparam to a non-null value, yet
+ visiblePositionForIndex requires root to be non-null. This causes a crash when selecting some text, hitting
+ 'Speak', and then changing the selection to somewhere near the end of the document, since accessibility code
+ will attempt to speak words at an offset past the end of the document. While this is a bug in and of itself, the
+ web process should still handle this case gracefully and not crash. To fix this, we simply bail and return a
+ null VisiblePosition if a root container node was not found.
+
+ Currently, visiblePositionForPositionWithOffset is implemented twice, in WebCore (AXObjectCache.cpp) and also in
+ WebKit (WebPageIOS.mm), as identical static functions. This patch moves this helper into Editing.cpp and removes
+ it from AXObjectCache and WebPageIOS.
+
+ Tests: AccessibilityTests.RectsForSpeakingSelectionBasic
+ AccessibilityTests.RectsForSpeakingSelectionWithLineWrapping
+ AccessibilityTests.RectsForSpeakingSelectionDoNotCrashWhenChangingSelection
+
+ * accessibility/AXObjectCache.cpp:
+ (WebCore::visiblePositionForPositionWithOffset): Deleted.
+ * editing/Editing.cpp:
+ (WebCore::visiblePositionForPositionWithOffset):
+ * editing/Editing.h:
+
2017-08-27 Devin Rousso <[email protected]>
Web Inspector: Record actions performed on WebGLRenderingContext
Modified: trunk/Source/WebCore/accessibility/AXObjectCache.cpp (221232 => 221233)
--- trunk/Source/WebCore/accessibility/AXObjectCache.cpp 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.cpp 2017-08-28 01:22:55 UTC (rev 221233)
@@ -1727,13 +1727,6 @@
return WTFMove(range);
}
-static VisiblePosition visiblePositionForPositionWithOffset(const VisiblePosition& position, int32_t offset)
-{
- RefPtr<ContainerNode> root;
- unsigned startIndex = indexForVisiblePosition(position, root);
- return visiblePositionForIndex(startIndex + offset, root.get());
-}
-
RefPtr<Range> AXObjectCache::rangeMatchesTextNearRange(RefPtr<Range> originalRange, const String& matchText)
{
if (!originalRange)
Modified: trunk/Source/WebCore/editing/Editing.cpp (221232 => 221233)
--- trunk/Source/WebCore/editing/Editing.cpp 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebCore/editing/Editing.cpp 2017-08-28 01:22:55 UTC (rev 221233)
@@ -1092,6 +1092,16 @@
return TextIterator::rangeLength(range.ptr(), forSelectionPreservation);
}
+VisiblePosition visiblePositionForPositionWithOffset(const VisiblePosition& position, int offset)
+{
+ RefPtr<ContainerNode> root;
+ unsigned startIndex = indexForVisiblePosition(position, root);
+ if (!root)
+ return { };
+
+ return visiblePositionForIndex(startIndex + offset, root.get());
+}
+
VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope)
{
auto range = TextIterator::rangeFromLocationAndLength(scope, index, 0, true);
Modified: trunk/Source/WebCore/editing/Editing.h (221232 => 221233)
--- trunk/Source/WebCore/editing/Editing.h 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebCore/editing/Editing.h 2017-08-28 01:22:55 UTC (rev 221233)
@@ -146,6 +146,7 @@
WEBCORE_EXPORT int indexForVisiblePosition(const VisiblePosition&, RefPtr<ContainerNode>& scope);
int indexForVisiblePosition(Node&, const VisiblePosition&, bool forSelectionPreservation);
+WEBCORE_EXPORT VisiblePosition visiblePositionForPositionWithOffset(const VisiblePosition&, int offset);
WEBCORE_EXPORT VisiblePosition visiblePositionForIndex(int index, ContainerNode* scope);
VisiblePosition visiblePositionForIndexUsingCharacterIterator(Node&, int index); // FIXME: Why do we need this version?
Modified: trunk/Source/WebKit/ChangeLog (221232 => 221233)
--- trunk/Source/WebKit/ChangeLog 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebKit/ChangeLog 2017-08-28 01:22:55 UTC (rev 221233)
@@ -1,3 +1,26 @@
+2017-08-27 Wenson Hsieh <[email protected]>
+
+ [iOS WK2] Web process crashes after changing selection to the end of the document when speaking a selection
+ https://bugs.webkit.org/show_bug.cgi?id=176011
+ <rdar://problem/32614095>
+
+ Reviewed by Ryosuke Niwa.
+
+ Adds an SPI hook to test accessibility codepaths when speaking selected content. This patch does some minor
+ refactoring by introducing _accessibilityRetrieveRectsAtSelectionOffset:withText:completionHandler:, which takes
+ and invokes a completion handler block. The existing _accessibilityRetrieveRectsAtSelectionOffset:withText:
+ method simply turns around and calls the former variant with `nil` as a completion handler.
+
+ * UIProcess/API/Cocoa/WKWebView.mm:
+ (-[WKWebView _accessibilityRetrieveRectsAtSelectionOffset:withText:completionHandler:]):
+ * UIProcess/API/Cocoa/WKWebViewPrivate.h:
+ * UIProcess/ios/WKContentViewInteraction.h:
+ * UIProcess/ios/WKContentViewInteraction.mm:
+ (-[WKContentView _accessibilityRetrieveRectsAtSelectionOffset:withText:]):
+ (-[WKContentView _accessibilityRetrieveRectsAtSelectionOffset:withText:completionHandler:]):
+ * WebProcess/WebPage/ios/WebPageIOS.mm:
+ (WebKit::visiblePositionForPositionWithOffset): Deleted.
+
2017-08-25 Alex Christensen <[email protected]>
Add WKUIDelegatePrivate equivalent of WKPageUIClient's saveDataToFileInDownloadsFolder
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm (221232 => 221233)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebView.mm 2017-08-28 01:22:55 UTC (rev 221233)
@@ -5401,6 +5401,18 @@
} forRequest:infoRequest];
}
+- (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text completionHandler:(void (^)(NSArray<NSValue *> *rects))completionHandler
+{
+ [_contentView _accessibilityRetrieveRectsAtSelectionOffset:offset withText:text completionHandler:[capturedCompletionHandler = makeBlockPtr(completionHandler)] (const Vector<WebCore::SelectionRect>& selectionRects) {
+ if (!capturedCompletionHandler)
+ return;
+ auto rectValues = adoptNS([[NSMutableArray alloc] initWithCapacity:selectionRects.size()]);
+ for (auto& selectionRect : selectionRects)
+ [rectValues addObject:[NSValue valueWithCGRect:selectionRect.rect()]];
+ capturedCompletionHandler(rectValues.get());
+ }];
+}
+
- (CGRect)_contentVisibleRect
{
return [self convertRect:[self bounds] toView:self._currentContentView];
Modified: trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h (221232 => 221233)
--- trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebKit/UIProcess/API/Cocoa/WKWebViewPrivate.h 2017-08-28 01:22:55 UTC (rev 221233)
@@ -377,6 +377,7 @@
@property (nonatomic, readonly) CGRect _dragCaretRect WK_API_AVAILABLE(ios(WK_IOS_TBA));
- (void)_requestActivatedElementAtPosition:(CGPoint)position completionBlock:(void (^)(_WKActivatedElementInfo *))block WK_API_AVAILABLE(ios(WK_IOS_TBA));
+- (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text completionHandler:(void (^)(NSArray<NSValue *> *rects))completionHandler WK_API_AVAILABLE(ios(WK_IOS_TBA));
#endif // TARGET_OS_IPHONE
Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h (221232 => 221233)
--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.h 2017-08-28 01:22:55 UTC (rev 221233)
@@ -58,6 +58,7 @@
class Color;
class FloatQuad;
class IntSize;
+class SelectionRect;
}
#if ENABLE(DRAG_SUPPORT)
@@ -331,6 +332,7 @@
- (NSArray<NSValue *> *)_uiTextSelectionRects;
- (void)accessibilityRetrieveSpeakSelectionContent;
- (void)_accessibilityRetrieveRectsEnclosingSelectionOffset:(NSInteger)offset withGranularity:(UITextGranularity)granularity;
+- (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text completionHandler:(void (^)(const Vector<WebCore::SelectionRect>& rects))completionHandler;
- (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text;
@property (nonatomic, readonly) WebKit::InteractionInformationAtPosition currentPositionInformation;
Modified: trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm (221232 => 221233)
--- trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebKit/UIProcess/ios/WKContentViewInteraction.mm 2017-08-28 01:22:55 UTC (rev 221233)
@@ -2280,8 +2280,16 @@
- (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text
{
+ [self _accessibilityRetrieveRectsAtSelectionOffset:offset withText:text completionHandler:nil];
+}
+
+- (void)_accessibilityRetrieveRectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text completionHandler:(void (^)(const Vector<SelectionRect>& rects))completionHandler
+{
RetainPtr<WKContentView> view = self;
- _page->requestRectsAtSelectionOffsetWithText(offset, text, [view, offset](const Vector<WebCore::SelectionRect>& selectionRects, CallbackBase::Error error) {
+ _page->requestRectsAtSelectionOffsetWithText(offset, text, [view, offset, capturedCompletionHandler = makeBlockPtr(completionHandler)](const Vector<SelectionRect>& selectionRects, CallbackBase::Error error) {
+ if (capturedCompletionHandler)
+ capturedCompletionHandler(selectionRects);
+
if (error != WebKit::CallbackBase::Error::None)
return;
if ([view respondsToSelector:@selector(_accessibilityDidGetSelectionRects:withGranularity:atOffset:)])
Modified: trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm (221232 => 221233)
--- trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Source/WebKit/WebProcess/WebPage/ios/WebPageIOS.mm 2017-08-28 01:22:55 UTC (rev 221233)
@@ -1843,13 +1843,6 @@
send(Messages::WebPageProxy::VoidCallback(callbackID));
}
-static VisiblePosition visiblePositionForPositionWithOffset(const VisiblePosition& position, int32_t offset)
-{
- RefPtr<ContainerNode> root;
- unsigned startIndex = indexForVisiblePosition(position, root);
- return visiblePositionForIndex(startIndex + offset, root.get());
-}
-
void WebPage::getRectsForGranularityWithSelectionOffset(uint32_t granularity, int32_t offset, CallbackID callbackID)
{
Frame& frame = m_page->focusController().focusedOrMainFrame();
Modified: trunk/Tools/ChangeLog (221232 => 221233)
--- trunk/Tools/ChangeLog 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Tools/ChangeLog 2017-08-28 01:22:55 UTC (rev 221233)
@@ -1,3 +1,20 @@
+2017-08-27 Wenson Hsieh <[email protected]>
+
+ [iOS WK2] Web process crashes after changing selection to the end of the document when speaking a selection
+ https://bugs.webkit.org/show_bug.cgi?id=176011
+ <rdar://problem/32614095>
+
+ Reviewed by Ryosuke Niwa.
+
+ Introduces AccessibilityTests, and adds three new tests that traverse selection-rect-finding codepaths when
+ speaking selected content. See WebKit and WebCore ChangeLogs for more detail.
+
+ * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+ * TestWebKitAPI/Tests/ios/AccessibilityTestsIOS.mm: Added.
+ (-[WKWebView rectsAtSelectionOffset:withText:]):
+ (checkCGRectValueAtIndex):
+ (TestWebKitAPI::TEST):
+
2017-08-25 Eric Carlson <[email protected]>
Add Logger::logAlways
Modified: trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj (221232 => 221233)
--- trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-08-27 16:12:39 UTC (rev 221232)
+++ trunk/Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj 2017-08-28 01:22:55 UTC (rev 221233)
@@ -86,6 +86,7 @@
2E1DFDED1D42A51100714A00 /* large-videos-with-audio.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1DFDEC1D42A41C00714A00 /* large-videos-with-audio.html */; };
2E1DFDEF1D42A6F200714A00 /* large-videos-with-audio-autoplay.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1DFDEE1D42A6EB00714A00 /* large-videos-with-audio-autoplay.html */; };
2E1DFDF11D42E1E400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E1DFDF01D42E14400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html */; };
+ 2E205BA41F527746005952DD /* AccessibilityTestsIOS.mm in Sources */ = {isa = PBXBuildFile; fileRef = 2E205BA31F527746005952DD /* AccessibilityTestsIOS.mm */; };
2E54F40D1D7BC84200921ADF /* large-video-mutes-onplaying.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E54F40C1D7BC83900921ADF /* large-video-mutes-onplaying.html */; };
2E691AEA1D78B53600129407 /* large-videos-paused-video-hides-controls.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E691AE81D78B52B00129407 /* large-videos-paused-video-hides-controls.html */; };
2E691AEB1D78B53600129407 /* large-videos-playing-video-keeps-controls.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 2E691AE91D78B52B00129407 /* large-videos-playing-video-keeps-controls.html */; };
@@ -1109,6 +1110,7 @@
2E1DFDEC1D42A41C00714A00 /* large-videos-with-audio.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-videos-with-audio.html"; sourceTree = "<group>"; };
2E1DFDEE1D42A6EB00714A00 /* large-videos-with-audio-autoplay.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-videos-with-audio-autoplay.html"; sourceTree = "<group>"; };
2E1DFDF01D42E14400714A00 /* large-video-seek-to-beginning-and-play-after-ending.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-video-seek-to-beginning-and-play-after-ending.html"; sourceTree = "<group>"; };
+ 2E205BA31F527746005952DD /* AccessibilityTestsIOS.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = AccessibilityTestsIOS.mm; sourceTree = "<group>"; };
2E54F40C1D7BC83900921ADF /* large-video-mutes-onplaying.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-video-mutes-onplaying.html"; sourceTree = "<group>"; };
2E691AE81D78B52B00129407 /* large-videos-paused-video-hides-controls.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-videos-paused-video-hides-controls.html"; sourceTree = "<group>"; };
2E691AE91D78B52B00129407 /* large-videos-playing-video-keeps-controls.html */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.html; path = "large-videos-playing-video-keeps-controls.html"; sourceTree = "<group>"; };
@@ -2067,6 +2069,7 @@
isa = PBXGroup;
children = (
A1C4FB6F1BACCEFA003742D0 /* Resources */,
+ 2E205BA31F527746005952DD /* AccessibilityTestsIOS.mm */,
F45B63FC1F19D410009D38B9 /* ActionSheetTests.mm */,
F4D4F3B71E4E36E400BB2767 /* DataInteractionTests.mm */,
7560917719259C59009EF06E /* MemoryCacheAddImageToCacheIOS.mm */,
@@ -3074,6 +3077,7 @@
7CEFA9661AC0B9E200B910FD /* _WKUserContentExtensionStore.mm in Sources */,
7CCE7EE41A411AE600447C4C /* AboutBlankLoad.cpp in Sources */,
7CCE7EB31A411A7E00447C4C /* AcceptsFirstMouse.mm in Sources */,
+ 2E205BA41F527746005952DD /* AccessibilityTestsIOS.mm in Sources */,
F45B63FE1F19D410009D38B9 /* ActionSheetTests.mm in Sources */,
37E7DD641EA06FF2009B396D /* AdditionalReadAccessAllowedURLs.mm in Sources */,
7A909A7D1D877480007E10F8 /* AffineTransform.cpp in Sources */,
Added: trunk/Tools/TestWebKitAPI/Tests/ios/AccessibilityTestsIOS.mm (0 => 221233)
--- trunk/Tools/TestWebKitAPI/Tests/ios/AccessibilityTestsIOS.mm (rev 0)
+++ trunk/Tools/TestWebKitAPI/Tests/ios/AccessibilityTestsIOS.mm 2017-08-28 01:22:55 UTC (rev 221233)
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2017 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
+ * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
+ * THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if PLATFORM(IOS) && WK_API_ENABLED
+
+#import "PlatformUtilities.h"
+#import "TestWKWebView.h"
+#import <WebKit/WKWebViewPrivate.h>
+
+@implementation WKWebView (WKAccessibilityTesting)
+- (NSArray<NSValue *> *)rectsAtSelectionOffset:(NSInteger)offset withText:(NSString *)text
+{
+ __block RetainPtr<NSArray> selectionRects;
+ __block bool done = false;
+ [self _accessibilityRetrieveRectsAtSelectionOffset:offset withText:text completionHandler:^(NSArray<NSValue *> *rects) {
+ selectionRects = rects;
+ done = true;
+ }];
+ TestWebKitAPI::Util::run(&done);
+ return selectionRects.autorelease();
+}
+@end
+
+static void checkCGRectValueAtIndex(NSArray<NSValue *> *rectValues, CGRect expectedRect, NSUInteger index)
+{
+ EXPECT_LT(index, rectValues.count);
+ auto observedRect = [rectValues[index] CGRectValue];
+ EXPECT_EQ(expectedRect.origin.x, observedRect.origin.x);
+ EXPECT_EQ(expectedRect.origin.y, observedRect.origin.y);
+ EXPECT_EQ(expectedRect.size.width, observedRect.size.width);
+ EXPECT_EQ(expectedRect.size.height, observedRect.size.height);
+}
+
+namespace TestWebKitAPI {
+
+TEST(AccessibilityTests, RectsForSpeakingSelectionBasic)
+{
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+ [webView synchronouslyLoadHTMLString:@"<meta name='viewport' content='width=device-width,initial-scale=1'><span id='first'>first</span><span id='second'> second</span><br><span id='third'> third</span>"];
+ [webView stringByEvaluatingJavaScript:@"document.execCommand('SelectAll')"];
+
+ checkCGRectValueAtIndex([webView rectsAtSelectionOffset:0 withText:@"first"], CGRectMake(8, 8, 26, 19), 0);
+ checkCGRectValueAtIndex([webView rectsAtSelectionOffset:6 withText:@"second"], CGRectMake(37, 8, 46, 19), 0);
+ checkCGRectValueAtIndex([webView rectsAtSelectionOffset:13 withText:@"third"], CGRectMake(8, 27, 31, 20), 0);
+}
+
+TEST(AccessibilityTests, RectsForSpeakingSelectionWithLineWrapping)
+{
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+ [webView synchronouslyLoadHTMLString:@"<meta name='viewport' content='width=device-width,initial-scale=1'><body style='font-size: 100px; word-wrap: break-word'><span id='text'>abcdefghijklmnopqrstuvwxyz</span></body>"];
+ [webView stringByEvaluatingJavaScript:@"document.execCommand('SelectAll')"];
+
+ NSArray<NSValue *> *rects = [webView rectsAtSelectionOffset:0 withText:@"abcdefghijklmnopqrstuvwxyz"];
+ checkCGRectValueAtIndex(rects, CGRectMake(8, 8, 304, 114), 0);
+ checkCGRectValueAtIndex(rects, CGRectMake(8, 122, 304, 117), 1);
+ checkCGRectValueAtIndex(rects, CGRectMake(8, 239, 304, 117), 2);
+ checkCGRectValueAtIndex(rects, CGRectMake(8, 356, 304, 117), 3);
+ checkCGRectValueAtIndex(rects, CGRectMake(8, 473, 145, 117), 4);
+}
+
+TEST(AccessibilityTests, RectsForSpeakingSelectionDoNotCrashWhenChangingSelection)
+{
+ auto webView = adoptNS([[TestWKWebView alloc] initWithFrame:CGRectMake(0, 0, 320, 500)]);
+ [webView synchronouslyLoadHTMLString:@"<meta name='viewport' content='width=device-width,initial-scale=1'><span id='first'>first</span><span id='second'> second</span><br><span id='third'> third</span>"];
+
+ [webView stringByEvaluatingJavaScript:@"getSelection().setBaseAndExtent(third, 0, third, 1)"];
+ EXPECT_EQ(0UL, [webView rectsAtSelectionOffset:13 withText:@"third"].count);
+ EXPECT_WK_STREQ("third", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]);
+
+ [webView stringByEvaluatingJavaScript:@"getSelection().removeAllRanges()"];
+ EXPECT_EQ(0UL, [webView rectsAtSelectionOffset:13 withText:@"third"].count);
+ EXPECT_WK_STREQ("", [webView stringByEvaluatingJavaScript:@"getSelection().toString()"]);
+}
+
+} // namespace TestWebKitAPI
+
+#endif // PLATFORM(IOS) && WK_API_ENABLED
+