<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head><meta http-equiv="content-type" content="text/html; charset=utf-8" />
<title>[195240] trunk</title>
</head>
<body>

<style type="text/css"><!--
#msg dl.meta { border: 1px #006 solid; background: #369; padding: 6px; color: #fff; }
#msg dl.meta dt { float: left; width: 6em; font-weight: bold; }
#msg dt:after { content:':';}
#msg dl, #msg dt, #msg ul, #msg li, #header, #footer, #logmsg { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt;  }
#msg dl a { font-weight: bold}
#msg dl a:link    { color:#fc3; }
#msg dl a:active  { color:#ff0; }
#msg dl a:visited { color:#cc6; }
h3 { font-family: verdana,arial,helvetica,sans-serif; font-size: 10pt; font-weight: bold; }
#msg pre { overflow: auto; background: #ffc; border: 1px #fa0 solid; padding: 6px; }
#logmsg { background: #ffc; border: 1px #fa0 solid; padding: 1em 1em 0 1em; }
#logmsg p, #logmsg pre, #logmsg blockquote { margin: 0 0 1em 0; }
#logmsg p, #logmsg li, #logmsg dt, #logmsg dd { line-height: 14pt; }
#logmsg h1, #logmsg h2, #logmsg h3, #logmsg h4, #logmsg h5, #logmsg h6 { margin: .5em 0; }
#logmsg h1:first-child, #logmsg h2:first-child, #logmsg h3:first-child, #logmsg h4:first-child, #logmsg h5:first-child, #logmsg h6:first-child { margin-top: 0; }
#logmsg ul, #logmsg ol { padding: 0; list-style-position: inside; margin: 0 0 0 1em; }
#logmsg ul { text-indent: -1em; padding-left: 1em; }#logmsg ol { text-indent: -1.5em; padding-left: 1.5em; }
#logmsg > ul, #logmsg > ol { margin: 0 0 1em 0; }
#logmsg pre { background: #eee; padding: 1em; }
#logmsg blockquote { border: 1px solid #fa0; border-left-width: 10px; padding: 1em 1em 0 1em; background: white;}
#logmsg dl { margin: 0; }
#logmsg dt { font-weight: bold; }
#logmsg dd { margin: 0; padding: 0 0 0.5em 0; }
#logmsg dd:before { content:'\00bb';}
#logmsg table { border-spacing: 0px; border-collapse: collapse; border-top: 4px solid #fa0; border-bottom: 1px solid #fa0; background: #fff; }
#logmsg table th { text-align: left; font-weight: normal; padding: 0.2em 0.5em; border-top: 1px dotted #fa0; }
#logmsg table td { text-align: right; border-top: 1px dotted #fa0; padding: 0.2em 0.5em; }
#logmsg table thead th { text-align: center; border-bottom: 1px solid #fa0; }
#logmsg table th.Corner { text-align: left; }
#logmsg hr { border: none 0; border-top: 2px dashed #fa0; height: 1px; }
#header, #footer { color: #fff; background: #636; border: 1px #300 solid; padding: 6px; }
#patch { width: 100%; }
#patch h4 {font-family: verdana,arial,helvetica,sans-serif;font-size:10pt;padding:8px;background:#369;color:#fff;margin:0;}
#patch .propset h4, #patch .binary h4 {margin:0;}
#patch pre {padding:0;line-height:1.2em;margin:0;}
#patch .diff {width:100%;background:#eee;padding: 0 0 10px 0;overflow:auto;}
#patch .propset .diff, #patch .binary .diff  {padding:10px 0;}
#patch span {display:block;padding:0 10px;}
#patch .modfile, #patch .addfile, #patch .delfile, #patch .propset, #patch .binary, #patch .copfile {border:1px solid #ccc;margin:10px 0;}
#patch ins {background:#dfd;text-decoration:none;display:block;padding:0 10px;}
#patch del {background:#fdd;text-decoration:none;display:block;padding:0 10px;}
#patch .lines, .info {color:#888;background:#fff;}
--></style>
<div id="msg">
<dl class="meta">
<dt>Revision</dt> <dd><a href="http://trac.webkit.org/projects/webkit/changeset/195240">195240</a></dd>
<dt>Author</dt> <dd>n_wang@apple.com</dd>
<dt>Date</dt> <dd>2016-01-18 16:56:13 -0800 (Mon, 18 Jan 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>AX: [Mac] Implement next/previous text marker functions using TextIterator
https://bugs.webkit.org/show_bug.cgi?id=152728

Reviewed by Chris Fleizach.

Source/WebCore:

The existing AXTextMarker based calls are implemented using visible position, and that introduced
some bugs which make VoiceOver working incorrectly on Mac sometimes. Since TextIterator uses rendering
position, we tried to use it to refactor those AXTextMarker based calls.
In this patch, I implemented functions to navigate to previous/next text marker using Range and TextIterator.
Also added a conversion between visible position and character offset to make sure unconverted text marker
related functions are still working correctly.

Tests: accessibility/mac/previous-next-text-marker.html
       accessibility/mac/text-marker-with-user-select-none.html

* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::visiblePositionForTextMarkerData):
(WebCore::AXObjectCache::traverseToOffsetInRange):
(WebCore::AXObjectCache::lengthForRange):
(WebCore::AXObjectCache::rangeForNodeContents):
(WebCore::characterOffsetsInOrder):
(WebCore::AXObjectCache::rangeForUnorderedCharacterOffsets):
(WebCore::AXObjectCache::setTextMarkerDataWithCharacterOffset):
(WebCore::AXObjectCache::startOrEndTextMarkerDataForRange):
(WebCore::AXObjectCache::textMarkerDataForCharacterOffset):
(WebCore::AXObjectCache::nextNode):
(WebCore::AXObjectCache::previousNode):
(WebCore::AXObjectCache::visiblePositionFromCharacterOffset):
(WebCore::AXObjectCache::characterOffsetFromVisiblePosition):
(WebCore::AXObjectCache::accessibilityObjectForTextMarkerData):
(WebCore::AXObjectCache::textMarkerDataForVisiblePosition):
* accessibility/AXObjectCache.h:
(WebCore::CharacterOffset::CharacterOffset):
(WebCore::CharacterOffset::remaining):
(WebCore::CharacterOffset::isNull):
(WebCore::AXObjectCache::setNodeInUse):
(WebCore::AXObjectCache::removeNodeForUse):
(WebCore::AXObjectCache::isNodeInUse):
* accessibility/AccessibilityObject.cpp:
(WebCore::AccessibilityObject::selectionRange):
(WebCore::AccessibilityObject::elementRange):
(WebCore::AccessibilityObject::selectText):
(WebCore::AccessibilityObject::lineRangeForPosition):
(WebCore::AccessibilityObject::replacedNodeNeedsCharacter):
(WebCore::renderListItemContainerForNode):
(WebCore::listMarkerTextForNode):
(WebCore::AccessibilityObject::listMarkerTextForNodeAndPosition):
(WebCore::AccessibilityObject::stringForRange):
(WebCore::AccessibilityObject::stringForVisiblePositionRange):
(WebCore::replacedNodeNeedsCharacter): Deleted.
* accessibility/AccessibilityObject.h:
(WebCore::AccessibilityObject::visiblePositionRange):
(WebCore::AccessibilityObject::visiblePositionRangeForLine):
(WebCore::AccessibilityObject::boundsForVisiblePositionRange):
(WebCore::AccessibilityObject::setSelectedVisiblePositionRange):
* accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(isTextMarkerIgnored):
(-[WebAccessibilityObjectWrapper accessibilityObjectForTextMarker:]):
(accessibilityObjectForTextMarker):
(-[WebAccessibilityObjectWrapper textMarkerRangeFromRange:]):
(textMarkerRangeFromRange):
(-[WebAccessibilityObjectWrapper startOrEndTextMarkerForRange:isStart:]):
(startOrEndTextmarkerForRange):
(-[WebAccessibilityObjectWrapper nextTextMarkerForNode:offset:]):
(-[WebAccessibilityObjectWrapper previousTextMarkerForNode:offset:]):
(-[WebAccessibilityObjectWrapper textMarkerForNode:offset:]):
(textMarkerForCharacterOffset):
(-[WebAccessibilityObjectWrapper rangeForTextMarkerRange:]):
(-[WebAccessibilityObjectWrapper characterOffsetForTextMarker:]):
(textMarkerForVisiblePosition):
(-[WebAccessibilityObjectWrapper accessibilityAttributeValue:forParameter:]):

Tools:

* WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
(WTR::AccessibilityUIElement::accessibilityElementForTextMarker):

LayoutTests:

* accessibility/mac/previous-next-text-marker-expected.txt: Added.
* accessibility/mac/previous-next-text-marker.html: Added.
* accessibility/mac/text-marker-with-user-select-none-expected.txt: Added.
* accessibility/mac/text-marker-with-user-select-none.html: Added.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreaccessibilityAXObjectCachecpp">trunk/Source/WebCore/accessibility/AXObjectCache.cpp</a></li>
<li><a href="#trunkSourceWebCoreaccessibilityAXObjectCacheh">trunk/Source/WebCore/accessibility/AXObjectCache.h</a></li>
<li><a href="#trunkSourceWebCoreaccessibilityAccessibilityObjectcpp">trunk/Source/WebCore/accessibility/AccessibilityObject.cpp</a></li>
<li><a href="#trunkSourceWebCoreaccessibilityAccessibilityObjecth">trunk/Source/WebCore/accessibility/AccessibilityObject.h</a></li>
<li><a href="#trunkSourceWebCoreaccessibilitymacWebAccessibilityObjectWrapperMacmm">trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm</a></li>
<li><a href="#trunkToolsChangeLog">trunk/Tools/ChangeLog</a></li>
<li><a href="#trunkToolsWebKitTestRunnerInjectedBundlemacAccessibilityUIElementMacmm">trunk/Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsaccessibilitymacpreviousnexttextmarkerexpectedtxt">trunk/LayoutTests/accessibility/mac/previous-next-text-marker-expected.txt</a></li>
<li><a href="#trunkLayoutTestsaccessibilitymacpreviousnexttextmarkerhtml">trunk/LayoutTests/accessibility/mac/previous-next-text-marker.html</a></li>
<li><a href="#trunkLayoutTestsaccessibilitymactextmarkerwithuserselectnoneexpectedtxt">trunk/LayoutTests/accessibility/mac/text-marker-with-user-select-none-expected.txt</a></li>
<li><a href="#trunkLayoutTestsaccessibilitymactextmarkerwithuserselectnonehtml">trunk/LayoutTests/accessibility/mac/text-marker-with-user-select-none.html</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/LayoutTests/ChangeLog        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -1,3 +1,15 @@
</span><ins>+2016-01-18  Nan Wang  &lt;n_wang@apple.com&gt;
+
+        AX: [Mac] Implement next/previous text marker functions using TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=152728
+
+        Reviewed by Chris Fleizach.
+
+        * accessibility/mac/previous-next-text-marker-expected.txt: Added.
+        * accessibility/mac/previous-next-text-marker.html: Added.
+        * accessibility/mac/text-marker-with-user-select-none-expected.txt: Added.
+        * accessibility/mac/text-marker-with-user-select-none.html: Added.
+
</ins><span class="cx"> 2016-01-17  Simon Fraser  &lt;simon.fraser@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         More displaylist tests, and minor cleanup
</span></span></pre></div>
<a id="trunkLayoutTestsaccessibilitymacpreviousnexttextmarkerexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/accessibility/mac/previous-next-text-marker-expected.txt (0 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/accessibility/mac/previous-next-text-marker-expected.txt                                (rev 0)
+++ trunk/LayoutTests/accessibility/mac/previous-next-text-marker-expected.txt        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -0,0 +1,37 @@
</span><ins>+text
+
+text1
+c  d
+
+can't select
+This tests the next/previous text marker functions are implemented correctly.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS text.textMarkerRangeLength(textMarkerRange) is 4
+PASS text.accessibilityElementForTextMarker(startMarker).isEqual(text) is true
+PASS text.accessibilityElementForTextMarker(endMarker).isEqual(text) is true
+PASS element.stringValue is 'AXValue: '
+PASS element.stringValue is 'AXValue: text1'
+PASS element.stringValue is 'AXValue: '
+PASS element.stringValue is 'AXValue: text'
+PASS text2.textMarkerRangeLength(textMarkerRange2) is 5
+Object string for range: c [ATTACHMENT] d
+AXValue: c 
+AXValue: c 
+AXValue: 
+AXValue:  d
+AXValue: 
+AXValue: c 
+AXValue: c 
+AXValue: text1
+PASS text3.stringForTextMarkerRange(markerRange) is 'ect'
+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 successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsaccessibilitymacpreviousnexttextmarkerhtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/accessibility/mac/previous-next-text-marker.html (0 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/accessibility/mac/previous-next-text-marker.html                                (rev 0)
+++ trunk/LayoutTests/accessibility/mac/previous-next-text-marker.html        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -0,0 +1,136 @@
</span><ins>+&lt;!DOCTYPE HTML PUBLIC &quot;-//IETF//DTD HTML//EN&quot;&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;style&gt;
+.userselect { user-select: none; -webkit-user-select: none; }
+&lt;/style&gt;
+&lt;body id=&quot;body&quot;&gt;
+
+&lt;div id=&quot;text&quot; tabindex=&quot;0&quot;&gt;text&lt;/div&gt;
+&lt;br&gt;
+text1
+
+&lt;div id=&quot;text2&quot;&gt;
+c &lt;img src=&quot;#&quot; aria-label=&quot;blah&quot; style=&quot;background-color: #aaaaaa; width: 100px; height: 100px;&quot;&gt; d
+&lt;/div&gt;
+
+&lt;input type=&quot;password&quot; id=&quot;psw&quot;&gt;
+
+&lt;div class=&quot;userselect&quot; id=&quot;text3&quot;&gt;can't select&lt;/div&gt;
+
+&lt;p id=&quot;description&quot;&gt;&lt;/p&gt;
+&lt;div id=&quot;console&quot;&gt;&lt;/div&gt;
+
+&lt;script&gt;
+
+    description(&quot;This tests the next/previous text marker functions are implemented correctly.&quot;);
+    
+    if (window.accessibilityController) {
+        
+        var text = accessibilityController.accessibleElementById(&quot;text&quot;);
+        // Get the actual text node.
+        text = text.childAtIndex(0);
+        
+        // Check that we can get the start/end marker for this range.
+        var textMarkerRange = text.textMarkerRangeForElement(text);
+        shouldBe(&quot;text.textMarkerRangeLength(textMarkerRange)&quot;, &quot;4&quot;);
+        
+        var startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
+        var endMarker = text.endTextMarkerForTextMarkerRange(textMarkerRange);
+        shouldBeTrue(&quot;text.accessibilityElementForTextMarker(startMarker).isEqual(text)&quot;);
+        shouldBeTrue(&quot;text.accessibilityElementForTextMarker(endMarker).isEqual(text)&quot;);
+        
+        // Check next text marker. (Advance 5 characters, it will land at &lt;br&gt;.)
+        var currentMarker = startMarker;
+        for (var i = 0; i &lt; 5; i++) {
+            currentMarker = text.nextTextMarker(currentMarker);
+        }
+        var element = text.accessibilityElementForTextMarker(currentMarker);
+        shouldBe(&quot;element.stringValue&quot;, &quot;'AXValue: '&quot;);
+        
+        // Advance one more character, it will lande at &quot;t&quot; in &quot;text1&quot;.
+        currentMarker = text.nextTextMarker(currentMarker);
+        element = text.accessibilityElementForTextMarker(currentMarker);
+        shouldBe(&quot;element.stringValue&quot;, &quot;'AXValue: text1'&quot;);
+        
+        // Check previous text marker. (Traverse backwards one character, it will land at &lt;br&gt;.)
+        currentMarker = text.previousTextMarker(currentMarker);
+        element = text.accessibilityElementForTextMarker(currentMarker);
+        shouldBe(&quot;element.stringValue&quot;, &quot;'AXValue: '&quot;);
+        
+        // Traverse backwards one more character, it will land at the last character of &quot;text&quot;.
+        currentMarker = text.previousTextMarker(currentMarker);
+        element = text.accessibilityElementForTextMarker(currentMarker);
+        shouldBe(&quot;element.stringValue&quot;, &quot;'AXValue: text'&quot;);
+        
+        // Check the case with replaced node
+        var text2 = accessibilityController.accessibleElementById(&quot;text2&quot;);
+        var textMarkerRange2 = text2.textMarkerRangeForElement(text2);
+        shouldBe(&quot;text2.textMarkerRangeLength(textMarkerRange2)&quot;, &quot;5&quot;);
+        var str = text2.stringForTextMarkerRange(textMarkerRange2).replace(String.fromCharCode(65532), &quot;[ATTACHMENT]&quot;);
+        debug(&quot;Object string for range: &quot; + str);
+        
+        currentMarker = text2.startTextMarkerForTextMarkerRange(textMarkerRange2);
+        // Advance 4 characters, it will land at first character of &quot; d&quot;.
+        for (var i = 0; i &lt; 4; i++) {
+            currentMarker = text2.nextTextMarker(currentMarker);
+            element = text2.accessibilityElementForTextMarker(currentMarker);
+            debug(element.stringValue);
+        }
+        
+        // Traverse backwards 4 characters, it will land at the last character of &quot;text1&quot;.
+        for (var i = 0; i &lt; 4; i++) {
+            currentMarker = text2.previousTextMarker(currentMarker);
+            element = text2.accessibilityElementForTextMarker(currentMarker);
+            debug(element.stringValue);
+        }
+        
+        // Check the case with user-select:none, nextTextMarker/previousTextMarker should still work.
+        var text3 = accessibilityController.accessibleElementById(&quot;text3&quot;);
+        text3 = text3.childAtIndex(0);
+        // Advance to land at user-select:none node.
+        var marker1, marker2;
+        for (var i = 0; i &lt; 17; i++) {
+            currentMarker = text3.nextTextMarker(currentMarker);
+            // i == 13, it should land on &quot;e&quot;, and i == 16, it should land on &quot;t&quot;
+            if (i == 13) {
+                marker1 = currentMarker;
+            }
+        }
+        marker2 = currentMarker;
+        var markerRange = text3.textMarkerRangeForMarkers(marker1, marker2);
+        shouldBe(&quot;text3.stringForTextMarkerRange(markerRange)&quot;, &quot;'ect'&quot;);
+        // Iterate backwards the second marker for 6 characters, the range should be &quot;sel&quot;
+        for (var i = 0; i &lt; 6; i++) {
+            currentMarker = text3.previousTextMarker(currentMarker);
+        }
+        marker2 = currentMarker;
+        markerRange = text3.textMarkerRangeForMarkers(marker1, marker2);
+        shouldBe(&quot;text3.stringForTextMarkerRange(markerRange)&quot;, &quot;'sel'&quot;);
+
+        // Check the case with password field.
+        var psw = accessibilityController.accessibleElementById(&quot;psw&quot;);
+        var textMarkerRange3 = psw.textMarkerRangeForElement(psw);
+        var start = psw.startTextMarkerForTextMarkerRange(textMarkerRange3);
+        shouldBeTrue(&quot;!psw.accessibilityElementForTextMarker(start)&quot;);
+        
+        // Check next/previous text marker call will skip password field
+        // We start from text2 and advance 6 characters, it should skip the password field and land on text3.
+        currentMarker = text2.startTextMarkerForTextMarkerRange(textMarkerRange2);
+        for (var i = 0; i &lt; 6; i++) {
+            currentMarker = text2.nextTextMarker(currentMarker);
+        }
+        shouldBeTrue(&quot;text2.accessibilityElementForTextMarker(currentMarker).isEqual(text3)&quot;);
+        // Check previous text marker, it should land on &quot; d&quot; node.
+        currentMarker = text2.previousTextMarker(currentMarker);
+        shouldBeTrue(&quot;text2.accessibilityElementForTextMarker(currentMarker).isEqual(text2.childAtIndex(2))&quot;);
+        
+    }
+
+&lt;/script&gt;
+
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkLayoutTestsaccessibilitymactextmarkerwithuserselectnoneexpectedtxt"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/accessibility/mac/text-marker-with-user-select-none-expected.txt (0 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/accessibility/mac/text-marker-with-user-select-none-expected.txt                                (rev 0)
+++ trunk/LayoutTests/accessibility/mac/text-marker-with-user-select-none-expected.txt        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -0,0 +1,16 @@
</span><ins>+hello test world test hello
+link to here
+test
+This tests that accessibility text markers still work even when user-select:none is set.
+
+On success, you will see a series of &quot;PASS&quot; messages, followed by &quot;TEST COMPLETE&quot;.
+
+
+PASS textElement.textMarkerRangeLength(textMarkerRange) is 45
+PASS text is &quot;hello test world test hello\nlink to here\ntest&quot;
+PASS text is 'h'
+PASS element.isEqual(textElement.childAtIndex(0)) is true
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
</ins></span></pre></div>
<a id="trunkLayoutTestsaccessibilitymactextmarkerwithuserselectnonehtml"></a>
<div class="addfile"><h4>Added: trunk/LayoutTests/accessibility/mac/text-marker-with-user-select-none.html (0 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/accessibility/mac/text-marker-with-user-select-none.html                                (rev 0)
+++ trunk/LayoutTests/accessibility/mac/text-marker-with-user-select-none.html        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -0,0 +1,49 @@
</span><ins>+&lt;!DOCTYPE HTML PUBLIC &quot;-//IETF//DTD HTML//EN&quot;&gt;
+&lt;html&gt;
+&lt;head&gt;
+&lt;script src=&quot;../../resources/js-test-pre.js&quot;&gt;&lt;/script&gt;
+&lt;/head&gt;
+&lt;body id=&quot;body&quot;&gt;
+
+&lt;div id=&quot;text&quot; style=&quot;-webkit-user-select:none&quot;&gt;
+
+hello test &lt;b&gt;world&lt;/b&gt; test hello&lt;br&gt;
+&lt;a href=&quot;#&quot;&gt;link&lt;/a&gt; to &lt;a href=&quot;#&quot;&gt;here&lt;/a&gt;&lt;br&gt;
+test
+
+&lt;/div&gt;
+
+
+
+&lt;p id=&quot;description&quot;&gt;&lt;/p&gt;
+&lt;div id=&quot;console&quot;&gt;&lt;/div&gt;
+
+&lt;script&gt;
+
+    description(&quot;This tests that accessibility text markers still work even when user-select:none is set.&quot;);
+
+    if (window.accessibilityController) {
+
+          var textElement = accessibilityController.accessibleElementById(&quot;text&quot;);
+          var textMarkerRange = textElement.textMarkerRangeForElement(textElement);
+          shouldBe(&quot;textElement.textMarkerRangeLength(textMarkerRange)&quot;, &quot;45&quot;);
+
+          var startMarker = textElement.startTextMarkerForTextMarkerRange(textMarkerRange);
+          var endMarker = textElement.endTextMarkerForTextMarkerRange(textMarkerRange);
+          textMarkerRange = textElement.textMarkerRangeForMarkers(startMarker, endMarker);
+          var text = textElement.stringForTextMarkerRange(textMarkerRange);
+          shouldBeEqualToString(&quot;text&quot;, &quot;hello test world test hello\nlink to here\ntest&quot;);
+
+          var nextMarker = textElement.nextTextMarker(startMarker);
+          textMarkerRange = textElement.textMarkerRangeForMarkers(startMarker, nextMarker);
+          text = textElement.stringForTextMarkerRange(textMarkerRange);
+          shouldBe(&quot;text&quot;, &quot;'h'&quot;);
+          var element = textElement.accessibilityElementForTextMarker(nextMarker);
+          shouldBeTrue(&quot;element.isEqual(textElement.childAtIndex(0))&quot;);
+    }
+
+&lt;/script&gt;
+
+&lt;script src=&quot;../../resources/js-test-post.js&quot;&gt;&lt;/script&gt;
+&lt;/body&gt;
+&lt;/html&gt;
</ins><span class="cx">\ No newline at end of file
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/Source/WebCore/ChangeLog        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -1,3 +1,77 @@
</span><ins>+2016-01-18  Nan Wang  &lt;n_wang@apple.com&gt;
+
+        AX: [Mac] Implement next/previous text marker functions using TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=152728
+
+        Reviewed by Chris Fleizach.
+
+        The existing AXTextMarker based calls are implemented using visible position, and that introduced
+        some bugs which make VoiceOver working incorrectly on Mac sometimes. Since TextIterator uses rendering
+        position, we tried to use it to refactor those AXTextMarker based calls.
+        In this patch, I implemented functions to navigate to previous/next text marker using Range and TextIterator.
+        Also added a conversion between visible position and character offset to make sure unconverted text marker
+        related functions are still working correctly.
+
+        Tests: accessibility/mac/previous-next-text-marker.html
+               accessibility/mac/text-marker-with-user-select-none.html
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::visiblePositionForTextMarkerData):
+        (WebCore::AXObjectCache::traverseToOffsetInRange):
+        (WebCore::AXObjectCache::lengthForRange):
+        (WebCore::AXObjectCache::rangeForNodeContents):
+        (WebCore::characterOffsetsInOrder):
+        (WebCore::AXObjectCache::rangeForUnorderedCharacterOffsets):
+        (WebCore::AXObjectCache::setTextMarkerDataWithCharacterOffset):
+        (WebCore::AXObjectCache::startOrEndTextMarkerDataForRange):
+        (WebCore::AXObjectCache::textMarkerDataForCharacterOffset):
+        (WebCore::AXObjectCache::nextNode):
+        (WebCore::AXObjectCache::previousNode):
+        (WebCore::AXObjectCache::visiblePositionFromCharacterOffset):
+        (WebCore::AXObjectCache::characterOffsetFromVisiblePosition):
+        (WebCore::AXObjectCache::accessibilityObjectForTextMarkerData):
+        (WebCore::AXObjectCache::textMarkerDataForVisiblePosition):
+        * accessibility/AXObjectCache.h:
+        (WebCore::CharacterOffset::CharacterOffset):
+        (WebCore::CharacterOffset::remaining):
+        (WebCore::CharacterOffset::isNull):
+        (WebCore::AXObjectCache::setNodeInUse):
+        (WebCore::AXObjectCache::removeNodeForUse):
+        (WebCore::AXObjectCache::isNodeInUse):
+        * accessibility/AccessibilityObject.cpp:
+        (WebCore::AccessibilityObject::selectionRange):
+        (WebCore::AccessibilityObject::elementRange):
+        (WebCore::AccessibilityObject::selectText):
+        (WebCore::AccessibilityObject::lineRangeForPosition):
+        (WebCore::AccessibilityObject::replacedNodeNeedsCharacter):
+        (WebCore::renderListItemContainerForNode):
+        (WebCore::listMarkerTextForNode):
+        (WebCore::AccessibilityObject::listMarkerTextForNodeAndPosition):
+        (WebCore::AccessibilityObject::stringForRange):
+        (WebCore::AccessibilityObject::stringForVisiblePositionRange):
+        (WebCore::replacedNodeNeedsCharacter): Deleted.
+        * accessibility/AccessibilityObject.h:
+        (WebCore::AccessibilityObject::visiblePositionRange):
+        (WebCore::AccessibilityObject::visiblePositionRangeForLine):
+        (WebCore::AccessibilityObject::boundsForVisiblePositionRange):
+        (WebCore::AccessibilityObject::setSelectedVisiblePositionRange):
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
+        (isTextMarkerIgnored):
+        (-[WebAccessibilityObjectWrapper accessibilityObjectForTextMarker:]):
+        (accessibilityObjectForTextMarker):
+        (-[WebAccessibilityObjectWrapper textMarkerRangeFromRange:]):
+        (textMarkerRangeFromRange):
+        (-[WebAccessibilityObjectWrapper startOrEndTextMarkerForRange:isStart:]):
+        (startOrEndTextmarkerForRange):
+        (-[WebAccessibilityObjectWrapper nextTextMarkerForNode:offset:]):
+        (-[WebAccessibilityObjectWrapper previousTextMarkerForNode:offset:]):
+        (-[WebAccessibilityObjectWrapper textMarkerForNode:offset:]):
+        (textMarkerForCharacterOffset):
+        (-[WebAccessibilityObjectWrapper rangeForTextMarkerRange:]):
+        (-[WebAccessibilityObjectWrapper characterOffsetForTextMarker:]):
+        (textMarkerForVisiblePosition):
+        (-[WebAccessibilityObjectWrapper accessibilityAttributeValue:forParameter:]):
+
</ins><span class="cx"> 2016-01-18  Olivier Blin  &lt;olivier.blin@softathome.com&gt;
</span><span class="cx"> 
</span><span class="cx">         [Mac] Remove unused playerToPrivateMap()
</span></span></pre></div>
<a id="trunkSourceWebCoreaccessibilityAXObjectCachecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/accessibility/AXObjectCache.cpp (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/accessibility/AXObjectCache.cpp        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.cpp        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -81,6 +81,7 @@
</span><span class="cx"> #include &quot;RenderTableRow.h&quot;
</span><span class="cx"> #include &quot;RenderView.h&quot;
</span><span class="cx"> #include &quot;ScrollView.h&quot;
</span><ins>+#include &quot;TextIterator.h&quot;
</ins><span class="cx"> #include &lt;wtf/DataLog.h&gt;
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(VIDEO)
</span><span class="lines">@@ -1418,6 +1419,332 @@
</span><span class="cx">     return visiblePos;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+CharacterOffset AXObjectCache::traverseToOffsetInRange(RefPtr&lt;Range&gt;range, int offset, bool toNodeEnd, bool stayWithinRange)
+{
+    if (!range)
+        return CharacterOffset();
+    
+    int offsetInCharacter = 0;
+    int offsetSoFar = 0;
+    int remaining = 0;
+    int lastLength = 0;
+    Node* currentNode = nullptr;
+    bool finished = false;
+    int lastStartOffset = 0;
+    
+    TextIterator iterator(range.get());
+    
+    // When the range has zero length, there might be replaced node or brTag that we need to increment the characterOffset.
+    if (iterator.atEnd()) {
+        currentNode = &amp;range-&gt;startContainer();
+        lastStartOffset = range-&gt;startOffset();
+        if (offset &gt; 0 || toNodeEnd) {
+            if (AccessibilityObject::replacedNodeNeedsCharacter(currentNode) || (currentNode-&gt;renderer() &amp;&amp; currentNode-&gt;renderer()-&gt;isBR()))
+                offsetSoFar++;
+            lastLength = offsetSoFar;
+            
+            // When going backwards, stayWithinRange is false.
+            // Here when we don't have any character to move and we are going backwards, we traverse to the previous node.
+            if (!lastLength &amp;&amp; toNodeEnd &amp;&amp; !stayWithinRange) {
+                if (Node* preNode = previousNode(currentNode))
+                    return traverseToOffsetInRange(rangeForNodeContents(preNode), offset, toNodeEnd);
+                return CharacterOffset();
+            }
+        }
+    }
+    
+    for (; !iterator.atEnd(); iterator.advance()) {
+        int currentLength = iterator.text().length();
+        
+        Node&amp; node = iterator.range()-&gt;startContainer();
+        currentNode = &amp;node;
+        // When currentLength == 0, we check if there's any replaced node.
+        // If not, we skip the node with no length.
+        if (!currentLength) {
+            int subOffset = iterator.range()-&gt;startOffset();
+            Node* childNode = node.traverseToChildAt(subOffset);
+            if (AccessibilityObject::replacedNodeNeedsCharacter(childNode)) {
+                offsetSoFar++;
+                currentLength++;
+                currentNode = childNode;
+            } else
+                continue;
+        } else {
+            // Ignore space, new line, tag node.
+            if (currentLength == 1 &amp;&amp; isSpaceOrNewline(iterator.text()[0]))
+                continue;
+            offsetSoFar += currentLength;
+        }
+
+        lastLength = currentLength;
+        lastStartOffset = iterator.range()-&gt;startOffset();
+        
+        // Break early if we have advanced enough characters.
+        if (!toNodeEnd &amp;&amp; offsetSoFar &gt;= offset) {
+            offsetInCharacter = offset - (offsetSoFar - currentLength);
+            finished = true;
+            break;
+        }
+    }
+    
+    if (!finished) {
+        offsetInCharacter = lastLength;
+        if (!toNodeEnd)
+            remaining = offset - offsetSoFar;
+    }
+    
+    return CharacterOffset(currentNode, lastStartOffset, offsetInCharacter, remaining);
+}
+
+int AXObjectCache::lengthForRange(Range* range)
+{
+    if (!range)
+        return -1;
+    
+    int length = 0;
+    for (TextIterator it(range); !it.atEnd(); it.advance()) {
+        // non-zero length means textual node, zero length means replaced node (AKA &quot;attachments&quot; in AX)
+        if (it.text().length())
+            length += it.text().length();
+        else {
+            // locate the node and starting offset for this replaced range
+            Node&amp; node = it.range()-&gt;startContainer();
+            int offset = it.range()-&gt;startOffset();
+            if (AccessibilityObject::replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
+                ++length;
+        }
+    }
+        
+    return length;
+}
+
+RefPtr&lt;Range&gt; AXObjectCache::rangeForNodeContents(Node* node)
+{
+    if (!node)
+        return nullptr;
+    
+    Document* document = &amp;node-&gt;document();
+    if (!document)
+        return nullptr;
+    RefPtr&lt;Range&gt; range = Range::create(*document);
+    ExceptionCode ec = 0;
+    range-&gt;selectNodeContents(node, ec);
+    return ec ? nullptr : range;
+}
+
+static bool characterOffsetsInOrder(const CharacterOffset&amp; characterOffset1, const CharacterOffset&amp; characterOffset2)
+{
+    if (characterOffset1.isNull() || characterOffset2.isNull())
+        return false;
+    
+    if (characterOffset1.node == characterOffset2.node)
+        return characterOffset1.offset &lt;= characterOffset2.offset;
+    
+    RefPtr&lt;Range&gt; range1 = AXObjectCache::rangeForNodeContents(characterOffset1.node);
+    RefPtr&lt;Range&gt; range2 = AXObjectCache::rangeForNodeContents(characterOffset2.node);
+    return range1-&gt;compareBoundaryPoints(Range::START_TO_START, range2.get(), IGNORE_EXCEPTION) &lt;= 0;
+}
+
+RefPtr&lt;Range&gt; AXObjectCache::rangeForUnorderedCharacterOffsets(const CharacterOffset&amp; characterOffset1, const CharacterOffset&amp; characterOffset2)
+{
+    if (characterOffset1.isNull() || characterOffset2.isNull())
+        return nullptr;
+    
+    bool alreadyInOrder = characterOffsetsInOrder(characterOffset1, characterOffset2);
+    CharacterOffset startCharacterOffset = alreadyInOrder ? characterOffset1 : characterOffset2;
+    CharacterOffset endCharacterOffset = alreadyInOrder ? characterOffset2 : characterOffset1;
+    
+    int endOffset = endCharacterOffset.offset;
+    
+    // endOffset can be out of bounds sometimes if the node is a replaced node or has brTag.
+    if (startCharacterOffset.node == endCharacterOffset.node) {
+        RefPtr&lt;Range&gt; nodeRange = AXObjectCache::rangeForNodeContents(startCharacterOffset.node);
+        int nodeLength = TextIterator::rangeLength(nodeRange.get());
+        if (endOffset &gt; nodeLength)
+            endOffset = nodeLength;
+    }
+    
+    int startOffset = startCharacterOffset.startIndex + startCharacterOffset.offset;
+    endOffset = endCharacterOffset.startIndex + endOffset;
+    
+    // If start node is a replaced node and it has children, we want to include the replaced node itself in the range.
+    Node* startNode = startCharacterOffset.node;
+    if (AccessibilityObject::replacedNodeNeedsCharacter(startNode) &amp;&amp; (startNode-&gt;hasChildNodes() || startNode != endCharacterOffset.node)) {
+        startOffset = startNode-&gt;computeNodeIndex();
+        startNode = startNode-&gt;parentNode();
+    }
+    
+    RefPtr&lt;Range&gt; result = Range::create(m_document);
+    ExceptionCode ecStart = 0, ecEnd = 0;
+    result-&gt;setStart(startNode, startOffset, ecStart);
+    result-&gt;setEnd(endCharacterOffset.node, endOffset, ecEnd);
+    if (ecStart || ecEnd)
+        return nullptr;
+    
+    return result;
+}
+
+void AXObjectCache::setTextMarkerDataWithCharacterOffset(TextMarkerData&amp; textMarkerData, const CharacterOffset&amp; characterOffset)
+{
+    if (characterOffset.isNull())
+        return;
+    
+    Node* domNode = characterOffset.node;
+    if (is&lt;HTMLInputElement&gt;(*domNode) &amp;&amp; downcast&lt;HTMLInputElement&gt;(*domNode).isPasswordField()) {
+        textMarkerData.ignored = true;
+        return;
+    }
+    
+    RefPtr&lt;AccessibilityObject&gt; obj = this-&gt;getOrCreate(domNode);
+    
+    // Convert to visible position.
+    VisiblePosition visiblePosition = visiblePositionFromCharacterOffset(obj.get(), characterOffset);
+    int vpOffset = 0;
+    if (!visiblePosition.isNull()) {
+        Position deepPos = visiblePosition.deepEquivalent();
+        vpOffset = deepPos.deprecatedEditingOffset();
+    }
+    
+    textMarkerData.axID = obj.get()-&gt;axObjectID();
+    textMarkerData.node = domNode;
+    textMarkerData.characterOffset = characterOffset.offset;
+    textMarkerData.characterStartIndex = characterOffset.startIndex;
+    textMarkerData.offset = vpOffset;
+    textMarkerData.affinity = visiblePosition.affinity();
+    
+    this-&gt;setNodeInUse(domNode);
+}
+
+void AXObjectCache::startOrEndTextMarkerDataForRange(TextMarkerData&amp; textMarkerData, RefPtr&lt;Range&gt; range, bool isStart)
+{
+    memset(&amp;textMarkerData, 0, sizeof(TextMarkerData));
+    
+    if (!range)
+        return;
+    
+    // If it's end text marker, we want to go to the end of the range, and stay within the range.
+    bool stayWithinRange = !isStart;
+    
+    // Change the start of the range, so the character offset starts from node beginning.
+    int offset = 0;
+    Node* node = &amp;range-&gt;startContainer();
+    if (node-&gt;offsetInCharacters()) {
+        CharacterOffset nodeStartOffset = traverseToOffsetInRange(rangeForNodeContents(node), 0, false);
+        offset = std::max(range-&gt;startOffset() - nodeStartOffset.startIndex, 0);
+        range-&gt;setStart(node, nodeStartOffset.startIndex);
+    }
+    
+    CharacterOffset characterOffset = traverseToOffsetInRange(range, offset, !isStart, stayWithinRange);
+    setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset);
+}
+
+void AXObjectCache::textMarkerDataForCharacterOffset(TextMarkerData&amp; textMarkerData, Node&amp; node, int offset, bool toNodeEnd)
+{
+    memset(&amp;textMarkerData, 0, sizeof(TextMarkerData));
+    
+    Node* domNode = &amp;node;
+    if (!domNode)
+        return;
+    
+    // If offset &lt;= 0, means we want to go to the previous node.
+    if (offset &lt;= 0 &amp;&amp; !toNodeEnd) {
+        // Set the offset to the amount of characters we need to go backwards.
+        offset = - offset + 1;
+        while (offset &gt; 0 &amp;&amp; textMarkerData.characterOffset &lt;= offset) {
+            offset -= textMarkerData.characterOffset;
+            domNode = previousNode(domNode);
+            if (domNode) {
+                textMarkerDataForCharacterOffset(textMarkerData, *domNode, 0, true);
+                offset--;
+            } else
+                return;
+        }
+        if (offset &gt; 0)
+            textMarkerDataForCharacterOffset(textMarkerData, *domNode, offset, false);
+        return;
+    }
+    
+    RefPtr&lt;Range&gt; range = rangeForNodeContents(domNode);
+
+    // Traverse the offset amount of characters forward and see if there's remaining offsets.
+    // Keep traversing to the next node when there's remaining offsets.
+    CharacterOffset characterOffset = traverseToOffsetInRange(range, offset, toNodeEnd);
+    while (!characterOffset.isNull() &amp;&amp; characterOffset.remaining() &amp;&amp; !toNodeEnd) {
+        domNode = nextNode(domNode);
+        if (!domNode)
+            return;
+        range = rangeForNodeContents(domNode);
+        characterOffset = traverseToOffsetInRange(range, characterOffset.remaining(), toNodeEnd);
+    }
+    
+    setTextMarkerDataWithCharacterOffset(textMarkerData, characterOffset);
+}
+
+Node* AXObjectCache::nextNode(Node* node) const
+{
+    if (!node)
+        return nullptr;
+    
+    return NodeTraversal::nextSkippingChildren(*node);
+}
+
+Node* AXObjectCache::previousNode(Node* node) const
+{
+    if (!node)
+        return nullptr;
+    
+    // First child of body shouldn't have previous node.
+    if (node-&gt;parentNode() &amp;&amp; node-&gt;parentNode()-&gt;renderer() &amp;&amp; node-&gt;parentNode()-&gt;renderer()-&gt;isBody() &amp;&amp; !node-&gt;previousSibling())
+        return nullptr;
+
+    return NodeTraversal::previousSkippingChildren(*node);
+}
+
+VisiblePosition AXObjectCache::visiblePositionFromCharacterOffset(AccessibilityObject* obj, const CharacterOffset&amp; characterOffset)
+{
+    if (!obj)
+        return VisiblePosition();
+    
+    // nextVisiblePosition means advancing one character. Use this to calculate the character offset.
+    VisiblePositionRange vpRange = obj-&gt;visiblePositionRange();
+    VisiblePosition start = vpRange.start;
+    VisiblePosition result = start;
+    for (int i = 0; i &lt; characterOffset.offset; i++)
+        result = obj-&gt;nextVisiblePosition(result);
+    
+    return result;
+}
+
+CharacterOffset AXObjectCache::characterOffsetFromVisiblePosition(AccessibilityObject* obj, const VisiblePosition&amp; visiblePos)
+{
+    if (!obj)
+        return 0;
+    
+    // Use nextVisiblePosition to calculate how many characters we need to traverse to the current position.
+    Position deepPos = visiblePos.deepEquivalent();
+    VisiblePositionRange vpRange = obj-&gt;visiblePositionRange();
+    VisiblePosition vp = vpRange.start;
+    int characterOffset = 0;
+    Position vpDeepPos = vp.deepEquivalent();
+    
+    while (!vpDeepPos.isNull() &amp;&amp; !deepPos.equals(vpDeepPos)) {
+        vp = obj-&gt;nextVisiblePosition(vp);
+        vpDeepPos = vp.deepEquivalent();
+        characterOffset++;
+    }
+    
+    return traverseToOffsetInRange(rangeForNodeContents(obj-&gt;node()), characterOffset, false);
+}
+
+AccessibilityObject* AXObjectCache::accessibilityObjectForTextMarkerData(TextMarkerData&amp; textMarkerData)
+{
+    if (!isNodeInUse(textMarkerData.node))
+        return nullptr;
+    
+    Node* domNode = textMarkerData.node;
+    return this-&gt;getOrCreate(domNode);
+}
+
</ins><span class="cx"> void AXObjectCache::textMarkerDataForVisiblePosition(TextMarkerData&amp; textMarkerData, const VisiblePosition&amp; visiblePos)
</span><span class="cx"> {
</span><span class="cx">     // This memory must be bzero'd so instances of TextMarkerData can be tested for byte-equivalence.
</span><span class="lines">@@ -1443,8 +1770,13 @@
</span><span class="cx">     textMarkerData.axID = obj.get()-&gt;axObjectID();
</span><span class="cx">     textMarkerData.node = domNode;
</span><span class="cx">     textMarkerData.offset = deepPos.deprecatedEditingOffset();
</span><del>-    textMarkerData.affinity = visiblePos.affinity();   
</del><ins>+    textMarkerData.affinity = visiblePos.affinity();
</ins><span class="cx">     
</span><ins>+    // convert to character offset
+    CharacterOffset characterOffset = characterOffsetFromVisiblePosition(obj.get(), visiblePos);
+    textMarkerData.characterOffset = characterOffset.offset;
+    textMarkerData.characterStartIndex = characterOffset.startIndex;
+    
</ins><span class="cx">     cache-&gt;setNodeInUse(domNode);
</span><span class="cx"> }
</span><span class="cx"> 
</span></span></pre></div>
<a id="trunkSourceWebCoreaccessibilityAXObjectCacheh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/accessibility/AXObjectCache.h (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/accessibility/AXObjectCache.h        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.h        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -51,9 +51,29 @@
</span><span class="cx">     AXID axID;
</span><span class="cx">     Node* node;
</span><span class="cx">     int offset;
</span><ins>+    int characterStartIndex;
+    int characterOffset;
+    bool ignored;
</ins><span class="cx">     EAffinity affinity;
</span><span class="cx"> };
</span><span class="cx"> 
</span><ins>+struct CharacterOffset {
+    Node* node;
+    int startIndex;
+    int offset;
+    int remainingOffset;
+    
+    CharacterOffset(Node* n = nullptr, int startIndex = 0, int offset = 0, int remaining = 0)
+        : node(n)
+        , startIndex(startIndex)
+        , offset(offset)
+        , remainingOffset(remaining)
+    { }
+    
+    int remaining() const { return remainingOffset; }
+    bool isNull() const { return !node; }
+};
+
</ins><span class="cx"> class AXComputedObjectAttributeCache {
</span><span class="cx"> public:
</span><span class="cx">     AccessibilityObjectInclusion getIgnored(AXID) const;
</span><span class="lines">@@ -164,6 +184,12 @@
</span><span class="cx">     // Text marker utilities.
</span><span class="cx">     void textMarkerDataForVisiblePosition(TextMarkerData&amp;, const VisiblePosition&amp;);
</span><span class="cx">     VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&amp;);
</span><ins>+    void textMarkerDataForCharacterOffset(TextMarkerData&amp;, Node&amp;, int, bool toNodeEnd = false);
+    void startOrEndTextMarkerDataForRange(TextMarkerData&amp;, RefPtr&lt;Range&gt;, bool);
+    AccessibilityObject* accessibilityObjectForTextMarkerData(TextMarkerData&amp;);
+    RefPtr&lt;Range&gt; rangeForUnorderedCharacterOffsets(const CharacterOffset&amp;, const CharacterOffset&amp;);
+    static RefPtr&lt;Range&gt; rangeForNodeContents(Node*);
+    static int lengthForRange(Range*);
</ins><span class="cx"> 
</span><span class="cx">     enum AXNotification {
</span><span class="cx">         AXActiveDescendantChanged,
</span><span class="lines">@@ -254,6 +280,13 @@
</span><span class="cx">     void setNodeInUse(Node* n) { m_textMarkerNodes.add(n); }
</span><span class="cx">     void removeNodeForUse(Node* n) { m_textMarkerNodes.remove(n); }
</span><span class="cx">     bool isNodeInUse(Node* n) { return m_textMarkerNodes.contains(n); }
</span><ins>+    
+    Node* nextNode(Node*) const;
+    Node* previousNode(Node*) const;
+    CharacterOffset traverseToOffsetInRange(RefPtr&lt;Range&gt;, int, bool, bool stayWithinRange = false);
+    VisiblePosition visiblePositionFromCharacterOffset(AccessibilityObject*, const CharacterOffset&amp;);
+    CharacterOffset characterOffsetFromVisiblePosition(AccessibilityObject*, const VisiblePosition&amp;);
+    void setTextMarkerDataWithCharacterOffset(TextMarkerData&amp;, const CharacterOffset&amp;);
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     AccessibilityObject* rootWebArea();
</span></span></pre></div>
<a id="trunkSourceWebCoreaccessibilityAccessibilityObjectcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.cpp (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/accessibility/AccessibilityObject.cpp        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.cpp        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -715,6 +715,11 @@
</span><span class="cx">     return Range::create(*frame-&gt;document());
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+RefPtr&lt;Range&gt; AccessibilityObject::elementRange() const
+{    
+    return AXObjectCache::rangeForNodeContents(node());
+}
+
</ins><span class="cx"> String AccessibilityObject::selectText(AccessibilitySelectTextCriteria* criteria)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(criteria);
</span><span class="lines">@@ -1203,7 +1208,7 @@
</span><span class="cx">     return VisiblePositionRange(startPosition, endPosition);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-static bool replacedNodeNeedsCharacter(Node* replacedNode)
</del><ins>+bool AccessibilityObject::replacedNodeNeedsCharacter(Node* replacedNode)
</ins><span class="cx"> {
</span><span class="cx">     // we should always be given a rendered node and a replaced node, but be safe
</span><span class="cx">     // replaced nodes are either attachments (widgets) or images
</span><span class="lines">@@ -1228,7 +1233,19 @@
</span><span class="cx">     }
</span><span class="cx">     return nullptr;
</span><span class="cx"> }
</span><ins>+
+static String listMarkerTextForNode(Node* node)
+{
+    RenderListItem* listItem = renderListItemContainerForNode(node);
+    if (!listItem)
+        return String();
</ins><span class="cx">     
</span><ins>+    // If this is in a list item, we need to manually add the text for the list marker
+    // because a RenderListMarker does not have a Node equivalent and thus does not appear
+    // when iterating text.
+    return listItem-&gt;markerTextWithSuffix();
+}
+
</ins><span class="cx"> // Returns the text associated with a list marker if this node is contained within a list item.
</span><span class="cx"> String AccessibilityObject::listMarkerTextForNodeAndPosition(Node* node, const VisiblePosition&amp; visiblePositionStart) const
</span><span class="cx"> {
</span><span class="lines">@@ -1236,14 +1253,36 @@
</span><span class="cx">     if (!isStartOfLine(visiblePositionStart))
</span><span class="cx">         return String();
</span><span class="cx"> 
</span><del>-    RenderListItem* listItem = renderListItemContainerForNode(node);
-    if (!listItem)
</del><ins>+    return listMarkerTextForNode(node);
+}
+
+String AccessibilityObject::stringForRange(RefPtr&lt;Range&gt; range) const
+{
+    if (!range)
</ins><span class="cx">         return String();
</span><del>-        
-    // If this is in a list item, we need to manually add the text for the list marker 
-    // because a RenderListMarker does not have a Node equivalent and thus does not appear
-    // when iterating text.
-    return listItem-&gt;markerTextWithSuffix();
</del><ins>+    
+    TextIterator it(range.get());
+    if (it.atEnd())
+        return String();
+    
+    StringBuilder builder;
+    for (; !it.atEnd(); it.advance()) {
+        // non-zero length means textual node, zero length means replaced node (AKA &quot;attachments&quot; in AX)
+        if (it.text().length()) {
+            // Add a textual representation for list marker text.
+            builder.append(listMarkerTextForNode(it.node()));
+            it.appendTextToStringBuilder(builder);
+        } else {
+            // locate the node and starting offset for this replaced range
+            Node&amp; node = it.range()-&gt;startContainer();
+            ASSERT(&amp;node == &amp;it.range()-&gt;endContainer());
+            int offset = it.range()-&gt;startOffset();
+            if (replacedNodeNeedsCharacter(node.traverseToChildAt(offset)))
+                builder.append(objectReplacementCharacter);
+        }
+    }
+    
+    return builder.toString();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> String AccessibilityObject::stringForVisiblePositionRange(const VisiblePositionRange&amp; visiblePositionRange) const
</span></span></pre></div>
<a id="trunkSourceWebCoreaccessibilityAccessibilityObjecth"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/accessibility/AccessibilityObject.h (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/accessibility/AccessibilityObject.h        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/Source/WebCore/accessibility/AccessibilityObject.h        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -817,6 +817,9 @@
</span><span class="cx">     virtual VisiblePositionRange visiblePositionRange() const { return VisiblePositionRange(); }
</span><span class="cx">     virtual VisiblePositionRange visiblePositionRangeForLine(unsigned) const { return VisiblePositionRange(); }
</span><span class="cx">     
</span><ins>+    RefPtr&lt;Range&gt; elementRange() const;
+    static bool replacedNodeNeedsCharacter(Node* replacedNode);
+    
</ins><span class="cx">     VisiblePositionRange visiblePositionRangeForUnorderedPositions(const VisiblePosition&amp;, const VisiblePosition&amp;) const;
</span><span class="cx">     VisiblePositionRange positionOfLeftWord(const VisiblePosition&amp;) const;
</span><span class="cx">     VisiblePositionRange positionOfRightWord(const VisiblePosition&amp;) const;
</span><span class="lines">@@ -829,6 +832,7 @@
</span><span class="cx">     VisiblePositionRange lineRangeForPosition(const VisiblePosition&amp;) const;
</span><span class="cx"> 
</span><span class="cx">     String stringForVisiblePositionRange(const VisiblePositionRange&amp;) const;
</span><ins>+    String stringForRange(RefPtr&lt;Range&gt;) const;
</ins><span class="cx">     virtual IntRect boundsForVisiblePositionRange(const VisiblePositionRange&amp;) const { return IntRect(); }
</span><span class="cx">     int lengthForVisiblePositionRange(const VisiblePositionRange&amp;) const;
</span><span class="cx">     virtual void setSelectedVisiblePositionRange(const VisiblePositionRange&amp;) const { }
</span></span></pre></div>
<a id="trunkSourceWebCoreaccessibilitymacWebAccessibilityObjectWrapperMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -782,6 +782,136 @@
</span><span class="cx"> 
</span><span class="cx"> #pragma mark Text Marker helpers
</span><span class="cx"> 
</span><ins>+static bool isTextMarkerIgnored(id textMarker)
+{
+    if (!textMarker)
+        return false;
+    
+    TextMarkerData textMarkerData;
+    if (!wkGetBytesFromAXTextMarker(textMarker, &amp;textMarkerData, sizeof(textMarkerData)))
+        return false;
+    
+    return textMarkerData.ignored;
+}
+
+- (AccessibilityObject*)accessibilityObjectForTextMarker:(id)textMarker
+{
+    return accessibilityObjectForTextMarker(m_object-&gt;axObjectCache(), textMarker);
+}
+
+static AccessibilityObject* accessibilityObjectForTextMarker(AXObjectCache* cache, id textMarker)
+{
+    if (!textMarker || !cache || isTextMarkerIgnored(textMarker))
+        return nullptr;
+    
+    TextMarkerData textMarkerData;
+    if (!wkGetBytesFromAXTextMarker(textMarker, &amp;textMarkerData, sizeof(textMarkerData)))
+        return nullptr;
+    return cache-&gt;accessibilityObjectForTextMarkerData(textMarkerData);
+}
+
+- (id)textMarkerRangeFromRange:(const RefPtr&lt;Range&gt;)range
+{
+    return textMarkerRangeFromRange(m_object-&gt;axObjectCache(), range);
+}
+
+static id textMarkerRangeFromRange(AXObjectCache *cache, const RefPtr&lt;Range&gt; range)
+{
+    id startTextMarker = startOrEndTextmarkerForRange(cache, range, true);
+    id endTextMarker = startOrEndTextmarkerForRange(cache, range, false);
+    return textMarkerRangeFromMarkers(startTextMarker, endTextMarker);
+}
+
+- (id)startOrEndTextMarkerForRange:(const RefPtr&lt;Range&gt;)range isStart:(BOOL)isStart
+{
+    return startOrEndTextmarkerForRange(m_object-&gt;axObjectCache(), range, isStart);
+}
+
+static id startOrEndTextmarkerForRange(AXObjectCache* cache, RefPtr&lt;Range&gt; range, bool isStart)
+{
+    if (!cache)
+        return nil;
+    
+    TextMarkerData textMarkerData;
+    cache-&gt;startOrEndTextMarkerDataForRange(textMarkerData, range, isStart);
+    if (!textMarkerData.axID)
+        return nil;
+    
+    return CFBridgingRelease(wkCreateAXTextMarker(&amp;textMarkerData, sizeof(textMarkerData)));
+}
+
+- (id)nextTextMarkerForNode:(Node&amp;)node offset:(int)offset
+{
+    int nextOffset = offset + 1;
+    id textMarker = [self textMarkerForNode:node offset:nextOffset];
+    if (isTextMarkerIgnored(textMarker))
+        textMarker = [self nextTextMarkerForNode:node offset:nextOffset];
+    return textMarker;
+}
+
+- (id)previousTextMarkerForNode:(Node&amp;)node offset:(int)offset
+{
+    int previousOffset = offset - 1;
+    id textMarker = [self textMarkerForNode:node offset:previousOffset];
+    if (isTextMarkerIgnored(textMarker))
+        textMarker = [self previousTextMarkerForNode:node offset:previousOffset];
+    return textMarker;
+}
+
+- (id)textMarkerForNode:(Node&amp;)node offset:(int)offset
+{
+    return textMarkerForCharacterOffset(m_object-&gt;axObjectCache(), node, offset);
+}
+
+static id textMarkerForCharacterOffset(AXObjectCache* cache, Node&amp; node, int offset, bool toNodeEnd = false)
+{
+    if (!cache)
+        return nil;
+    
+    Node* domNode = &amp;node;
+    if (!domNode)
+        return nil;
+    
+    TextMarkerData textMarkerData;
+    cache-&gt;textMarkerDataForCharacterOffset(textMarkerData, node, offset, toNodeEnd);
+    if (!textMarkerData.axID &amp;&amp; !textMarkerData.ignored)
+        return nil;
+    
+    return CFBridgingRelease(wkCreateAXTextMarker(&amp;textMarkerData, sizeof(textMarkerData)));
+}
+
+- (RefPtr&lt;Range&gt;)rangeForTextMarkerRange:(id)textMarkerRange
+{
+    if (!textMarkerRange)
+        return nullptr;
+    
+    id startTextMarker = AXTextMarkerRangeStart(textMarkerRange);
+    id endTextMarker = AXTextMarkerRangeEnd(textMarkerRange);
+    
+    if (!startTextMarker || !endTextMarker)
+        return nullptr;
+    
+    AXObjectCache* cache = m_object-&gt;axObjectCache();
+    if (!cache)
+        return nullptr;
+    
+    CharacterOffset startCharacterOffset = [self characterOffsetForTextMarker:startTextMarker];
+    CharacterOffset endCharacterOffset = [self characterOffsetForTextMarker:endTextMarker];
+    return cache-&gt;rangeForUnorderedCharacterOffsets(startCharacterOffset, endCharacterOffset);
+}
+
+- (CharacterOffset)characterOffsetForTextMarker:(id)textMarker
+{
+    if (!textMarker || isTextMarkerIgnored(textMarker))
+        return CharacterOffset();
+    
+    TextMarkerData textMarkerData;
+    if (!wkGetBytesFromAXTextMarker(textMarker, &amp;textMarkerData, sizeof(textMarkerData)))
+        return CharacterOffset();
+    
+    return CharacterOffset(textMarkerData.node, textMarkerData.characterStartIndex, textMarkerData.characterOffset);
+}
+
</ins><span class="cx"> static id textMarkerForVisiblePosition(AXObjectCache* cache, const VisiblePosition&amp; visiblePos)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(cache);
</span><span class="lines">@@ -3828,16 +3958,15 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXUIElementForTextMarker&quot;]) {
</span><del>-        VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
-        AccessibilityObject* axObject = m_object-&gt;accessibilityObjectForPosition(visiblePos);
</del><ins>+        AccessibilityObject* axObject = [self accessibilityObjectForTextMarker:textMarker];
</ins><span class="cx">         if (!axObject)
</span><span class="cx">             return nil;
</span><span class="cx">         return axObject-&gt;wrapper();
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXTextMarkerRangeForUIElement&quot;]) {
</span><del>-        VisiblePositionRange vpRange = uiElement.get()-&gt;visiblePositionRange();
-        return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
</del><ins>+        RefPtr&lt;Range&gt; range = uiElement.get()-&gt;elementRange();
+        return [self textMarkerRangeFromRange:range];
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXLineForTextMarker&quot;]) {
</span><span class="lines">@@ -3851,8 +3980,8 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXStringForTextMarkerRange&quot;]) {
</span><del>-        VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
-        return m_object-&gt;stringForVisiblePositionRange(visiblePosRange);
</del><ins>+        RefPtr&lt;Range&gt; range = [self rangeForTextMarkerRange:textMarkerRange];
+        return m_object-&gt;stringForRange(range);
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXTextMarkerForPosition&quot;]) {
</span><span class="lines">@@ -3895,20 +4024,23 @@
</span><span class="cx">         if (!AXObjectIsTextMarker(textMarker1) || !AXObjectIsTextMarker(textMarker2))
</span><span class="cx">             return nil;
</span><span class="cx">         
</span><del>-        VisiblePosition visiblePos1 = [self visiblePositionForTextMarker:(textMarker1)];
-        VisiblePosition visiblePos2 = [self visiblePositionForTextMarker:(textMarker2)];
-        VisiblePositionRange vpRange = m_object-&gt;visiblePositionRangeForUnorderedPositions(visiblePos1, visiblePos2);
-        return [self textMarkerRangeFromVisiblePositions:vpRange.start endPosition:vpRange.end];
</del><ins>+        AXObjectCache* cache = m_object-&gt;axObjectCache();
+        if (!cache)
+            return nil;
+        CharacterOffset characterOffset1 = [self characterOffsetForTextMarker:textMarker1];
+        CharacterOffset characterOffset2 = [self characterOffsetForTextMarker:textMarker2];
+        RefPtr&lt;Range&gt; range = cache-&gt;rangeForUnorderedCharacterOffsets(characterOffset1, characterOffset2);
+        return [self textMarkerRangeFromRange:range];
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXNextTextMarkerForTextMarker&quot;]) {
</span><del>-        VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
-        return [self textMarkerForVisiblePosition:m_object-&gt;nextVisiblePosition(visiblePos)];
</del><ins>+        CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker];
+        return [self nextTextMarkerForNode:*characterOffset.node offset:characterOffset.offset];
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXPreviousTextMarkerForTextMarker&quot;]) {
</span><del>-        VisiblePosition visiblePos = [self visiblePositionForTextMarker:(textMarker)];
-        return [self textMarkerForVisiblePosition:m_object-&gt;previousVisiblePosition(visiblePos)];
</del><ins>+        CharacterOffset characterOffset = [self characterOffsetForTextMarker:textMarker];
+        return [self previousTextMarkerForNode:*characterOffset.node offset:characterOffset.offset];
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXLeftWordTextMarkerRangeForTextMarker&quot;]) {
</span><span class="lines">@@ -3994,8 +4126,8 @@
</span><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXLengthForTextMarkerRange&quot;]) {
</span><del>-        VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
-        int length = m_object-&gt;lengthForVisiblePositionRange(visiblePosRange);
</del><ins>+        RefPtr&lt;Range&gt; range = [self rangeForTextMarkerRange:textMarkerRange];
+        int length = AXObjectCache::lengthForRange(range.get());
</ins><span class="cx">         if (length &lt; 0)
</span><span class="cx">             return nil;
</span><span class="cx">         return [NSNumber numberWithInt:length];
</span><span class="lines">@@ -4003,13 +4135,13 @@
</span><span class="cx">     
</span><span class="cx">     // Used only by DumpRenderTree (so far).
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXStartTextMarkerForTextMarkerRange&quot;]) {
</span><del>-        VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
-        return [self textMarkerForVisiblePosition:visiblePosRange.start];
</del><ins>+        RefPtr&lt;Range&gt; range = [self rangeForTextMarkerRange:textMarkerRange];
+        return [self startOrEndTextMarkerForRange:range isStart:YES];
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     if ([attribute isEqualToString:@&quot;AXEndTextMarkerForTextMarkerRange&quot;]) {
</span><del>-        VisiblePositionRange visiblePosRange = [self visiblePositionRangeForTextMarkerRange:textMarkerRange];
-        return [self textMarkerForVisiblePosition:visiblePosRange.end];
</del><ins>+        RefPtr&lt;Range&gt; range = [self rangeForTextMarkerRange:textMarkerRange];
+        return [self startOrEndTextMarkerForRange:range isStart:NO];
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx"> #if ENABLE(TREE_DEBUGGING)
</span></span></pre></div>
<a id="trunkToolsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Tools/ChangeLog (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/ChangeLog        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/Tools/ChangeLog        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -1,3 +1,13 @@
</span><ins>+2016-01-18  Nan Wang  &lt;n_wang@apple.com&gt;
+
+        AX: [Mac] Implement next/previous text marker functions using TextIterator
+        https://bugs.webkit.org/show_bug.cgi?id=152728
+
+        Reviewed by Chris Fleizach.
+
+        * WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm:
+        (WTR::AccessibilityUIElement::accessibilityElementForTextMarker):
+
</ins><span class="cx"> 2016-01-18  Csaba Osztrogonác  &lt;ossy@webkit.org&gt;
</span><span class="cx"> 
</span><span class="cx">         [cmake] Add testair to the build system
</span></span></pre></div>
<a id="trunkToolsWebKitTestRunnerInjectedBundlemacAccessibilityUIElementMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm (195239 => 195240)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm        2016-01-19 00:06:46 UTC (rev 195239)
+++ trunk/Tools/WebKitTestRunner/InjectedBundle/mac/AccessibilityUIElementMac.mm        2016-01-19 00:56:13 UTC (rev 195240)
</span><span class="lines">@@ -1801,7 +1801,8 @@
</span><span class="cx"> {
</span><span class="cx">     BEGIN_AX_OBJC_EXCEPTIONS
</span><span class="cx">     id uiElement = [m_element accessibilityAttributeValue:@&quot;AXUIElementForTextMarker&quot; forParameter:(id)marker-&gt;platformTextMarker()];
</span><del>-    return AccessibilityUIElement::create(uiElement);
</del><ins>+    if (uiElement)
+        return AccessibilityUIElement::create(uiElement);
</ins><span class="cx">     END_AX_OBJC_EXCEPTIONS
</span><span class="cx">     
</span><span class="cx">     return nullptr;
</span></span></pre>
</div>
</div>

</body>
</html>