Title: [221233] trunk
Revision
221233
Author
[email protected]
Date
2017-08-27 18:22:55 -0700 (Sun, 27 Aug 2017)

Log Message

[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.

Source/WebCore:

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:

Source/WebKit:

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.

Tools:

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):

Modified Paths

Added Paths

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
+
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to