AX: [iOS] Implement a way to retrieve a text marker range with desired text that...
authorn_wang@apple.com <n_wang@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Jul 2017 17:58:25 +0000 (17:58 +0000)
committern_wang@apple.com <n_wang@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 12 Jul 2017 17:58:25 +0000 (17:58 +0000)
https://bugs.webkit.org/show_bug.cgi?id=174393
<rdar://problem/33248006>

Reviewed by Chris Fleizach.

Source/WebCore:

Used the existing findClosestPlainText function to search the range on iOS.
Also exposed a function on the iOS wrapper to return the selection rects of
the result range from the searching.

Test: accessibility/ios-simulator/text-marker-range-matches-text.html

* accessibility/AXObjectCache.cpp:
(WebCore::visiblePositionForPositionWithOffset):
(WebCore::AXObjectCache::rangeMatchesTextNearRange):
* accessibility/AXObjectCache.h:
* accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
(-[WebAccessibilityObjectWrapper rangeFromMarkers:withText:]):
(-[WebAccessibilityObjectWrapper textMarkerRangeFromMarkers:withText:]):
(-[WebAccessibilityObjectWrapper textRectsFromMarkers:withText:]):
(-[WebAccessibilityObjectWrapper rectsForSelectionRects:]):

Tools:

* DumpRenderTree/AccessibilityUIElement.cpp:
(textMarkerRangeMatchesTextNearMarkersCallback):
(AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers):
(AccessibilityUIElement::getJSClass):
* DumpRenderTree/AccessibilityUIElement.h:
* DumpRenderTree/ios/AccessibilityUIElementIOS.mm:
(AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers):
* WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp:
(WTR::AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers):
* WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
* WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl:
* WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:
(WTR::AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers):

LayoutTests:

* accessibility/ios-simulator/text-marker-range-matches-text-expected.txt: Added.
* accessibility/ios-simulator/text-marker-range-matches-text.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@219409 268f45cc-cd09-0410-ab3c-d52691b4dbfc

15 files changed:
LayoutTests/ChangeLog
LayoutTests/accessibility/ios-simulator/text-marker-range-matches-text-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/ios-simulator/text-marker-range-matches-text.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AXObjectCache.h
Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm
Tools/ChangeLog
Tools/DumpRenderTree/AccessibilityUIElement.cpp
Tools/DumpRenderTree/AccessibilityUIElement.h
Tools/DumpRenderTree/ios/AccessibilityUIElementIOS.mm
Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp
Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h
Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl
Tools/WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm

index c1ef8cc..3ec73bd 100644 (file)
@@ -1,3 +1,14 @@
+2017-07-12  Nan Wang  <n_wang@apple.com>
+
+        AX: [iOS] Implement a way to retrieve a text marker range with desired text that is closest to a position
+        https://bugs.webkit.org/show_bug.cgi?id=174393
+        <rdar://problem/33248006>
+
+        Reviewed by Chris Fleizach.
+
+        * accessibility/ios-simulator/text-marker-range-matches-text-expected.txt: Added.
+        * accessibility/ios-simulator/text-marker-range-matches-text.html: Added.
+
 2017-07-12  Per Arne Vollan  <pvollan@apple.com>
 
         Skip WebRTC tests on Windows.
diff --git a/LayoutTests/accessibility/ios-simulator/text-marker-range-matches-text-expected.txt b/LayoutTests/accessibility/ios-simulator/text-marker-range-matches-text-expected.txt
new file mode 100644 (file)
index 0000000..c970b01
--- /dev/null
@@ -0,0 +1,13 @@
+This is some testing content.
+This tests that we are able to get a range with desired text that is close to a given range.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS text.stringForTextMarkerRange(markerRange) is 'ome t'
+PASS text.stringForTextMarkerRange(matchedMarkerRange) is 'testing'
+PASS text.stringForTextMarkerRange(matchedMarkerRange) is 'some'
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/ios-simulator/text-marker-range-matches-text.html b/LayoutTests/accessibility/ios-simulator/text-marker-range-matches-text.html
new file mode 100644 (file)
index 0000000..56be16d
--- /dev/null
@@ -0,0 +1,46 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+    <script src="../../resources/js-test-pre.js"></script>
+    <body id="body" tabindex="0">
+        <div tabindex="0" id="text">
+        This is some testing content. 
+        </div>
+
+        <p id="description"></p>
+        <div id="console"></div>
+
+        <script>
+
+            description("This tests that we are able to get a range with desired text that is close to a given range.");
+
+            if (window.accessibilityController) {
+                accessibilityController.enableEnhancedAccessibility(true);
+                var webArea = accessibilityController.rootElement.childAtIndex(0);
+                var text = accessibilityController.accessibleElementById("text").childAtIndex(0);
+
+                // Get the marker range (9, 5), should be "ome t".
+                var startMarker = text.startTextMarkerForTextMarkerRange(text.textMarkerRangeForElement(text));
+                for (var i = 0; i < 9 ; i++)
+                    startMarker = text.nextTextMarker(startMarker);
+                var endMarker = startMarker;
+                for (var i = 0; i < 5 ; i++)
+                    endMarker = text.nextTextMarker(endMarker);
+                    
+                var markerRange = text.textMarkerRangeForMarkers(startMarker, endMarker);
+                shouldBe("text.stringForTextMarkerRange(markerRange)", "'ome t'");
+                
+                // Search forward and backwards.
+                var matchedMarkerRange = text.textMarkerRangeMatchesTextNearMarkers("testing", startMarker, endMarker);
+                shouldBe("text.stringForTextMarkerRange(matchedMarkerRange)", "'testing'");
+                
+                matchedMarkerRange = text.textMarkerRangeMatchesTextNearMarkers("some", startMarker, endMarker);
+                shouldBe("text.stringForTextMarkerRange(matchedMarkerRange)", "'some'");
+            }
+
+            </script>
+
+        <script src="../../resources/js-test-post.js"></script>
+
+    </body>
+</html>
+
index 986bc7d..d376103 100644 (file)
@@ -1,3 +1,27 @@
+2017-07-12  Nan Wang  <n_wang@apple.com>
+
+        AX: [iOS] Implement a way to retrieve a text marker range with desired text that is closest to a position
+        https://bugs.webkit.org/show_bug.cgi?id=174393
+        <rdar://problem/33248006>
+
+        Reviewed by Chris Fleizach.
+
+        Used the existing findClosestPlainText function to search the range on iOS.
+        Also exposed a function on the iOS wrapper to return the selection rects of
+        the result range from the searching. 
+
+        Test: accessibility/ios-simulator/text-marker-range-matches-text.html
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::visiblePositionForPositionWithOffset):
+        (WebCore::AXObjectCache::rangeMatchesTextNearRange):
+        * accessibility/AXObjectCache.h:
+        * accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
+        (-[WebAccessibilityObjectWrapper rangeFromMarkers:withText:]):
+        (-[WebAccessibilityObjectWrapper textMarkerRangeFromMarkers:withText:]):
+        (-[WebAccessibilityObjectWrapper textRectsFromMarkers:withText:]):
+        (-[WebAccessibilityObjectWrapper rectsForSelectionRects:]):
+
 2017-07-12  Daniel Bates  <dabates@apple.com>
 
         NavigationAction should track whether the navigation was initiated by the main frame
index 1ad5d84..ec976d5 100644 (file)
@@ -1726,6 +1726,37 @@ RefPtr<Range> AXObjectCache::rangeForNodeContents(Node* node)
     }
     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)
+        return nullptr;
+    
+    // Create a large enough range for searching the text within.
+    unsigned textLength = matchText.length();
+    auto startPosition = visiblePositionForPositionWithOffset(originalRange->startPosition(), -textLength);
+    auto endPosition = visiblePositionForPositionWithOffset(originalRange->startPosition(), 2 * textLength);
+    
+    if (startPosition.isNull())
+        startPosition = firstPositionInOrBeforeNode(&originalRange->startContainer());
+    if (endPosition.isNull())
+        endPosition = lastPositionInOrAfterNode(&originalRange->endContainer());
+    
+    RefPtr<Range> searchRange = Range::create(m_document, startPosition, endPosition);
+    if (!searchRange || searchRange->collapsed())
+        return nullptr;
+    
+    RefPtr<Range> range = Range::create(m_document, startPosition, originalRange->startPosition());
+    unsigned targetOffset = TextIterator::rangeLength(range.get(), true);
+    return findClosestPlainText(*searchRange.get(), matchText, 0, targetOffset);
+}
 
 static bool isReplacedNodeOrBR(Node* node)
 {
index f3e1058..ab9bc70 100644 (file)
@@ -332,6 +332,9 @@ public:
     void deferRecomputeIsIgnored(Element*);
     void deferTextChangedIfNeeded(Node*);
     void performDeferredCacheUpdate();
+    
+    RefPtr<Range> rangeMatchesTextNearRange(RefPtr<Range>, const String&);
+    
 
 protected:
     void postPlatformNotification(AccessibilityObject*, AXNotification);
index 54f5006..12c80e0 100644 (file)
@@ -52,6 +52,7 @@
 #import "RuntimeApplicationChecks.h"
 #import "SVGNames.h"
 #import "SVGElement.h"
+#import "SelectionRect.h"
 #import "TextIterator.h"
 #import "WAKScrollView.h"
 #import "WAKView.h"
@@ -2588,6 +2589,56 @@ static void AXAttributedStringAppendText(NSMutableAttributedString* attrString,
     return [self convertRectToScreenSpace:rect];
 }
 
+- (RefPtr<Range>)rangeFromMarkers:(NSArray *)markers withText:(NSString *)text
+{
+    RefPtr<Range> originalRange = [self rangeForTextMarkers:markers];
+    if (!originalRange)
+        return nil;
+    
+    AXObjectCache* cache = m_object->axObjectCache();
+    if (!cache)
+        return nil;
+    
+    return cache->rangeMatchesTextNearRange(originalRange, text);
+}
+
+// This is only used in the layout test.
+- (NSArray *)textMarkerRangeFromMarkers:(NSArray *)markers withText:(NSString *)text
+{
+    return [self textMarkersForRange:[self rangeFromMarkers:markers withText:text]];
+}
+
+- (NSArray *)textRectsFromMarkers:(NSArray *)markers withText:(NSString *)text
+{
+    if (![self _prepareAccessibilityCall])
+        return nil;
+    
+    RefPtr<Range> range = [self rangeFromMarkers:markers withText:text];
+    if (!range || range->collapsed())
+        return nil;
+    
+    Vector<WebCore::SelectionRect> selectionRects;
+    range->collectSelectionRectsWithoutUnionInteriorLines(selectionRects);
+    return [self rectsForSelectionRects:selectionRects];
+}
+
+- (NSArray *)rectsForSelectionRects:(const Vector<WebCore::SelectionRect>&)selectionRects
+{
+    unsigned size = selectionRects.size();
+    if (!size)
+        return nil;
+    
+    NSMutableArray *rects = [NSMutableArray arrayWithCapacity:size];
+    for (unsigned i = 0; i < size; i++) {
+        const WebCore::SelectionRect& coreRect = selectionRects[i];
+        IntRect selectionRect = coreRect.rect();
+        CGRect rect = [self convertRectToScreenSpace:selectionRect];
+        [rects addObject:[NSValue valueWithRect:rect]];
+    }
+    
+    return rects;
+}
+
 - (WebAccessibilityTextMarker *)textMarkerForPoint:(CGPoint)point
 {
     if (![self _prepareAccessibilityCall])
index 8a39fcc..5774fde 100644 (file)
@@ -1,3 +1,25 @@
+2017-07-12  Nan Wang  <n_wang@apple.com>
+
+        AX: [iOS] Implement a way to retrieve a text marker range with desired text that is closest to a position
+        https://bugs.webkit.org/show_bug.cgi?id=174393
+        <rdar://problem/33248006>
+
+        Reviewed by Chris Fleizach.
+
+        * DumpRenderTree/AccessibilityUIElement.cpp:
+        (textMarkerRangeMatchesTextNearMarkersCallback):
+        (AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers):
+        (AccessibilityUIElement::getJSClass):
+        * DumpRenderTree/AccessibilityUIElement.h:
+        * DumpRenderTree/ios/AccessibilityUIElementIOS.mm:
+        (AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers):
+        * WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp:
+        (WTR::AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers):
+        * WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
+        * WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl:
+        * WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:
+        (WTR::AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers):
+
 2017-07-12  Emilio Cobos Ãlvarez  <ecobos@igalia.com>
 
         Make prepare-ChangeLog -g <commit> generate a more standard ChangeLog entry.
index a8d874d..e0aa546 100644 (file)
@@ -927,8 +927,8 @@ static JSValueRef textMarkerForPointCallback(JSContextRef context, JSObjectRef f
 
 static JSValueRef textMarkerRangeForMarkersCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
-    AccessibilityTextMarker* startMarker = 0;
-    AccessibilityTextMarker* endMarker = 0;
+    AccessibilityTextMarker* startMarker = nullptr;
+    AccessibilityTextMarker* endMarker = nullptr;
     if (argumentCount == 2) {
         startMarker = toTextMarker(JSValueToObject(context, arguments[0], exception));
         endMarker = toTextMarker(JSValueToObject(context, arguments[1], exception));
@@ -1483,6 +1483,23 @@ static JSValueRef hasContainedByFieldsetTraitCallback(JSContextRef context, JSOb
     return JSValueMakeBoolean(context, toAXElement(thisObject)->hasContainedByFieldsetTrait());
 }
 
+static JSValueRef textMarkerRangeMatchesTextNearMarkersCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    JSStringRef searchText = nullptr;
+    AccessibilityTextMarker* startMarker = nullptr;
+    AccessibilityTextMarker* endMarker = nullptr;
+    if (argumentCount == 3) {
+        searchText = JSValueToStringCopy(context, arguments[0], exception);
+        startMarker = toTextMarker(JSValueToObject(context, arguments[1], exception));
+        endMarker = toTextMarker(JSValueToObject(context, arguments[2], exception));
+    }
+    
+    JSValueRef result = AccessibilityTextMarkerRange::makeJSAccessibilityTextMarkerRange(context, toAXElement(thisObject)->textMarkerRangeMatchesTextNearMarkers(searchText, startMarker, endMarker));
+    if (searchText)
+        JSStringRelease(searchText);
+    return result;
+}
+
 #endif // PLATFORM(IOS)
 
 #if PLATFORM(MAC) && !PLATFORM(IOS)
@@ -1704,6 +1721,11 @@ AccessibilityTextMarker AccessibilityUIElement::nextSentenceEndTextMarkerForText
     return nullptr;
 }
 
+AccessibilityTextMarkerRange AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers(JSStringRef, AccessibilityTextMarker*, AccessibilityTextMarker*)
+{
+    return nullptr;
+}
+
 #endif
 
 // Destruction
@@ -1920,6 +1942,7 @@ JSClassRef AccessibilityUIElement::getJSClass()
         { "scrollPageRight", scrollPageRightCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "assistiveTechnologySimulatedFocus", assistiveTechnologySimulatedFocusCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "fieldsetAncestorElement", fieldsetAncestorElementCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "textMarkerRangeMatchesTextNearMarkers", textMarkerRangeMatchesTextNearMarkersCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
 #endif
         { 0, 0, 0 }
     };
index 79f0d8a..879c0a6 100644 (file)
@@ -306,6 +306,8 @@ public:
     bool isTextArea() const;
     bool isSearchField() const;
     
+    AccessibilityTextMarkerRange textMarkerRangeMatchesTextNearMarkers(JSStringRef, AccessibilityTextMarker*, AccessibilityTextMarker*);
+    
 #endif // PLATFORM(IOS)
 
 #if PLATFORM(MAC) && !PLATFORM(IOS)
index fb92ff3..dfaa60c 100644 (file)
@@ -113,6 +113,7 @@ AccessibilityUIElement::~AccessibilityUIElement()
 - (id)accessibilityObjectForTextMarker:(id)marker;
 - (id)lineStartMarkerForMarker:(id)marker;
 - (id)lineEndMarkerForMarker:(id)marker;
+- (NSArray *)textMarkerRangeFromMarkers:(NSArray *)markers withText:(NSString *)text;
 @end
 
 @interface NSObject (WebAccessibilityObjectWrapperPrivate)
@@ -620,6 +621,16 @@ AccessibilityTextMarker AccessibilityUIElement::nextSentenceEndTextMarkerForText
     return nullptr;
 }
 
+AccessibilityTextMarkerRange AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers(JSStringRef text, AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
+{
+    NSArray *textMarkers = nil;
+    if (startMarker->platformTextMarker() && endMarker->platformTextMarker())
+        textMarkers = [NSArray arrayWithObjects:(id)startMarker->platformTextMarker(), (id)endMarker->platformTextMarker(), nil];
+    id textMarkerRange = [m_element textMarkerRangeFromMarkers:textMarkers withText:[NSString stringWithJSStringRef:text]];
+    return AccessibilityTextMarkerRange(textMarkerRange);
+}
+
+
 #endif // SUPPORTS_AX_TEXTMARKERS && PLATFORM(IOS)
 
 #pragma mark Unused
index 95cd26a..a80e053 100644 (file)
@@ -74,7 +74,7 @@ bool AccessibilityUIElement::hasContainedByFieldsetTrait() { return false; }
 RefPtr<AccessibilityUIElement> AccessibilityUIElement::fieldsetAncestorElement() { return nullptr; }
 bool AccessibilityUIElement::isSearchField() const { return false; }
 bool AccessibilityUIElement::isTextArea() const { return false; }
-
+RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers(JSStringRef, AccessibilityTextMarker*, AccessibilityTextMarker*) { return nullptr; }
 #endif
     
 // Unsupported methods on various platforms. As they're implemented on other platforms this list should be modified.
index 4a27253..d2114a6 100644 (file)
@@ -289,6 +289,7 @@ public:
     RefPtr<AccessibilityTextMarkerRange> sentenceTextMarkerRangeForTextMarker(AccessibilityTextMarker*);
     RefPtr<AccessibilityTextMarker> nextSentenceEndTextMarkerForTextMarker(AccessibilityTextMarker*);
     RefPtr<AccessibilityTextMarker> previousSentenceStartTextMarkerForTextMarker(AccessibilityTextMarker*);
+    RefPtr<AccessibilityTextMarkerRange> textMarkerRangeMatchesTextNearMarkers(JSStringRef, AccessibilityTextMarker*, AccessibilityTextMarker*);
 
     // Returns an ordered list of supported actions for an element.
     JSRetainPtr<JSStringRef> supportedActions() const;
index be0d646..8fa1bb9 100644 (file)
@@ -226,6 +226,7 @@ interface AccessibilityUIElement {
     AccessibilityTextMarkerRange sentenceTextMarkerRangeForTextMarker(AccessibilityTextMarker textMarker);
     AccessibilityTextMarker previousSentenceStartTextMarkerForTextMarker(AccessibilityTextMarker textMarker);
     AccessibilityTextMarker nextSentenceEndTextMarkerForTextMarker(AccessibilityTextMarker textMarker);
+    AccessibilityTextMarkerRange textMarkerRangeMatchesTextNearMarkers(DOMString text, AccessibilityTextMarker startMarker, AccessibilityTextMarker endMarker);
 
     // Returns an ordered list of supported actions for an element.
     readonly attribute DOMString supportedActions;
index a2a5a5e..06aea12 100644 (file)
@@ -91,6 +91,7 @@ typedef void (*AXPostedNotificationCallback)(id element, NSString* notification,
 - (id)accessibilityObjectForTextMarker:(id)marker;
 - (id)lineStartMarkerForMarker:(id)marker;
 - (id)lineEndMarkerForMarker:(id)marker;
+- (NSArray *)textMarkerRangeFromMarkers:(NSArray *)markers withText:(NSString *)text;
 @end
 
 @interface NSObject (WebAccessibilityObjectWrapperPrivate)
@@ -1200,6 +1201,15 @@ RefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousSentenceStartTex
 {
     return nullptr;
 }
+    
+RefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::textMarkerRangeMatchesTextNearMarkers(JSStringRef text, AccessibilityTextMarker* startMarker, AccessibilityTextMarker* endMarker)
+{
+    NSArray *textMarkers = nil;
+    if (startMarker->platformTextMarker() && endMarker->platformTextMarker())
+        textMarkers = [NSArray arrayWithObjects:(id)startMarker->platformTextMarker(), (id)endMarker->platformTextMarker(), nil];
+    id textMarkerRange = [m_element textMarkerRangeFromMarkers:textMarkers withText:[NSString stringWithJSStringRef:text]];
+    return AccessibilityTextMarkerRange::create(textMarkerRange);
+}
 
 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
 {