AX: Implement paragraph related text marker functions using TextIterator
authorn_wang@apple.com <n_wang@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 13 Feb 2016 05:24:24 +0000 (05:24 +0000)
committern_wang@apple.com <n_wang@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Sat, 13 Feb 2016 05:24:24 +0000 (05:24 +0000)
https://bugs.webkit.org/show_bug.cgi?id=154098
<rdar://problem/24269675>

Reviewed by Chris Fleizach.

Source/WebCore:

Using CharacterOffset to implement paragraph related text marker calls. Reused
logic from VisibleUnits class. And refactored textMarkerForCharacterOffset method
to get better performance. Also fixed an issue where we can't navigate through a text
node with line breaks in it using next/previousCharacterOffset call.

Test: accessibility/mac/text-marker-paragraph-nav.html

* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::traverseToOffsetInRange):
(WebCore::AXObjectCache::startOrEndTextMarkerDataForRange):
(WebCore::AXObjectCache::characterOffsetForNodeAndOffset):
(WebCore::AXObjectCache::textMarkerDataForCharacterOffset):
(WebCore::AXObjectCache::textMarkerDataForNextCharacterOffset):
(WebCore::AXObjectCache::textMarkerDataForPreviousCharacterOffset):
(WebCore::AXObjectCache::nextNode):
(WebCore::AXObjectCache::textMarkerDataForVisiblePosition):
(WebCore::AXObjectCache::nextCharacterOffset):
(WebCore::AXObjectCache::previousCharacterOffset):
(WebCore::startWordBoundary):
(WebCore::AXObjectCache::startCharacterOffsetOfWord):
(WebCore::AXObjectCache::endCharacterOffsetOfWord):
(WebCore::AXObjectCache::previousWordStartCharacterOffset):
(WebCore::AXObjectCache::previousWordBoundary):
(WebCore::AXObjectCache::startCharacterOffsetOfParagraph):
(WebCore::AXObjectCache::endCharacterOffsetOfParagraph):
(WebCore::AXObjectCache::paragraphForCharacterOffset):
(WebCore::AXObjectCache::nextParagraphEndCharacterOffset):
(WebCore::AXObjectCache::previousParagraphStartCharacterOffset):
(WebCore::AXObjectCache::rootAXEditableElement):
* accessibility/AXObjectCache.h:
(WebCore::CharacterOffset::remaining):
(WebCore::CharacterOffset::isNull):
(WebCore::CharacterOffset::isEqual):
(WebCore::AXObjectCache::isNodeInUse):
* accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
(+[WebAccessibilityTextMarker textMarkerWithCharacterOffset:cache:]):
(-[WebAccessibilityObjectWrapper nextMarkerForCharacterOffset:]):
(-[WebAccessibilityObjectWrapper previousMarkerForCharacterOffset:]):
(-[WebAccessibilityObjectWrapper rangeForTextMarkers:]):
* accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(startOrEndTextmarkerForRange):
(nextTextMarkerForCharacterOffset):
(previousTextMarkerForCharacterOffset):
(-[WebAccessibilityObjectWrapper nextTextMarkerForCharacterOffset:]):
(-[WebAccessibilityObjectWrapper previousTextMarkerForCharacterOffset:]):
(-[WebAccessibilityObjectWrapper textMarkerForCharacterOffset:]):
(textMarkerForCharacterOffset):
(-[WebAccessibilityObjectWrapper accessibilityAttributeValue:forParameter:]):
(-[WebAccessibilityObjectWrapper nextTextMarkerForNode:offset:]): Deleted.
(-[WebAccessibilityObjectWrapper previousTextMarkerForNode:offset:]): Deleted.
(-[WebAccessibilityObjectWrapper textMarkerForNode:offset:ignoreStart:]): Deleted.
(-[WebAccessibilityObjectWrapper textMarkerForNode:offset:]): Deleted.
* editing/VisibleUnits.cpp:
(WebCore::nextSentencePosition):
(WebCore::findStartOfParagraph):
(WebCore::findEndOfParagraph):
(WebCore::startOfParagraph):
(WebCore::endOfParagraph):
* editing/VisibleUnits.h:

Tools:

* DumpRenderTree/AccessibilityUIElement.cpp:
(nextWordEndTextMarkerForTextMarkerCallback):
(paragraphTextMarkerRangeForTextMarkerCallback):
(previousParagraphStartTextMarkerForTextMarkerCallback):
(nextParagraphEndTextMarkerForTextMarkerCallback):
(setSelectedVisibleTextRangeCallback):
(AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
(AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
(AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
(AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
(AccessibilityUIElement::getJSClass):
* DumpRenderTree/AccessibilityUIElement.h:
* DumpRenderTree/ios/AccessibilityUIElementIOS.mm:
(AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
(AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
(AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
(AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
* DumpRenderTree/mac/AccessibilityUIElementMac.mm:
(AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
(AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
(AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
(AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
(AccessibilityUIElement::supportedActions):
* WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp:
(WTR::AccessibilityUIElement::rightWordTextMarkerRangeForTextMarker):
(WTR::AccessibilityUIElement::previousWordStartTextMarkerForTextMarker):
(WTR::AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
(WTR::AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
(WTR::AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
(WTR::AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
* WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
* WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl:
* WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:
(WTR::AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
(WTR::AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
(WTR::AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
(WTR::AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
(WTR::AccessibilityUIElement::mathPostscriptsDescription):
* WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
(WTR::AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
(WTR::AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
(WTR::AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
(WTR::AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
(WTR::_convertMathMultiscriptPairsToString):

LayoutTests:

* accessibility/mac/text-marker-paragraph-nav-expected.txt: Added.
* accessibility/mac/text-marker-paragraph-nav.html: Added.
* accessibility/text-marker/text-marker-previous-next-expected.txt:
* accessibility/text-marker/text-marker-previous-next.html:

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

22 files changed:
LayoutTests/ChangeLog
LayoutTests/accessibility/mac/text-marker-paragraph-nav-expected.txt [new file with mode: 0644]
LayoutTests/accessibility/mac/text-marker-paragraph-nav.html [new file with mode: 0644]
LayoutTests/accessibility/text-marker/text-marker-previous-next-expected.txt
LayoutTests/accessibility/text-marker/text-marker-previous-next.html
Source/WebCore/ChangeLog
Source/WebCore/accessibility/AXObjectCache.cpp
Source/WebCore/accessibility/AXObjectCache.h
Source/WebCore/accessibility/ios/WebAccessibilityObjectWrapperIOS.mm
Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm
Source/WebCore/editing/VisibleUnits.cpp
Source/WebCore/editing/VisibleUnits.h
Tools/ChangeLog
Tools/DumpRenderTree/AccessibilityUIElement.cpp
Tools/DumpRenderTree/AccessibilityUIElement.h
Tools/DumpRenderTree/ios/AccessibilityUIElementIOS.mm
Tools/DumpRenderTree/mac/AccessibilityUIElementMac.mm
Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp
Tools/WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h
Tools/WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl
Tools/WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm
Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm

index dc443d4..0368d9d 100644 (file)
@@ -1,3 +1,16 @@
+2016-02-12  Nan Wang  <n_wang@apple.com>
+
+        AX: Implement paragraph related text marker functions using TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=154098
+        <rdar://problem/24269675>
+
+        Reviewed by Chris Fleizach.
+
+        * accessibility/mac/text-marker-paragraph-nav-expected.txt: Added.
+        * accessibility/mac/text-marker-paragraph-nav.html: Added.
+        * accessibility/text-marker/text-marker-previous-next-expected.txt:
+        * accessibility/text-marker/text-marker-previous-next.html:
+
 2016-02-12  Saam barati  <sbarati@apple.com>
 
         [ES6] we have an incorrect syntax error when a callee of a function expression has the same name as a top-level lexical declaration
diff --git a/LayoutTests/accessibility/mac/text-marker-paragraph-nav-expected.txt b/LayoutTests/accessibility/mac/text-marker-paragraph-nav-expected.txt
new file mode 100644 (file)
index 0000000..5ef0e6d
--- /dev/null
@@ -0,0 +1,84 @@
+paragraph test
+Test Contenteditable is working.
+c d
+test audio file
+can't select
+巧克力 是食物吗?
+كيف حالك؟
+both   spaces
+line breaks
+some
+text
+this is my first paragraph. Of text. it has some text.
+this is my second paragraph. Of text. it has some text.
+this is my third paragraph. Of text. it has some text.
+
+This tests that paragraph navigation is working correctly.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+Current character is: p
+Current paragraph is: paragraph test
+Pre paragraph start to next paragraph end: paragraph test
+
+Current character is: T
+Current paragraph is: Test Content
+Pre paragraph start to next paragraph end: Test Content
+
+Current character is: c
+Current paragraph is: c [ATTACHMENT]d
+Pre paragraph start to next paragraph end: c [ATTACHMENT]d
+
+Current character is: d
+Current paragraph is: c [ATTACHMENT]d
+Pre paragraph start to next paragraph end: c [ATTACHMENT]d
+test audio file
+
+Current character is: t
+Current paragraph is: test audio [ATTACHMENT]file
+Pre paragraph start to next paragraph end: test audio [ATTACHMENT]file
+
+Current character is: c
+Current paragraph is: can't select
+Pre paragraph start to next paragraph end: can't select
+
+Current character is: 巧
+Current paragraph is: 巧克力 是食物吗?
+Pre paragraph start to next paragraph end: 巧克力 是食物吗?
+
+Current character is: ك
+Current paragraph is: كيف حالك؟
+Pre paragraph start to next paragraph end: كيف حالك؟
+
+Current character is: b
+Current paragraph is: both   spaces
+Pre paragraph start to next paragraph end: both   spaces
+
+Current character is: i
+Current paragraph is: line breaks
+Pre paragraph start to next paragraph end: line breaks
+
+Current character is: s
+Current paragraph is: some
+Pre paragraph start to next paragraph end: some
+
+Current character is: t
+Current paragraph is: text
+Pre paragraph start to next paragraph end: text
+
+Paragraph: this is my first paragraph. Of text. it has some text.
+Paragraph: this is my second paragraph. Of text. it has some text.
+Paragraph: this is my third paragraph. Of text. it has some text.
+Paragraph: this is my third paragraph. Of text. it has some text.
+Paragraph: this is my second paragraph. Of text. it has some text.
+Paragraph: this is my first paragraph. Of text. it has some text.
+Test going forward.
+End paragraph: text
+
+Test going backwards.
+Start paragraph: paragraph test
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/accessibility/mac/text-marker-paragraph-nav.html b/LayoutTests/accessibility/mac/text-marker-paragraph-nav.html
new file mode 100644 (file)
index 0000000..d7374c7
--- /dev/null
@@ -0,0 +1,219 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<meta charset="utf-8"> 
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<style>
+.userselect { user-select: none; -webkit-user-select: none; }
+</style>
+<body id="body">
+
+<div id="text1" tabindex="0">
+para<span>graph</span>
+test
+</div>
+
+<div id="text2">
+Test Content<span contenteditable="true">editable is working.</span>
+</div>
+
+<div id="text3">
+c <img src="#" aria-label="blah" style="background-color: #aaaaaa; width: 100px; height: 100px;">d
+</div>
+
+<div id="text4">
+test audio <audio controls><source src="test.mp3" type="audio/mpeg"></audio>file
+</div>
+
+<div class="" id="text5">can't select</div>
+
+<div id="text6">
+巧克力
+是食物吗?
+</div>
+
+<div id="text6a">
+كيف حالك؟
+</div>
+
+<pre id="text7">
+both   spaces
+line breaks
+</pre>
+
+<div id="text8">
+some<br>text
+</div>
+
+<div id="text9">this is my first paragraph. Of text. it has some text.<br>
+this is my second paragraph. Of text. it has some text.<br>
+this is my third paragraph. Of text. it has some text.<br><br>
+</div>
+
+<p id="description"></p>
+<div id="console"></div>
+
+<script>
+
+    description("This tests that paragraph navigation is working correctly.");
+    
+    if (window.accessibilityController) {
+        
+        // Check that we can get the paragraph range with span tag.
+        var text = accessibilityController.accessibleElementById("text1");
+        var textMarkerRange = text.textMarkerRangeForElement(text);
+        var startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        var currentMarker = advanceAndVerify(startMarker, 1, text);
+        
+        // Check with contenteditable.
+        text = accessibilityController.accessibleElementById("text2");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = advanceAndVerify(startMarker, 1, text);
+        
+        // Check with replaced elements.
+        text = accessibilityController.accessibleElementById("text3");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = advanceAndVerify(startMarker, 1, text);
+        currentMarker = advanceAndVerify(currentMarker, 3, text);
+        // Audio tag.
+        text = accessibilityController.accessibleElementById("text4");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = advanceAndVerify(startMarker, 1, text);
+        
+        // Check with user-select:none.
+        text = accessibilityController.accessibleElementById("text5");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = advanceAndVerify(startMarker, 1, text);
+        
+        // Multi-languages.
+        text = accessibilityController.accessibleElementById("text6");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = advanceAndVerify(startMarker, 1, text);
+        
+        text = accessibilityController.accessibleElementById("text6a");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = advanceAndVerify(startMarker, 1, text);
+        
+        // Check the case with pre tag.
+        text = accessibilityController.accessibleElementById("text7");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = advanceAndVerify(startMarker, 1, text);
+        currentMarker = advanceAndVerify(currentMarker, 15, text);
+        
+        // Check the case with br tag.
+        text = accessibilityController.accessibleElementById("text8");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = advanceAndVerify(startMarker, 1, text);
+        currentMarker = advanceAndVerify(currentMarker, 5, text);
+        
+        // Check getting the correct paragraphs
+        text = accessibilityController.accessibleElementById("text9");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        startMarker = text.nextTextMarker(startMarker);
+        var endMarker = text.endTextMarkerForTextMarkerRange(textMarkerRange);
+        verifyParagraphs(text, startMarker, 3);
+        
+        // Check the paragraph marker runs from start to end, and backwards.
+        // Make sure it won't hang.
+        verifyDocument(text);
+        
+        
+        function advanceAndVerify(currentMarker, offset, obj) {
+            var previousMarker = currentMarker;
+            for (var i = 0; i < offset; i++) {
+                previousMarker = currentMarker;
+                currentMarker = obj.nextTextMarker(previousMarker);
+            }
+            verifyParagraphRangeForTextMarker(previousMarker, currentMarker, obj);
+            return currentMarker;
+        }
+        
+        function replaceAttachmentInString(str) {
+            str =  str.replace(String.fromCharCode(65532), "[ATTACHMENT]");
+            return str;
+        }
+        
+        function verifyParagraphRangeForTextMarker(preMarker, textMarker, obj) {
+            var markerRange = obj.textMarkerRangeForMarkers(preMarker, textMarker);
+            var currentCharacter = replaceAttachmentInString(obj.stringForTextMarkerRange(markerRange));
+            debug("Current character is: " + currentCharacter);
+            
+            var paragraphRange = obj.paragraphTextMarkerRangeForTextMarker(textMarker);
+            var paragraph = replaceAttachmentInString(obj.stringForTextMarkerRange(paragraphRange));
+            debug("Current paragraph is: " + paragraph);
+            
+            var preStart = obj.previousParagraphStartTextMarkerForTextMarker(textMarker);
+            var nextEnd = obj.nextParagraphEndTextMarkerForTextMarker(textMarker);
+            var preAndNextParagraphRange = obj.textMarkerRangeForMarkers(preStart, nextEnd);
+            var preAndNextParagraph = replaceAttachmentInString(obj.stringForTextMarkerRange(preAndNextParagraphRange));
+            debug("Pre paragraph start to next paragraph end: " + preAndNextParagraph + "\n");
+        }
+        
+        function verifyParagraphs(obj, startMarker, paragraphCount) {
+            // Move to the end of first paragraph, so the first paragraph
+            // won't print twice.
+            var current = obj.nextParagraphEndTextMarkerForTextMarker(startMarker);
+            var i = 0;
+            while(i < paragraphCount) {
+                var currRange = obj.paragraphTextMarkerRangeForTextMarker(current);
+                var currParagraph = obj.stringForTextMarkerRange(currRange);
+                debug("Paragraph: " + currParagraph);
+                current = obj.nextParagraphEndTextMarkerForTextMarker(current);
+                i++;
+            }
+            
+            // Backwards.
+            current = obj.previousParagraphStartTextMarkerForTextMarker(current);
+            i = 0;
+            while(i < paragraphCount) {
+                var currRange = obj.paragraphTextMarkerRangeForTextMarker(current);
+                var currParagraph = obj.stringForTextMarkerRange(currRange);
+                debug("Paragraph: " + currParagraph);
+                current = obj.previousParagraphStartTextMarkerForTextMarker(current);
+                i++;
+            }
+        }
+        
+        function verifyDocument(obj) {
+            var start = obj.startTextMarker;
+            
+            // Going forward.
+            debug("Test going forward.");
+            var current = start;
+            var end = "text";
+            var currParagraph = "";
+            while(currParagraph != end) {
+                var currRange = obj.paragraphTextMarkerRangeForTextMarker(current);
+                currParagraph = obj.stringForTextMarkerRange(currRange);
+                current = obj.nextParagraphEndTextMarkerForTextMarker(current);
+            }
+            debug("End paragraph: " + replaceAttachmentInString(currParagraph));
+            
+            // Going backwards.
+            debug("\nTest going backwards.");
+            var start = "paragraph test";
+            currParagraph = ""; 
+            while(currParagraph != start) {
+                var currentRange = obj.paragraphTextMarkerRangeForTextMarker(current);
+                currParagraph = obj.stringForTextMarkerRange(currentRange);
+                current = obj.previousParagraphStartTextMarkerForTextMarker(current);
+            }
+            debug("Start paragraph: " + replaceAttachmentInString(currParagraph));
+        }
+    }
+
+</script>
+
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
\ No newline at end of file
index 0ca3941..38151cf 100644 (file)
@@ -4,6 +4,7 @@ text1
 c  d
 
 can't select
+abc de f
 This tests the next/previous text marker functions are implemented correctly.
 
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
@@ -25,6 +26,8 @@ PASS text3.stringForTextMarkerRange(markerRange) is 'sel'
 PASS !psw.accessibilityElementForTextMarker(start) is true
 PASS text2.accessibilityElementForTextMarker(currentMarker).isEqual(text3) is true
 PASS text2.accessibilityElementForTextMarker(currentMarker).isEqual(text2.childAtIndex(2)) is true
+PASS text.stringForTextMarkerRange(markerRange) is 'f'
+PASS text.stringForTextMarkerRange(markerRange) is 'a'
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 927f05b..9ac6b2e 100644 (file)
@@ -20,6 +20,12 @@ c <img src="#" aria-label="blah" style="background-color: #aaaaaa; width: 100px;
 
 <div class="userselect" id="text3">can't select</div>
 
+<div id="text4">
+abc
+de
+f
+</div>
+
 <p id="description"></p>
 <div id="console"></div>
 
@@ -135,6 +141,27 @@ c <img src="#" aria-label="blah" style="background-color: #aaaaaa; width: 100px;
         currentMarker = text2.previousTextMarker(currentMarker);
         shouldBeTrue("text2.accessibilityElementForTextMarker(currentMarker).isEqual(text2.childAtIndex(2))");
         
+        
+        // Make sure that text node with line breaks, we can go through it with next/previous call.
+        text = accessibilityController.accessibleElementById("text4");
+        textMarkerRange = text.textMarkerRangeForElement(text);
+        startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = startMarker;
+        for (var i = 0; i < 8; i++) {
+            previousMarker = currentMarker;
+            currentMarker = text.nextTextMarker(currentMarker);
+        }
+        markerRange = text.textMarkerRangeForMarkers(previousMarker, currentMarker)
+        shouldBe("text.stringForTextMarkerRange(markerRange)", "'f'");
+        
+        endMarker = text.endTextMarkerForTextMarkerRange(textMarkerRange);
+        currentMarker = endMarker;
+        for (var i = 0; i < 7; i++) {
+            currentMarker = text.previousTextMarker(currentMarker);
+        }
+        markerRange = text.textMarkerRangeForMarkers(startMarker, currentMarker)
+        shouldBe("text.stringForTextMarkerRange(markerRange)", "'a'");
+        
     }
 
 </script>
index 3d3e9ae..759993a 100644 (file)
@@ -1,3 +1,71 @@
+2016-02-12  Nan Wang  <n_wang@apple.com>
+
+        AX: Implement paragraph related text marker functions using TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=154098
+        <rdar://problem/24269675>
+
+        Reviewed by Chris Fleizach.
+
+        Using CharacterOffset to implement paragraph related text marker calls. Reused
+        logic from VisibleUnits class. And refactored textMarkerForCharacterOffset method
+        to get better performance. Also fixed an issue where we can't navigate through a text
+        node with line breaks in it using next/previousCharacterOffset call.
+
+        Test: accessibility/mac/text-marker-paragraph-nav.html
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::traverseToOffsetInRange):
+        (WebCore::AXObjectCache::startOrEndTextMarkerDataForRange):
+        (WebCore::AXObjectCache::characterOffsetForNodeAndOffset):
+        (WebCore::AXObjectCache::textMarkerDataForCharacterOffset):
+        (WebCore::AXObjectCache::textMarkerDataForNextCharacterOffset):
+        (WebCore::AXObjectCache::textMarkerDataForPreviousCharacterOffset):
+        (WebCore::AXObjectCache::nextNode):
+        (WebCore::AXObjectCache::textMarkerDataForVisiblePosition):
+        (WebCore::AXObjectCache::nextCharacterOffset):
+        (WebCore::AXObjectCache::previousCharacterOffset):
+        (WebCore::startWordBoundary):
+        (WebCore::AXObjectCache::startCharacterOffsetOfWord):
+        (WebCore::AXObjectCache::endCharacterOffsetOfWord):
+        (WebCore::AXObjectCache::previousWordStartCharacterOffset):
+        (WebCore::AXObjectCache::previousWordBoundary):
+        (WebCore::AXObjectCache::startCharacterOffsetOfParagraph):
+        (WebCore::AXObjectCache::endCharacterOffsetOfParagraph):
+        (WebCore::AXObjectCache::paragraphForCharacterOffset):
+        (WebCore::AXObjectCache::nextParagraphEndCharacterOffset):
+        (WebCore::AXObjectCache::previousParagraphStartCharacterOffset):
+        (WebCore::AXObjectCache::rootAXEditableElement):
+        * accessibility/AXObjectCache.h:
+        (WebCore::CharacterOffset::remaining):
+        (WebCore::CharacterOffset::isNull):
+        (WebCore::CharacterOffset::isEqual):
+        (WebCore::AXObjectCache::isNodeInUse):
+        * accessibility/ios/WebAccessibilityObjectWrapperIOS.mm:
+        (+[WebAccessibilityTextMarker textMarkerWithCharacterOffset:cache:]):
+        (-[WebAccessibilityObjectWrapper nextMarkerForCharacterOffset:]):
+        (-[WebAccessibilityObjectWrapper previousMarkerForCharacterOffset:]):
+        (-[WebAccessibilityObjectWrapper rangeForTextMarkers:]):
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
+        (startOrEndTextmarkerForRange):
+        (nextTextMarkerForCharacterOffset):
+        (previousTextMarkerForCharacterOffset):
+        (-[WebAccessibilityObjectWrapper nextTextMarkerForCharacterOffset:]):
+        (-[WebAccessibilityObjectWrapper previousTextMarkerForCharacterOffset:]):
+        (-[WebAccessibilityObjectWrapper textMarkerForCharacterOffset:]):
+        (textMarkerForCharacterOffset):
+        (-[WebAccessibilityObjectWrapper accessibilityAttributeValue:forParameter:]):
+        (-[WebAccessibilityObjectWrapper nextTextMarkerForNode:offset:]): Deleted.
+        (-[WebAccessibilityObjectWrapper previousTextMarkerForNode:offset:]): Deleted.
+        (-[WebAccessibilityObjectWrapper textMarkerForNode:offset:ignoreStart:]): Deleted.
+        (-[WebAccessibilityObjectWrapper textMarkerForNode:offset:]): Deleted.
+        * editing/VisibleUnits.cpp:
+        (WebCore::nextSentencePosition):
+        (WebCore::findStartOfParagraph):
+        (WebCore::findEndOfParagraph):
+        (WebCore::startOfParagraph):
+        (WebCore::endOfParagraph):
+        * editing/VisibleUnits.h:
+
 2016-02-12  Ryan Haddad  <ryanhaddad@apple.com>
 
         Reset results for bindings tests after r196520
index af95d8b..9bd73fc 100644 (file)
@@ -83,6 +83,7 @@
 #include "ScrollView.h"
 #include "TextBoundaries.h"
 #include "TextIterator.h"
+#include "htmlediting.h"
 #include <wtf/DataLog.h>
 
 #if ENABLE(VIDEO)
@@ -1469,6 +1470,9 @@ CharacterOffset AXObjectCache::traverseToOffsetInRange(RefPtr<Range>range, int o
         }
     }
     
+    // Sometimes text contents in a node are splitted into several iterations, so that iterator.range()->startOffset()
+    // might not be the correct character count. Here we use a previousNode object to keep track of that.
+    Node* previousNode = nullptr;
     for (; !iterator.atEnd(); iterator.advance()) {
         int currentLength = iterator.text().length();
         bool hasReplacedNodeOrBR = false;
@@ -1497,22 +1501,27 @@ CharacterOffset AXObjectCache::traverseToOffsetInRange(RefPtr<Range>range, int o
                     if (childNode && childNode->renderer() && childNode->renderer()->isBR()) {
                         currentNode = childNode;
                         hasReplacedNodeOrBR = true;
-                    } else
+                    } else if (currentNode != previousNode)
                         continue;
                 }
             }
             offsetSoFar += currentLength;
         }
 
-        lastLength = currentLength;
-        lastStartOffset = hasReplacedNodeOrBR ? 0 : iterator.range()->startOffset();
+        if (currentNode == previousNode)
+            lastLength += currentLength;
+        else {
+            lastLength = currentLength;
+            lastStartOffset = hasReplacedNodeOrBR ? 0 : iterator.range()->startOffset();
+        }
         
         // Break early if we have advanced enough characters.
         if (!toNodeEnd && offsetSoFar >= offset) {
-            offsetInCharacter = offset - (offsetSoFar - currentLength);
+            offsetInCharacter = offset - (offsetSoFar - lastLength);
             finished = true;
             break;
         }
+        previousNode = currentNode;
     }
     
     if (!finished) {
@@ -1707,15 +1716,18 @@ void AXObjectCache::startOrEndTextMarkerDataForRange(TextMarkerData& textMarkerD
     setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset);
 }
 
-CharacterOffset AXObjectCache::characterOffsetForNodeAndOffset(Node& node, int offset, bool toNodeEnd, bool ignoreStart)
+CharacterOffset AXObjectCache::characterOffsetForNodeAndOffset(Node& node, int offset, TraverseOption option)
 {
     Node* domNode = &node;
     if (!domNode)
         return CharacterOffset();
     
+    bool toNodeEnd = option & TraverseOptionToNodeEnd;
+    bool includeStart = option & TraverseOptionIncludeStart;
+    
     // ignoreStart is used to determine if we should go to previous node or
     // stay in current node when offset is 0.
-    if (!toNodeEnd && (offset < 0 || (!offset && ignoreStart))) {
+    if (!toNodeEnd && (offset < 0 || (!offset && !includeStart))) {
         // Set the offset to the amount of characters we need to go backwards.
         offset = - offset;
         CharacterOffset charOffset = CharacterOffset();
@@ -1723,14 +1735,14 @@ CharacterOffset AXObjectCache::characterOffsetForNodeAndOffset(Node& node, int o
             offset -= charOffset.offset;
             domNode = previousNode(domNode);
             if (domNode) {
-                charOffset = characterOffsetForNodeAndOffset(*domNode, 0, true);
+                charOffset = characterOffsetForNodeAndOffset(*domNode, 0, TraverseOptionToNodeEnd);
             } else
                 return CharacterOffset();
             if (!offset)
                 break;
         }
         if (offset > 0)
-            charOffset = characterOffsetForNodeAndOffset(*charOffset.node, charOffset.offset - offset, false);
+            charOffset = characterOffsetForNodeAndOffset(*charOffset.node, charOffset.offset - offset);
         return charOffset;
     }
     
@@ -1750,14 +1762,30 @@ CharacterOffset AXObjectCache::characterOffsetForNodeAndOffset(Node& node, int o
     return characterOffset;
 }
 
-void AXObjectCache::textMarkerDataForCharacterOffset(TextMarkerData& textMarkerData, Node& node, int offset, bool toNodeEnd, bool ignoreStart)
+void AXObjectCache::textMarkerDataForCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
 {
     memset(&textMarkerData, 0, sizeof(TextMarkerData));
-    
-    CharacterOffset characterOffset = characterOffsetForNodeAndOffset(node, offset, toNodeEnd, ignoreStart);
     setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset);
 }
 
+void AXObjectCache::textMarkerDataForNextCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+    CharacterOffset next = characterOffset;
+    do {
+        next = nextCharacterOffset(next);
+        textMarkerDataForCharacterOffset(textMarkerData, next);
+    } while (textMarkerData.ignored);
+}
+
+void AXObjectCache::textMarkerDataForPreviousCharacterOffset(TextMarkerData& textMarkerData, const CharacterOffset& characterOffset)
+{
+    CharacterOffset previous = characterOffset;
+    do {
+        previous = previousCharacterOffset(previous);
+        textMarkerDataForCharacterOffset(textMarkerData, previous);
+    } while (textMarkerData.ignored);
+}
+
 Node* AXObjectCache::nextNode(Node* node) const
 {
     if (!node)
@@ -1875,20 +1903,20 @@ void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData& textMarkerD
     cache->setNodeInUse(domNode);
 }
 
-CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset& characterOffset)
+CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset& characterOffset, bool ignoreStart)
 {
     if (characterOffset.isNull())
         return CharacterOffset();
     
-    return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset + 1);
+    return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset + 1, ignoreStart ? TraverseOptionDefault : TraverseOptionIncludeStart);
 }
 
-CharacterOffset AXObjectCache::previousCharacterOffset(const CharacterOffset& characterOffset)
+CharacterOffset AXObjectCache::previousCharacterOffset(const CharacterOffset& characterOffset, bool ignoreStart)
 {
     if (characterOffset.isNull())
         return CharacterOffset();
     
-    return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset - 1, false, false);
+    return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset - 1, ignoreStart ? TraverseOptionDefault : TraverseOptionIncludeStart);
 }
 
 static unsigned startWordBoundary(StringView text, unsigned offset, BoundarySearchContextAvailability mayHaveMoreContext, bool& needMoreContext)
@@ -1925,9 +1953,8 @@ CharacterOffset AXObjectCache::startCharacterOffsetOfWord(const CharacterOffset&
     
     CharacterOffset c = characterOffset;
     if (side == RightWordIfOnBoundary) {
-        // FIXME: need to remove this when isEndOfParagraph is implemented for CharacterOffset.
-        VisiblePosition vp = visiblePositionFromCharacterOffset(c);
-        if (isEndOfParagraph(vp))
+        CharacterOffset endOfParagraph = endCharacterOffsetOfParagraph(c);
+        if (c.isEqual(endOfParagraph))
             return c;
         
         c = nextCharacterOffset(characterOffset);
@@ -1945,12 +1972,11 @@ CharacterOffset AXObjectCache::endCharacterOffsetOfWord(const CharacterOffset& c
     
     CharacterOffset c = characterOffset;
     if (side == LeftWordIfOnBoundary) {
-        // FIXME: need to remove this when isStartOfParagraph is implemented for CharacterOffset.
-        VisiblePosition vp = visiblePositionFromCharacterOffset(c);
-        if (isStartOfParagraph(vp))
+        CharacterOffset startOfParagraph = startCharacterOffsetOfParagraph(c);
+        if (c.isEqual(startOfParagraph))
             return c;
         
-        c = previousCharacterOffset(characterOffset);
+        c = previousCharacterOffset(characterOffset, false);
         if (c.isNull())
             return characterOffset;
     }
@@ -1963,7 +1989,7 @@ CharacterOffset AXObjectCache::previousWordStartCharacterOffset(const CharacterO
     if (characterOffset.isNull())
         return CharacterOffset();
     
-    CharacterOffset previousOffset = previousCharacterOffset(characterOffset);
+    CharacterOffset previousOffset = previousCharacterOffset(characterOffset, false);
     if (previousOffset.isNull())
         return CharacterOffset();
     
@@ -2111,11 +2137,82 @@ CharacterOffset AXObjectCache::previousWordBoundary(CharacterOffset& characterOf
         // The next variable contains a usable index into a text node
         if (&node == characterOffset.node)
             next -= characterOffset.startIndex;
-        return characterOffsetForNodeAndOffset(node, next, false);
+        return characterOffsetForNodeAndOffset(node, next, TraverseOptionIncludeStart);
     }
     
     int characterCount = characterOffset.offset - (string.size() - suffixLength - next);
-    return characterOffsetForNodeAndOffset(*characterOffset.node, characterCount, false, false);
+    return characterOffsetForNodeAndOffset(*characterOffset.node, characterCount, TraverseOptionIncludeStart);
+}
+
+CharacterOffset AXObjectCache::startCharacterOffsetOfParagraph(const CharacterOffset& characterOffset, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
+    if (characterOffset.isNull())
+        return CharacterOffset();
+    
+    Node* startNode = characterOffset.node;
+    
+    if (isRenderedAsNonInlineTableImageOrHR(startNode))
+        return startOrEndCharacterOffsetForRange(rangeForNodeContents(startNode), true);
+    
+    Node* startBlock = enclosingBlock(startNode);
+    int offset = characterOffset.startIndex + characterOffset.offset;
+    Position p(startNode, offset, Position::PositionIsOffsetInAnchor);
+    Node* highestRoot = highestEditableRoot(p);
+    Position::AnchorType type = Position::PositionIsOffsetInAnchor;
+    
+    Node* node = findStartOfParagraph(startNode, highestRoot, startBlock, offset, type, boundaryCrossingRule);
+    
+    if (type == Position::PositionIsOffsetInAnchor)
+        return characterOffsetForNodeAndOffset(*node, offset, TraverseOptionIncludeStart);
+    
+    return startOrEndCharacterOffsetForRange(rangeForNodeContents(node), true);
+}
+
+CharacterOffset AXObjectCache::endCharacterOffsetOfParagraph(const CharacterOffset& characterOffset, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
+    if (characterOffset.isNull())
+        return CharacterOffset();
+    
+    Node* startNode = characterOffset.node;
+    if (isRenderedAsNonInlineTableImageOrHR(startNode))
+        return startOrEndCharacterOffsetForRange(rangeForNodeContents(startNode), false);
+    
+    Node* stayInsideBlock = enclosingBlock(startNode);
+    int offset = characterOffset.startIndex + characterOffset.offset;
+    Position p(startNode, offset, Position::PositionIsOffsetInAnchor);
+    Node* highestRoot = highestEditableRoot(p);
+    Position::AnchorType type = Position::PositionIsOffsetInAnchor;
+    
+    Node* node = findEndOfParagraph(startNode, highestRoot, stayInsideBlock, offset, type, boundaryCrossingRule);
+    if (type == Position::PositionIsOffsetInAnchor) {
+        if (node->isTextNode()) {
+            CharacterOffset startOffset = startOrEndCharacterOffsetForRange(rangeForNodeContents(node), true);
+            offset -= startOffset.startIndex;
+        }
+        return characterOffsetForNodeAndOffset(*node, offset, TraverseOptionIncludeStart);
+    }
+    
+    return startOrEndCharacterOffsetForRange(rangeForNodeContents(node), false);
+}
+
+RefPtr<Range> AXObjectCache::paragraphForCharacterOffset(const CharacterOffset& characterOffset)
+{
+    CharacterOffset start = startCharacterOffsetOfParagraph(characterOffset);
+    CharacterOffset end = endCharacterOffsetOfParagraph(characterOffset);
+    
+    return rangeForUnorderedCharacterOffsets(start, end);
+}
+
+CharacterOffset AXObjectCache::nextParagraphEndCharacterOffset(const CharacterOffset& characterOffset)
+{
+    // make sure we move off of a paragraph end
+    return endCharacterOffsetOfParagraph(nextCharacterOffset(characterOffset));
+}
+
+CharacterOffset AXObjectCache::previousParagraphStartCharacterOffset(const CharacterOffset& characterOffset)
+{
+    // make sure we move off of a paragraph start
+    return startCharacterOffsetOfParagraph(previousCharacterOffset(characterOffset, false));
 }
 
 const Element* AXObjectCache::rootAXEditableElement(const Node* node)
index 3527b99..bb95868 100644 (file)
@@ -74,6 +74,12 @@ struct CharacterOffset {
     
     int remaining() const { return remainingOffset; }
     bool isNull() const { return !node; }
+    bool isEqual(CharacterOffset& other) const
+    {
+        if (isNull() || other.isNull())
+            return false;
+        return node == other.node && startIndex == other.startIndex && offset == other.offset;
+    }
 };
 
 class AXComputedObjectAttributeCache {
@@ -185,9 +191,13 @@ public:
 
     // Text marker utilities.
     void textMarkerDataForVisiblePosition(TextMarkerData&, const VisiblePosition&);
+    void textMarkerDataForCharacterOffset(TextMarkerData&, const CharacterOffset&);
+    void textMarkerDataForNextCharacterOffset(TextMarkerData&, const CharacterOffset&);
+    void textMarkerDataForPreviousCharacterOffset(TextMarkerData&, const CharacterOffset&);
     VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&);
     CharacterOffset characterOffsetForTextMarkerData(TextMarkerData&);
-    void textMarkerDataForCharacterOffset(TextMarkerData&, Node&, int, bool toNodeEnd = false, bool ignoreStart = true);
+    CharacterOffset nextCharacterOffset(const CharacterOffset&, bool ignoreStart = true);
+    CharacterOffset previousCharacterOffset(const CharacterOffset&, bool ignoreStart = true);
     void startOrEndTextMarkerDataForRange(TextMarkerData&, RefPtr<Range>, bool);
     AccessibilityObject* accessibilityObjectForTextMarkerData(TextMarkerData&);
     RefPtr<Range> rangeForUnorderedCharacterOffsets(const CharacterOffset&, const CharacterOffset&);
@@ -195,12 +205,15 @@ public:
     static int lengthForRange(Range*);
     
     // Word boundary
-    CharacterOffset startCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary);
-    CharacterOffset endCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary);
     CharacterOffset nextWordEndCharacterOffset(const CharacterOffset&);
     CharacterOffset previousWordStartCharacterOffset(const CharacterOffset&);
     RefPtr<Range> leftWordRange(const CharacterOffset&);
     RefPtr<Range> rightWordRange(const CharacterOffset&);
+    
+    // Paragraph
+    RefPtr<Range> paragraphForCharacterOffset(const CharacterOffset&);
+    CharacterOffset nextParagraphEndCharacterOffset(const CharacterOffset&);
+    CharacterOffset previousParagraphStartCharacterOffset(const CharacterOffset&);
 
     enum AXNotification {
         AXActiveDescendantChanged,
@@ -293,6 +306,7 @@ protected:
     bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
     
     // CharacterOffset functions.
+    enum TraverseOption { TraverseOptionDefault = 1 << 0, TraverseOptionToNodeEnd = 1 << 1, TraverseOptionIncludeStart = 1 << 2 };
     Node* nextNode(Node*) const;
     Node* previousNode(Node*) const;
     CharacterOffset traverseToOffsetInRange(RefPtr<Range>, int, bool, bool stayWithinRange = false);
@@ -302,11 +316,13 @@ protected:
     UChar32 characterAfter(const CharacterOffset&);
     UChar32 characterBefore(const CharacterOffset&);
     CharacterOffset startOrEndCharacterOffsetForRange(RefPtr<Range>, bool);
-    CharacterOffset characterOffsetForNodeAndOffset(Node&, int, bool toNodeEnd = false, bool ignoreStart = true);
-    CharacterOffset nextCharacterOffset(const CharacterOffset&);
-    CharacterOffset previousCharacterOffset(const CharacterOffset&);
+    CharacterOffset characterOffsetForNodeAndOffset(Node&, int, TraverseOption = TraverseOptionDefault);
     CharacterOffset previousWordBoundary(CharacterOffset&, BoundarySearchFunction);
     CharacterOffset nextWordBoundary(CharacterOffset&, BoundarySearchFunction);
+    CharacterOffset startCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary);
+    CharacterOffset endCharacterOffsetOfWord(const CharacterOffset&, EWordSide = RightWordIfOnBoundary);
+    CharacterOffset startCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
+    CharacterOffset endCharacterOffsetOfParagraph(const CharacterOffset&, EditingBoundaryCrossingRule = CannotCrossEditingBoundary);
 
 private:
     AccessibilityObject* rootWebArea();
index d40c97e..a6f836e 100644 (file)
@@ -189,7 +189,7 @@ static AccessibilityObjectWrapper* AccessibilityUnignoredAncestor(AccessibilityO
         return nil;
     
     TextMarkerData textMarkerData;
-    cache->textMarkerDataForCharacterOffset(textMarkerData, *characterOffset.node, characterOffset.offset, false);
+    cache->textMarkerDataForCharacterOffset(textMarkerData, characterOffset);
     if (!textMarkerData.axID && !textMarkerData.ignored)
         return nil;
     return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
@@ -2425,20 +2425,28 @@ static void AXAttributedStringAppendText(NSMutableAttributedString* attrString,
 
 - (WebAccessibilityTextMarker *)nextMarkerForCharacterOffset:(CharacterOffset&)characterOffset
 {
-    characterOffset.offset = characterOffset.offset + 1;
-    WebAccessibilityTextMarker *textMarker = [WebAccessibilityTextMarker textMarkerWithCharacterOffset:characterOffset cache:m_object->axObjectCache()];
-    if (textMarker && textMarker.isIgnored)
-        textMarker = [self nextMarkerForCharacterOffset:characterOffset];
-    return textMarker;
+    AXObjectCache* cache = m_object->axObjectCache();
+    if (!cache)
+        return nil;
+    
+    TextMarkerData textMarkerData;
+    cache->textMarkerDataForNextCharacterOffset(textMarkerData, characterOffset);
+    if (!textMarkerData.axID)
+        return nil;
+    return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
 }
 
 - (WebAccessibilityTextMarker *)previousMarkerForCharacterOffset:(CharacterOffset&)characterOffset
 {
-    characterOffset.offset = characterOffset.offset - 1;
-    WebAccessibilityTextMarker *textMarker = [WebAccessibilityTextMarker textMarkerWithCharacterOffset:characterOffset cache:m_object->axObjectCache()];
-    if (textMarker && textMarker.isIgnored)
-        textMarker = [self previousMarkerForCharacterOffset:characterOffset];
-    return textMarker;
+    AXObjectCache* cache = m_object->axObjectCache();
+    if (!cache)
+        return nil;
+    
+    TextMarkerData textMarkerData;
+    cache->textMarkerDataForPreviousCharacterOffset(textMarkerData, characterOffset);
+    if (!textMarkerData.axID)
+        return nil;
+    return [[[WebAccessibilityTextMarker alloc] initWithTextMarker:&textMarkerData cache:cache] autorelease];
 }
 
 - (RefPtr<Range>)rangeForTextMarkers:(NSArray *)textMarkers
index 2c70ad1..f7ffd3f 100644 (file)
@@ -840,45 +840,52 @@ static id startOrEndTextmarkerForRange(AXObjectCache* cache, RefPtr<Range> range
     return CFBridgingRelease(wkCreateAXTextMarker(&textMarkerData, sizeof(textMarkerData)));
 }
 
-- (id)nextTextMarkerForNode:(Node&)node offset:(int)offset
+static id nextTextMarkerForCharacterOffset(AXObjectCache* cache, CharacterOffset& characterOffset)
 {
-    int nextOffset = offset + 1;
-    id textMarker = [self textMarkerForNode:node offset:nextOffset];
-    if (isTextMarkerIgnored(textMarker))
-        textMarker = [self nextTextMarkerForNode:node offset:nextOffset];
-    return textMarker;
+    if (!cache)
+        return nil;
+    
+    TextMarkerData textMarkerData;
+    cache->textMarkerDataForNextCharacterOffset(textMarkerData, characterOffset);
+    if (!textMarkerData.axID)
+        return nil;
+    return CFBridgingRelease(wkCreateAXTextMarker(&textMarkerData, sizeof(textMarkerData)));
 }
 
-- (id)previousTextMarkerForNode:(Node&)node offset:(int)offset
+static id previousTextMarkerForCharacterOffset(AXObjectCache* cache, CharacterOffset& characterOffset)
 {
-    int previousOffset = offset - 1;
-    id textMarker = [self textMarkerForNode:node offset:previousOffset];
-    if (isTextMarkerIgnored(textMarker))
-        textMarker = [self previousTextMarkerForNode:node offset:previousOffset];
-    return textMarker;
+    if (!cache)
+        return nil;
+    
+    TextMarkerData textMarkerData;
+    cache->textMarkerDataForPreviousCharacterOffset(textMarkerData, characterOffset);
+    if (!textMarkerData.axID)
+        return nil;
+    return CFBridgingRelease(wkCreateAXTextMarker(&textMarkerData, sizeof(textMarkerData)));
 }
 
-- (id)textMarkerForNode:(Node&)node offset:(int)offset ignoreStart:(BOOL)ignoreStart
+- (id)nextTextMarkerForCharacterOffset:(CharacterOffset&)characterOffset
 {
-    return textMarkerForCharacterOffset(m_object->axObjectCache(), node, offset, false, ignoreStart);
+    return nextTextMarkerForCharacterOffset(m_object->axObjectCache(), characterOffset);
 }
 
-- (id)textMarkerForNode:(Node&)node offset:(int)offset
+- (id)previousTextMarkerForCharacterOffset:(CharacterOffset&)characterOffset
 {
-    return [self textMarkerForNode:node offset:offset ignoreStart:YES];
+    return previousTextMarkerForCharacterOffset(m_object->axObjectCache(), characterOffset);
 }
 
-static id textMarkerForCharacterOffset(AXObjectCache* cache, Node& node, int offset, bool toNodeEnd, bool ignoreStart)
+- (id)textMarkerForCharacterOffset:(CharacterOffset&)characterOffset
+{
+    return textMarkerForCharacterOffset(m_object->axObjectCache(), characterOffset);
+}
+
+static id textMarkerForCharacterOffset(AXObjectCache* cache, const CharacterOffset& characterOffset)
 {
     if (!cache)
         return nil;
     
-    Node* domNode = &node;
-    if (!domNode)
-        return nil;
-    
     TextMarkerData textMarkerData;
-    cache->textMarkerDataForCharacterOffset(textMarkerData, node, offset, toNodeEnd, ignoreStart);
+    cache->textMarkerDataForCharacterOffset(textMarkerData, characterOffset);
     if (!textMarkerData.axID && !textMarkerData.ignored)
         return nil;
     
@@ -4066,12 +4073,12 @@ static void formatForDebugger(const VisiblePositionRange& range, char* buffer, u
     
     if ([attribute isEqualToString:@"AXNextTextMarkerForTextMarker"]) {
         CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker];
-        return [self nextTextMarkerForNode:*characterOffset.node offset:characterOffset.offset];
+        return [self nextTextMarkerForCharacterOffset:characterOffset];
     }
     
     if ([attribute isEqualToString:@"AXPreviousTextMarkerForTextMarker"]) {
         CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker];
-        return [self previousTextMarkerForNode:*characterOffset.node offset:characterOffset.offset];
+        return [self previousTextMarkerForCharacterOffset:characterOffset];
     }
     
     if ([attribute isEqualToString:@"AXLeftWordTextMarkerRangeForTextMarker"]) {
@@ -4111,9 +4118,12 @@ static void formatForDebugger(const VisiblePositionRange& range, char* buffer, u
     }
     
     if ([attribute isEqualToString:@"AXParagraphTextMarkerRangeForTextMarker"]) {
-        VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
-        VisiblePositionRange vpRange = m_object->paragraphForPosition(visiblePos);
-        return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
+        AXObjectCache* cache = m_object->axObjectCache();
+        if (!cache)
+            return nil;
+        CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker];
+        RefPtr<Range> range = cache->paragraphForCharacterOffset(characterOffset);
+        return [self textMarkerRangeFromRange:range];
     }
     
     if ([attribute isEqualToString:@"AXNextWordEndTextMarkerForTextMarker"]) {
@@ -4122,7 +4132,7 @@ static void formatForDebugger(const VisiblePositionRange& range, char* buffer, u
             return nil;
         CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker];
         CharacterOffset nextEnd = cache->nextWordEndCharacterOffset(characterOffset);
-        return [self textMarkerForNode:*nextEnd.node offset:nextEnd.offset];
+        return [self textMarkerForCharacterOffset:nextEnd];
     }
     
     if ([attribute isEqualToString:@"AXPreviousWordStartTextMarkerForTextMarker"]) {
@@ -4131,7 +4141,7 @@ static void formatForDebugger(const VisiblePositionRange& range, char* buffer, u
             return nil;
         CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker];
         CharacterOffset previousStart = cache->previousWordStartCharacterOffset(characterOffset);
-        return [self textMarkerForNode:*previousStart.node offset:previousStart.offset ignoreStart:NO];
+        return [self textMarkerForCharacterOffset:previousStart];
     }
     
     if ([attribute isEqualToString:@"AXNextLineEndTextMarkerForTextMarker"]) {
@@ -4160,8 +4170,12 @@ static void formatForDebugger(const VisiblePositionRange& range, char* buffer, u
     }
     
     if ([attribute isEqualToString:@"AXPreviousParagraphStartTextMarkerForTextMarker"]) {
-        VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
-        return [self textMarkerForVisiblePosition:m_object->previousParagraphStartPosition(visiblePos)];
+        AXObjectCache* cache = m_object->axObjectCache();
+        if (!cache)
+            return nil;
+        CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker];
+        CharacterOffset previousStart = cache->previousParagraphStartCharacterOffset(characterOffset);
+        return [self textMarkerForCharacterOffset:previousStart];
     }
     
     if ([attribute isEqualToString:@"AXStyleTextMarkerRangeForTextMarker"]) {
index c99890d..9b2e2ef 100644 (file)
@@ -1142,24 +1142,9 @@ VisiblePosition nextSentencePosition(const VisiblePosition& position)
     return position.honorEditingBoundaryAtOrAfter(nextBoundary(position, nextSentencePositionBoundary));
 }
 
-VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
+Node* findStartOfParagraph(Node* startNode, Node* highestRoot, Node* startBlock, int& offset, Position::AnchorType& type, EditingBoundaryCrossingRule boundaryCrossingRule)
 {
-    Position p = c.deepEquivalent();
-    Node* startNode = p.deprecatedNode();
-
-    if (!startNode)
-        return VisiblePosition();
-    
-    if (isRenderedAsNonInlineTableImageOrHR(startNode))
-        return positionBeforeNode(startNode);
-
-    Node* startBlock = enclosingBlock(startNode);
-
     Node* node = startNode;
-    Node* highestRoot = highestEditableRoot(p);
-    int offset = p.deprecatedEditingOffset();
-    Position::AnchorType type = p.anchorType();
-
     Node* n = startNode;
     while (n) {
 #if ENABLE(USERSELECT_ALL)
@@ -1198,8 +1183,10 @@ VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossi
                 if (n == startNode && o < i)
                     i = std::max(0, o);
                 while (--i >= 0) {
-                    if (text[i] == '\n')
-                        return VisiblePosition(Position(downcast<Text>(n), i + 1), DOWNSTREAM);
+                    if (text[i] == '\n') {
+                        offset = i + 1;
+                        return n;
+                    }
                 }
             }
             node = n;
@@ -1213,33 +1200,12 @@ VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossi
             n = NodeTraversal::previousPostOrder(*n, startBlock);
     }
 
-    if (type == Position::PositionIsOffsetInAnchor) {
-        ASSERT(type == Position::PositionIsOffsetInAnchor || !offset);
-        return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
-    }
-
-    return VisiblePosition(Position(node, type), DOWNSTREAM);
+    return node;
 }
 
-VisiblePosition endOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
-{    
-    if (c.isNull())
-        return VisiblePosition();
-
-    Position p = c.deepEquivalent();
-    Node* startNode = p.deprecatedNode();
-
-    if (isRenderedAsNonInlineTableImageOrHR(startNode))
-        return positionAfterNode(startNode);
-    
-    Node* startBlock = enclosingBlock(startNode);
-    Node* stayInsideBlock = startBlock;
-    
+Node* findEndOfParagraph(Node* startNode, Node* highestRoot, Node* stayInsideBlock, int& offset, Position::AnchorType& type, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
     Node* node = startNode;
-    Node* highestRoot = highestEditableRoot(p);
-    int offset = p.deprecatedEditingOffset();
-    Position::AnchorType type = p.anchorType();
-
     Node* n = startNode;
     while (n) {
 #if ENABLE(USERSELECT_ALL)
@@ -1279,8 +1245,10 @@ VisiblePosition endOfParagraph(const VisiblePosition& c, EditingBoundaryCrossing
                 int o = n == startNode ? offset : 0;
                 int length = text.length();
                 for (int i = o; i < length; ++i) {
-                    if (text[i] == '\n')
-                        return VisiblePosition(Position(downcast<Text>(n), i), DOWNSTREAM);
+                    if (text[i] == '\n') {
+                        offset = i;
+                        return n;
+                    }
                 }
             }
             node = n;
@@ -1293,7 +1261,62 @@ VisiblePosition endOfParagraph(const VisiblePosition& c, EditingBoundaryCrossing
         } else
             n = NodeTraversal::next(*n, stayInsideBlock);
     }
+    return node;
+}
 
+VisiblePosition startOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
+{
+    Position p = c.deepEquivalent();
+    Node* startNode = p.deprecatedNode();
+    
+    if (!startNode)
+        return VisiblePosition();
+    
+    if (isRenderedAsNonInlineTableImageOrHR(startNode))
+        return positionBeforeNode(startNode);
+    
+    Node* startBlock = enclosingBlock(startNode);
+    
+    Node* highestRoot = highestEditableRoot(p);
+    int offset = p.deprecatedEditingOffset();
+    Position::AnchorType type = p.anchorType();
+    
+    Node* node = findStartOfParagraph(startNode, highestRoot, startBlock, offset, type, boundaryCrossingRule);
+    
+    if (is<Text>(node))
+        return VisiblePosition(Position(downcast<Text>(node), offset), DOWNSTREAM);
+    
+    if (type == Position::PositionIsOffsetInAnchor) {
+        ASSERT(type == Position::PositionIsOffsetInAnchor || !offset);
+        return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
+    }
+    
+    return VisiblePosition(Position(node, type), DOWNSTREAM);
+}
+
+VisiblePosition endOfParagraph(const VisiblePosition& c, EditingBoundaryCrossingRule boundaryCrossingRule)
+{    
+    if (c.isNull())
+        return VisiblePosition();
+    
+    Position p = c.deepEquivalent();
+    Node* startNode = p.deprecatedNode();
+    
+    if (isRenderedAsNonInlineTableImageOrHR(startNode))
+        return positionAfterNode(startNode);
+    
+    Node* startBlock = enclosingBlock(startNode);
+    Node* stayInsideBlock = startBlock;
+    
+    Node* highestRoot = highestEditableRoot(p);
+    int offset = p.deprecatedEditingOffset();
+    Position::AnchorType type = p.anchorType();
+    
+    Node* node = findEndOfParagraph(startNode, highestRoot, stayInsideBlock, offset, type, boundaryCrossingRule);
+    
+    if (is<Text>(node))
+        return VisiblePosition(Position(downcast<Text>(node), offset), DOWNSTREAM);
+    
     if (type == Position::PositionIsOffsetInAnchor)
         return VisiblePosition(Position(node, offset, type), DOWNSTREAM);
 
index 56307ea..bd5b705 100644 (file)
@@ -116,6 +116,8 @@ unsigned suffixLengthForRange(RefPtr<Range>, Vector<UChar, 1024>&);
 unsigned prefixLengthForRange(RefPtr<Range>, Vector<UChar, 1024>&);
 unsigned backwardSearchForBoundaryWithTextIterator(SimplifiedBackwardsTextIterator&, Vector<UChar, 1024>&, unsigned, BoundarySearchFunction);
 unsigned forwardSearchForBoundaryWithTextIterator(TextIterator&, Vector<UChar, 1024>&, unsigned, BoundarySearchFunction);
+Node* findStartOfParagraph(Node*, Node*, Node*, int&, Position::AnchorType&, EditingBoundaryCrossingRule);
+Node* findEndOfParagraph(Node*, Node*, Node*, int&, Position::AnchorType&, EditingBoundaryCrossingRule);
 
 } // namespace WebCore
 
index 8c05dc9..b3a0112 100644 (file)
@@ -1,3 +1,56 @@
+2016-02-12  Nan Wang  <n_wang@apple.com>
+
+        AX: Implement paragraph related text marker functions using TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=154098
+        <rdar://problem/24269675>
+
+        Reviewed by Chris Fleizach.
+
+        * DumpRenderTree/AccessibilityUIElement.cpp:
+        (nextWordEndTextMarkerForTextMarkerCallback):
+        (paragraphTextMarkerRangeForTextMarkerCallback):
+        (previousParagraphStartTextMarkerForTextMarkerCallback):
+        (nextParagraphEndTextMarkerForTextMarkerCallback):
+        (setSelectedVisibleTextRangeCallback):
+        (AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
+        (AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
+        (AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
+        (AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
+        (AccessibilityUIElement::getJSClass):
+        * DumpRenderTree/AccessibilityUIElement.h:
+        * DumpRenderTree/ios/AccessibilityUIElementIOS.mm:
+        (AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
+        (AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
+        (AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
+        (AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
+        * DumpRenderTree/mac/AccessibilityUIElementMac.mm:
+        (AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
+        (AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
+        (AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
+        (AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
+        (AccessibilityUIElement::supportedActions):
+        * WebKitTestRunner/InjectedBundle/AccessibilityUIElement.cpp:
+        (WTR::AccessibilityUIElement::rightWordTextMarkerRangeForTextMarker):
+        (WTR::AccessibilityUIElement::previousWordStartTextMarkerForTextMarker):
+        (WTR::AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
+        (WTR::AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
+        (WTR::AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
+        (WTR::AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
+        * WebKitTestRunner/InjectedBundle/AccessibilityUIElement.h:
+        * WebKitTestRunner/InjectedBundle/Bindings/AccessibilityUIElement.idl:
+        * WebKitTestRunner/InjectedBundle/ios/AccessibilityUIElementIOS.mm:
+        (WTR::AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
+        (WTR::AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
+        (WTR::AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
+        (WTR::AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
+        (WTR::AccessibilityUIElement::mathPostscriptsDescription):
+        * WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
+        (WTR::AccessibilityUIElement::nextWordEndTextMarkerForTextMarker):
+        (WTR::AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker):
+        (WTR::AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker):
+        (WTR::AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker):
+        (WTR::_convertMathMultiscriptPairsToString):
+
 2016-02-12  Jason Marcell  <jmarcell@apple.com>
 
         Open source bot watcher's dashboard fails assertion in BuildbotQueue.prototype.compareIterationsByRevisions
index eb60c12..f674960 100644 (file)
@@ -1010,6 +1010,33 @@ static JSValueRef nextWordEndTextMarkerForTextMarkerCallback(JSContextRef contex
     return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->nextWordEndTextMarkerForTextMarker(marker));
 }
 
+static JSValueRef paragraphTextMarkerRangeForTextMarkerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    AccessibilityTextMarker* marker = nullptr;
+    if (argumentCount == 1)
+        marker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+    
+    return AccessibilityTextMarkerRange::makeJSAccessibilityTextMarkerRange(context, toAXElement(thisObject)->paragraphTextMarkerRangeForTextMarker(marker));
+}
+
+static JSValueRef previousParagraphStartTextMarkerForTextMarkerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    AccessibilityTextMarker* marker = nullptr;
+    if (argumentCount == 1)
+        marker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+    
+    return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->previousParagraphStartTextMarkerForTextMarker(marker));
+}
+
+static JSValueRef nextParagraphEndTextMarkerForTextMarkerCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
+{
+    AccessibilityTextMarker* marker = nullptr;
+    if (argumentCount == 1)
+        marker = toTextMarker(JSValueToObject(context, arguments[0], exception));
+    
+    return AccessibilityTextMarker::makeJSAccessibilityTextMarker(context, toAXElement(thisObject)->nextParagraphEndTextMarkerForTextMarker(marker));
+}
+
 static JSValueRef setSelectedVisibleTextRangeCallback(JSContextRef context, JSObjectRef function, JSObjectRef thisObject, size_t argumentCount, const JSValueRef arguments[], JSValueRef* exception)
 {
     AccessibilityUIElement* uiElement = toAXElement(thisObject);
@@ -1620,6 +1647,21 @@ AccessibilityTextMarker AccessibilityUIElement::nextWordEndTextMarkerForTextMark
     return nullptr;
 }
 
+AccessibilityTextMarkerRange AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker*)
+{
+    return nullptr;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker*)
+{
+    return nullptr;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker*)
+{
+    return nullptr;
+}
+
 #endif
 
 // Destruction
@@ -1804,6 +1846,9 @@ JSClassRef AccessibilityUIElement::getJSClass()
         { "rightWordTextMarkerRangeForTextMarker", rightWordTextMarkerRangeForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "previousWordStartTextMarkerForTextMarker", previousWordStartTextMarkerForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "nextWordEndTextMarkerForTextMarker", nextWordEndTextMarkerForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "paragraphTextMarkerRangeForTextMarker", paragraphTextMarkerRangeForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "previousParagraphStartTextMarkerForTextMarker", previousParagraphStartTextMarkerForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
+        { "nextParagraphEndTextMarkerForTextMarker", nextParagraphEndTextMarkerForTextMarkerCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setSelectedChild", setSelectedChildCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "setSelectedChildAtIndex", setSelectedChildAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
         { "removeSelectionAtIndex", removeSelectionAtIndexCallback, kJSPropertyAttributeReadOnly | kJSPropertyAttributeDontDelete },
index 9fd770a..d950db8 100644 (file)
@@ -264,6 +264,9 @@ public:
     AccessibilityTextMarkerRange rightWordTextMarkerRangeForTextMarker(AccessibilityTextMarker*);
     AccessibilityTextMarker previousWordStartTextMarkerForTextMarker(AccessibilityTextMarker*);
     AccessibilityTextMarker nextWordEndTextMarkerForTextMarker(AccessibilityTextMarker*);
+    AccessibilityTextMarkerRange paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker*);
+    AccessibilityTextMarker previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker*);
+    AccessibilityTextMarker nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker*);
     AccessibilityTextMarkerRange selectedTextMarkerRange();
     void resetSelectedTextMarkerRange();
     bool setSelectedVisibleTextRange(AccessibilityTextMarkerRange*);
index e82bb31..58e40ce 100644 (file)
@@ -570,6 +570,21 @@ AccessibilityTextMarker AccessibilityUIElement::nextWordEndTextMarkerForTextMark
     return nullptr;
 }
 
+AccessibilityTextMarkerRange AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker*)
+{
+    return nullptr;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker*)
+{
+    return nullptr;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker*)
+{
+    return nullptr;
+}
+
 #endif // SUPPORTS_AX_TEXTMARKERS && PLATFORM(IOS)
 
 #pragma mark Unused
index a19242b..59d258c 100644 (file)
@@ -1886,6 +1886,36 @@ AccessibilityTextMarker AccessibilityUIElement::nextWordEndTextMarkerForTextMark
     return nullptr;
 }
 
+AccessibilityTextMarkerRange AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    BEGIN_AX_OBJC_EXCEPTIONS
+    id textMarkerRange = [m_element accessibilityAttributeValue:@"AXParagraphTextMarkerRangeForTextMarker" forParameter:(id)textMarker->platformTextMarker()];
+    return AccessibilityTextMarkerRange(textMarkerRange);
+    END_AX_OBJC_EXCEPTIONS
+    
+    return nullptr;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    BEGIN_AX_OBJC_EXCEPTIONS
+    id previousTextMarker = [m_element accessibilityAttributeValue:@"AXPreviousParagraphStartTextMarkerForTextMarker" forParameter:(id)textMarker->platformTextMarker()];
+    return AccessibilityTextMarker(previousTextMarker);
+    END_AX_OBJC_EXCEPTIONS
+    
+    return nullptr;
+}
+
+AccessibilityTextMarker AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    BEGIN_AX_OBJC_EXCEPTIONS
+    id nextTextMarker = [m_element accessibilityAttributeValue:@"AXNextParagraphEndTextMarkerForTextMarker" forParameter:(id)textMarker->platformTextMarker()];
+    return AccessibilityTextMarker(nextTextMarker);
+    END_AX_OBJC_EXCEPTIONS
+    
+    return nullptr;
+}
+
 #endif // SUPPORTS_AX_TEXTMARKERS && PLATFORM(MAC)
 
 JSStringRef AccessibilityUIElement::supportedActions()
index 2464607..c7607b3 100644 (file)
@@ -249,6 +249,9 @@ PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::leftWordTextMar
 PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::rightWordTextMarkerRangeForTextMarker(AccessibilityTextMarker*) { return nullptr; }
 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousWordStartTextMarkerForTextMarker(AccessibilityTextMarker*) { return nullptr; }
 PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextWordEndTextMarkerForTextMarker(AccessibilityTextMarker*) { return nullptr; }
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker*) { return nullptr; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker*) { return nullptr; }
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker*) { return nullptr; }
 #endif
 
 } // namespace WTR
index e917550..c842b63 100644 (file)
@@ -255,6 +255,9 @@ public:
     PassRefPtr<AccessibilityTextMarkerRange> rightWordTextMarkerRangeForTextMarker(AccessibilityTextMarker*);
     PassRefPtr<AccessibilityTextMarker> previousWordStartTextMarkerForTextMarker(AccessibilityTextMarker*);
     PassRefPtr<AccessibilityTextMarker> nextWordEndTextMarkerForTextMarker(AccessibilityTextMarker*);
+    PassRefPtr<AccessibilityTextMarkerRange> paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker*);
+    PassRefPtr<AccessibilityTextMarker> nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker*);
+    PassRefPtr<AccessibilityTextMarker> previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker*);
 
     // Returns an ordered list of supported actions for an element.
     JSRetainPtr<JSStringRef> supportedActions() const;
index bdf31f7..84beed6 100644 (file)
@@ -205,6 +205,9 @@ interface AccessibilityUIElement {
     AccessibilityTextMarkerRange rightWordTextMarkerRangeForTextMarker(AccessibilityTextMarker textMarker);
     AccessibilityTextMarker previousWordStartTextMarkerForTextMarker(AccessibilityTextMarker textMarker);
     AccessibilityTextMarker nextWordEndTextMarkerForTextMarker(AccessibilityTextMarker textMarker);
+    AccessibilityTextMarkerRange paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker textMarker);
+    AccessibilityTextMarker previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker textMarker);
+    AccessibilityTextMarker nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker textMarker);
 
     // Returns an ordered list of supported actions for an element.
     readonly attribute DOMString supportedActions;
index a82becc..0f7ac04 100644 (file)
@@ -1116,6 +1116,21 @@ PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextWordEndTextMarke
     return nullptr;
 }
 
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    return nullptr;
+}
+
 JSRetainPtr<JSStringRef> AccessibilityUIElement::mathPostscriptsDescription() const
 {
     return 0;
index 94a913c..56a717c 100644 (file)
@@ -1913,6 +1913,36 @@ PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextWordEndTextMarke
     return nullptr;
 }
 
+PassRefPtr<AccessibilityTextMarkerRange> AccessibilityUIElement::paragraphTextMarkerRangeForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    BEGIN_AX_OBJC_EXCEPTIONS
+    id textMarkerRange = [m_element accessibilityAttributeValue:@"AXParagraphTextMarkerRangeForTextMarker" forParameter:(id)textMarker->platformTextMarker()];
+    return AccessibilityTextMarkerRange::create(textMarkerRange);
+    END_AX_OBJC_EXCEPTIONS
+    
+    return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::previousParagraphStartTextMarkerForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    BEGIN_AX_OBJC_EXCEPTIONS
+    id previousParagraphStartMarker = [m_element accessibilityAttributeValue:@"AXPreviousParagraphStartTextMarkerForTextMarker" forParameter:(id)textMarker->platformTextMarker()];
+    return AccessibilityTextMarker::create(previousParagraphStartMarker);
+    END_AX_OBJC_EXCEPTIONS
+    
+    return nullptr;
+}
+
+PassRefPtr<AccessibilityTextMarker> AccessibilityUIElement::nextParagraphEndTextMarkerForTextMarker(AccessibilityTextMarker* textMarker)
+{
+    BEGIN_AX_OBJC_EXCEPTIONS
+    id nextParagraphEndMarker = [m_element accessibilityAttributeValue:@"AXNextParagraphEndTextMarkerForTextMarker" forParameter:(id)textMarker->platformTextMarker()];
+    return AccessibilityTextMarker::create(nextParagraphEndMarker);
+    END_AX_OBJC_EXCEPTIONS
+    
+    return nullptr;
+}
+
 static NSString *_convertMathMultiscriptPairsToString(NSArray *pairs)
 {
     __block NSMutableString *result = [NSMutableString string];