<!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>[196824] 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/196824">196824</a></dd>
<dt>Author</dt> <dd>n_wang@apple.com</dd>
<dt>Date</dt> <dd>2016-02-19 10:58:31 -0800 (Fri, 19 Feb 2016)</dd>
</dl>

<h3>Log Message</h3>
<pre>AX: Inconsistency between CharacterOffset and VisiblePostition
https://bugs.webkit.org/show_bug.cgi?id=154431

Reviewed by Chris Fleizach.

Source/WebCore:

VoiceOver is not getting the correct text marker from VisiblePostition when
navigating using arrow keys. We should make the CharacterOffset behavior consistent
with VisiblePosition so that the conversion between the two won't create different
text markers.

Changes are covered in the modified tests.

* accessibility/AXObjectCache.cpp:
(WebCore::AXObjectCache::characterOffsetForTextMarkerData):
(WebCore::AXObjectCache::traverseToOffsetInRange):
(WebCore::AXObjectCache::startOrEndCharacterOffsetForRange):
(WebCore::AXObjectCache::startOrEndTextMarkerDataForRange):
(WebCore::AXObjectCache::characterOffsetForNodeAndOffset):
(WebCore::AXObjectCache::textMarkerDataForNextCharacterOffset):
(WebCore::AXObjectCache::textMarkerDataForPreviousCharacterOffset):
(WebCore::AXObjectCache::visiblePositionFromCharacterOffset):
(WebCore::AXObjectCache::characterOffsetFromVisiblePosition):
(WebCore::AXObjectCache::accessibilityObjectForTextMarkerData):
(WebCore::AXObjectCache::textMarkerDataForVisiblePosition):
(WebCore::AXObjectCache::nextCharacterOffset):
(WebCore::AXObjectCache::previousCharacterOffset):
(WebCore::AXObjectCache::startCharacterOffsetOfWord):
(WebCore::AXObjectCache::endCharacterOffsetOfWord):
(WebCore::AXObjectCache::previousWordStartCharacterOffset):
(WebCore::AXObjectCache::previousParagraphStartCharacterOffset):
(WebCore::AXObjectCache::previousSentenceStartCharacterOffset):
* accessibility/AXObjectCache.h:
* accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
(-[WebAccessibilityObjectWrapper doAXAttributedStringForTextMarkerRange:]):

LayoutTests:

* accessibility/mac/text-marker-word-nav-expected.txt:
* accessibility/mac/text-marker-word-nav.html:
* accessibility/text-marker/text-marker-previous-next.html:</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkLayoutTestsChangeLog">trunk/LayoutTests/ChangeLog</a></li>
<li><a href="#trunkLayoutTestsaccessibilitymactextmarkerwordnavexpectedtxt">trunk/LayoutTests/accessibility/mac/text-marker-word-nav-expected.txt</a></li>
<li><a href="#trunkLayoutTestsaccessibilitymactextmarkerwordnavhtml">trunk/LayoutTests/accessibility/mac/text-marker-word-nav.html</a></li>
<li><a href="#trunkLayoutTestsaccessibilitytextmarkertextmarkerpreviousnexthtml">trunk/LayoutTests/accessibility/text-marker/text-marker-previous-next.html</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="#trunkSourceWebCoreaccessibilitymacWebAccessibilityObjectWrapperMacmm">trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkLayoutTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/ChangeLog (196823 => 196824)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/ChangeLog        2016-02-19 18:06:32 UTC (rev 196823)
+++ trunk/LayoutTests/ChangeLog        2016-02-19 18:58:31 UTC (rev 196824)
</span><span class="lines">@@ -1,3 +1,14 @@
</span><ins>+2016-02-19  Nan Wang  &lt;n_wang@apple.com&gt;
+
+        AX: Inconsistency between CharacterOffset and VisiblePostition
+        https://bugs.webkit.org/show_bug.cgi?id=154431
+
+        Reviewed by Chris Fleizach.
+
+        * accessibility/mac/text-marker-word-nav-expected.txt:
+        * accessibility/mac/text-marker-word-nav.html:
+        * accessibility/text-marker/text-marker-previous-next.html:
+
</ins><span class="cx"> 2016-02-19  Ryan Haddad  &lt;ryanhaddad@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Rebaseline imported/w3c/web-platform-tests/html/dom/interfaces.html for ios-simulator after r196797
</span></span></pre></div>
<a id="trunkLayoutTestsaccessibilitymactextmarkerwordnavexpectedtxt"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/accessibility/mac/text-marker-word-nav-expected.txt (196823 => 196824)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/accessibility/mac/text-marker-word-nav-expected.txt        2016-02-19 18:06:32 UTC (rev 196823)
+++ trunk/LayoutTests/accessibility/mac/text-marker-word-nav-expected.txt        2016-02-19 18:58:31 UTC (rev 196824)
</span><span class="lines">@@ -19,6 +19,11 @@
</span><span class="cx"> Right word is: word1
</span><span class="cx"> Pre word start to next word end: word1
</span><span class="cx"> 
</span><ins>+Current character is: t
+Left word is: test
+Right word is: 
+Pre word start to next word end: test'line break'Thisislongword
+
</ins><span class="cx"> Current character is: T
</span><span class="cx"> Left word is: Thisislongword
</span><span class="cx"> Right word is: Thisislongword
</span><span class="lines">@@ -86,17 +91,17 @@
</span><span class="cx"> 
</span><span class="cx"> Current character is: s
</span><span class="cx"> Left word is: spaces
</span><del>-Right word is: 'line break'
</del><ins>+Right word is: 
</ins><span class="cx"> Pre word start to next word end: spaces'line break'
</span><span class="cx"> 
</span><span class="cx"> Current character is: e
</span><span class="cx"> Left word is: some
</span><del>-Right word is: 'line break'
</del><ins>+Right word is: 
</ins><span class="cx"> Pre word start to next word end: some'line break'
</span><span class="cx"> 
</span><span class="cx"> Current character is: 'line break'
</span><del>-Left word is: 'line break'
-Right word is: text
</del><ins>+Left word is: 
+Right word is: 
</ins><span class="cx"> Pre word start to next word end: 'line break'text
</span><span class="cx"> 
</span><span class="cx"> Current character is:  
</span></span></pre></div>
<a id="trunkLayoutTestsaccessibilitymactextmarkerwordnavhtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/accessibility/mac/text-marker-word-nav.html (196823 => 196824)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/accessibility/mac/text-marker-word-nav.html        2016-02-19 18:06:32 UTC (rev 196823)
+++ trunk/LayoutTests/accessibility/mac/text-marker-word-nav.html        2016-02-19 18:58:31 UTC (rev 196824)
</span><span class="lines">@@ -57,17 +57,20 @@
</span><span class="cx">         var startMarker = text.startTextMarkerForTextMarkerRange(textMarkerRange);
</span><span class="cx">         var currentMarker = advanceAndVerify(startMarker, 1, text);
</span><span class="cx">         
</span><ins>+        // Check that we are at the end of paragraph, so right word should be empty
+        currentMarker = advanceAndVerify(currentMarker, 9, text);
+        
</ins><span class="cx">         // Check the case with span
</span><span class="cx">         // At &quot;T&quot; in &quot;Thisis&quot;, should return the word as &quot;Thisislongword&quot;.
</span><del>-        currentMarker = advanceAndVerify(currentMarker, 10, text);
</del><ins>+        currentMarker = advanceAndVerify(currentMarker, 2, text);
</ins><span class="cx">         // At &quot; &quot; before &quot;I&quot;, the word should be &quot;I'll&quot;.
</span><del>-        currentMarker = advanceAndVerify(currentMarker, 14, text);
</del><ins>+        currentMarker = advanceAndVerify(currentMarker, 15, text);
</ins><span class="cx">         // At &quot; &quot; before &quot;try&quot;, the word should excludes &quot;.&quot;
</span><del>-        currentMarker = advanceAndVerify(currentMarker, 5, text);
</del><ins>+        currentMarker = advanceAndVerify(currentMarker, 6, text);
</ins><span class="cx">         
</span><span class="cx">         // Check the case with contenteditable
</span><span class="cx">         // At &quot;e&quot; in &quot;editable&quot;, the word should NOT include &quot;Content&quot; before it.
</span><del>-        currentMarker = advanceAndVerify(currentMarker, 17, text);
</del><ins>+        currentMarker = advanceAndVerify(currentMarker, 19, text);
</ins><span class="cx">         
</span><span class="cx">         // Check the case with replaced node, the replaced node should be considered a word.
</span><span class="cx">         var text2 = accessibilityController.accessibleElementById(&quot;text2&quot;);
</span></span></pre></div>
<a id="trunkLayoutTestsaccessibilitytextmarkertextmarkerpreviousnexthtml"></a>
<div class="modfile"><h4>Modified: trunk/LayoutTests/accessibility/text-marker/text-marker-previous-next.html (196823 => 196824)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/LayoutTests/accessibility/text-marker/text-marker-previous-next.html        2016-02-19 18:06:32 UTC (rev 196823)
+++ trunk/LayoutTests/accessibility/text-marker/text-marker-previous-next.html        2016-02-19 18:58:31 UTC (rev 196824)
</span><span class="lines">@@ -49,31 +49,32 @@
</span><span class="cx">         shouldBeTrue(&quot;text.accessibilityElementForTextMarker(endMarker).isEqual(text)&quot;);
</span><span class="cx">         
</span><span class="cx">         // Check next text marker. (Advance 5 characters, it will land at &lt;br&gt;.)
</span><del>-        var currentMarker = startMarker;
-        var previousMarker, markerRange;
-        for (var i = 0; i &lt; 5; i++) {
-            previousMarker = currentMarker;
-            currentMarker = text.nextTextMarker(currentMarker);
-        }
</del><ins>+        var currentMarker, previousMarker, markerRange;;
+        var result = forward(5, previousMarker, startMarker, text);
+        previousMarker = result.previous;
+        currentMarker = result.current;
</ins><span class="cx">         markerRange = text.textMarkerRangeForMarkers(previousMarker, currentMarker);
</span><span class="cx">         var newline = '\n';
</span><span class="cx">         shouldBe(&quot;text.stringForTextMarkerRange(markerRange)&quot;, &quot;newline&quot;);
</span><span class="cx">         
</span><del>-        // Advance one more character, it will lande at &quot;t&quot; in &quot;text1&quot;.
-        previousMarker = currentMarker;
-        currentMarker = text.nextTextMarker(currentMarker);
</del><ins>+        // Advance one more characters, it will lande at &quot;t&quot; in &quot;text1&quot;.
+        result = forward(1, previousMarker, currentMarker, text);
+        previousMarker = result.previous;
+        currentMarker = result.current;
</ins><span class="cx">         markerRange = text.textMarkerRangeForMarkers(previousMarker, currentMarker);
</span><span class="cx">         shouldBe(&quot;text.stringForTextMarkerRange(markerRange)&quot;, &quot;'t'&quot;);
</span><span class="cx">         
</span><span class="cx">         // Check previous text marker. (Traverse backwards one character, it will land at &lt;br&gt;.)
</span><del>-        previousMarker = text.previousTextMarker(previousMarker);
-        currentMarker = text.previousTextMarker(currentMarker);
</del><ins>+        result = backwards(1, previousMarker, currentMarker, text);
+        previousMarker = result.previous;
+        currentMarker = result.current;
</ins><span class="cx">         markerRange = text.textMarkerRangeForMarkers(previousMarker, currentMarker);
</span><span class="cx">         shouldBe(&quot;text.stringForTextMarkerRange(markerRange)&quot;, &quot;newline&quot;);
</span><span class="cx">         
</span><del>-        // Traverse backwards one more character, it will land at the last character of &quot;text&quot;.
-        previousMarker = text.previousTextMarker(previousMarker);
-        currentMarker = text.previousTextMarker(currentMarker);
</del><ins>+        // Traverse backwards two more character, it will land at the last character of &quot;text&quot;.
+        result = backwards(2, previousMarker, currentMarker, text);
+        previousMarker = result.previous;
+        currentMarker = result.current;
</ins><span class="cx">         markerRange = text.textMarkerRangeForMarkers(previousMarker, currentMarker);
</span><span class="cx">         shouldBe(&quot;text.stringForTextMarkerRange(markerRange)&quot;, &quot;'t'&quot;);
</span><span class="cx">         
</span><span class="lines">@@ -86,30 +87,29 @@
</span><span class="cx">         
</span><span class="cx">         currentMarker = text2.startTextMarkerForTextMarkerRange(textMarkerRange2);
</span><span class="cx">         // Advance 5 characters, it will land at &quot;d&quot;.
</span><del>-        for (var i = 0; i &lt; 5; i++) {
-            previousMarker = currentMarker;
-            currentMarker = text2.nextTextMarker(currentMarker);
-        }
</del><ins>+        result = forward(5, previousMarker, currentMarker, text2);
+        previousMarker = result.previous;
+        currentMarker = result.current;
</ins><span class="cx">         markerRange = text2.textMarkerRangeForMarkers(previousMarker, currentMarker);
</span><span class="cx">         shouldBe(&quot;text2.stringForTextMarkerRange(markerRange)&quot;, &quot;'d'&quot;);
</span><span class="cx">         
</span><del>-        // Traverse backwards 5 characters, it will land at the last character of &quot;text1&quot;.
-        for (var i = 0; i &lt; 5; i++) {
-            previousMarker = text2.previousTextMarker(previousMarker);
-            currentMarker = text2.previousTextMarker(currentMarker);
-        }
</del><ins>+        // Traverse backwards 8 characters, it will land at the last character of &quot;text1&quot;.
+        result = backwards(8, previousMarker, currentMarker, text2);
+        previousMarker = result.previous;
+        currentMarker = result.current;
</ins><span class="cx">         markerRange = text2.textMarkerRangeForMarkers(previousMarker, currentMarker);
</span><span class="cx">         shouldBe(&quot;text2.stringForTextMarkerRange(markerRange)&quot;, &quot;'1'&quot;);
</span><span class="cx">         
</span><span class="cx">         // Check the case with user-select:none, nextTextMarker/previousTextMarker should still work.
</span><span class="cx">         var text3 = accessibilityController.accessibleElementById(&quot;text3&quot;);
</span><span class="cx">         text3 = text3.childAtIndex(0);
</span><ins>+        
</ins><span class="cx">         // Advance to land at user-select:none node.
</span><span class="cx">         var marker1, marker2;
</span><del>-        for (var i = 0; i &lt; 17; i++) {
</del><ins>+        for (var i = 0; i &lt; 18; i++) {
</ins><span class="cx">             currentMarker = text3.nextTextMarker(currentMarker);
</span><del>-            // i == 13, it should land on &quot;e&quot;, and i == 16, it should land on &quot;t&quot;
-            if (i == 13) {
</del><ins>+            // i == 14, it should land on &quot;e&quot;, and i == 17, it should land on &quot;t&quot;
+            if (i == 14) {
</ins><span class="cx">                 marker1 = currentMarker;
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="lines">@@ -139,6 +139,7 @@
</span><span class="cx">         shouldBeTrue(&quot;text2.accessibilityElementForTextMarker(currentMarker).isEqual(text3)&quot;);
</span><span class="cx">         // Check previous text marker, it should land on &quot; d&quot; node.
</span><span class="cx">         currentMarker = text2.previousTextMarker(currentMarker);
</span><ins>+        currentMarker = text2.previousTextMarker(currentMarker);
</ins><span class="cx">         shouldBeTrue(&quot;text2.accessibilityElementForTextMarker(currentMarker).isEqual(text2.childAtIndex(2))&quot;);
</span><span class="cx">         
</span><span class="cx">         
</span><span class="lines">@@ -162,6 +163,27 @@
</span><span class="cx">         markerRange = text.textMarkerRangeForMarkers(startMarker, currentMarker)
</span><span class="cx">         shouldBe(&quot;text.stringForTextMarkerRange(markerRange)&quot;, &quot;'a'&quot;);
</span><span class="cx">         
</span><ins>+        function forward(count, previousMarker, currentMarker, obj) {
+            for (var i = 0; i &lt; count; i++) {
+                previousMarker = currentMarker;
+                currentMarker = obj.nextTextMarker(currentMarker);
+            }
+            return {
+                previous: previousMarker,
+                current: currentMarker
+            };
+        }
+        
+        function backwards(count, previousMarker, currentMarker, obj) {
+            for (var i = 0; i &lt; count; i++) {
+                previousMarker = obj.previousTextMarker(previousMarker);
+                currentMarker = obj.previousTextMarker(currentMarker);
+            }
+            return {
+                previous: previousMarker,
+                current: currentMarker
+            };
+        }
</ins><span class="cx">     }
</span><span class="cx"> 
</span><span class="cx"> &lt;/script&gt;
</span></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (196823 => 196824)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2016-02-19 18:06:32 UTC (rev 196823)
+++ trunk/Source/WebCore/ChangeLog        2016-02-19 18:58:31 UTC (rev 196824)
</span><span class="lines">@@ -1,3 +1,40 @@
</span><ins>+2016-02-19  Nan Wang  &lt;n_wang@apple.com&gt;
+
+        AX: Inconsistency between CharacterOffset and VisiblePostition
+        https://bugs.webkit.org/show_bug.cgi?id=154431
+
+        Reviewed by Chris Fleizach.
+
+        VoiceOver is not getting the correct text marker from VisiblePostition when
+        navigating using arrow keys. We should make the CharacterOffset behavior consistent
+        with VisiblePosition so that the conversion between the two won't create different
+        text markers.
+        
+        Changes are covered in the modified tests.
+
+        * accessibility/AXObjectCache.cpp:
+        (WebCore::AXObjectCache::characterOffsetForTextMarkerData):
+        (WebCore::AXObjectCache::traverseToOffsetInRange):
+        (WebCore::AXObjectCache::startOrEndCharacterOffsetForRange):
+        (WebCore::AXObjectCache::startOrEndTextMarkerDataForRange):
+        (WebCore::AXObjectCache::characterOffsetForNodeAndOffset):
+        (WebCore::AXObjectCache::textMarkerDataForNextCharacterOffset):
+        (WebCore::AXObjectCache::textMarkerDataForPreviousCharacterOffset):
+        (WebCore::AXObjectCache::visiblePositionFromCharacterOffset):
+        (WebCore::AXObjectCache::characterOffsetFromVisiblePosition):
+        (WebCore::AXObjectCache::accessibilityObjectForTextMarkerData):
+        (WebCore::AXObjectCache::textMarkerDataForVisiblePosition):
+        (WebCore::AXObjectCache::nextCharacterOffset):
+        (WebCore::AXObjectCache::previousCharacterOffset):
+        (WebCore::AXObjectCache::startCharacterOffsetOfWord):
+        (WebCore::AXObjectCache::endCharacterOffsetOfWord):
+        (WebCore::AXObjectCache::previousWordStartCharacterOffset):
+        (WebCore::AXObjectCache::previousParagraphStartCharacterOffset):
+        (WebCore::AXObjectCache::previousSentenceStartCharacterOffset):
+        * accessibility/AXObjectCache.h:
+        * accessibility/mac/WebAccessibilityObjectWrapperMac.mm:
+        (-[WebAccessibilityObjectWrapper doAXAttributedStringForTextMarkerRange:]):
+
</ins><span class="cx"> 2016-02-19  Jer Noble  &lt;jer.noble@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Allow CachedRawResource clients to opt out of caching on a per-response basis
</span></span></pre></div>
<a id="trunkSourceWebCoreaccessibilityAXObjectCachecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/accessibility/AXObjectCache.cpp (196823 => 196824)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/accessibility/AXObjectCache.cpp        2016-02-19 18:06:32 UTC (rev 196823)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.cpp        2016-02-19 18:58:31 UTC (rev 196824)
</span><span class="lines">@@ -1436,11 +1436,13 @@
</span><span class="cx">     return CharacterOffset(textMarkerData.node, textMarkerData.characterStartIndex, textMarkerData.characterOffset);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-CharacterOffset AXObjectCache::traverseToOffsetInRange(RefPtr&lt;Range&gt;range, int offset, bool toNodeEnd, bool stayWithinRange)
</del><ins>+CharacterOffset AXObjectCache::traverseToOffsetInRange(RefPtr&lt;Range&gt;range, int offset, TraverseOption option, bool stayWithinRange)
</ins><span class="cx"> {
</span><span class="cx">     if (!range)
</span><span class="cx">         return CharacterOffset();
</span><span class="cx">     
</span><ins>+    bool toNodeEnd = option &amp; TraverseOptionToNodeEnd;
+    
</ins><span class="cx">     int offsetInCharacter = 0;
</span><span class="cx">     int offsetSoFar = 0;
</span><span class="cx">     int remaining = 0;
</span><span class="lines">@@ -1464,7 +1466,7 @@
</span><span class="cx">             // Here when we don't have any character to move and we are going backwards, we traverse to the previous node.
</span><span class="cx">             if (!lastLength &amp;&amp; toNodeEnd &amp;&amp; !stayWithinRange) {
</span><span class="cx">                 if (Node* preNode = previousNode(currentNode))
</span><del>-                    return traverseToOffsetInRange(rangeForNodeContents(preNode), offset, toNodeEnd);
</del><ins>+                    return traverseToOffsetInRange(rangeForNodeContents(preNode), offset, option);
</ins><span class="cx">                 return CharacterOffset();
</span><span class="cx">             }
</span><span class="cx">         }
</span><span class="lines">@@ -1501,8 +1503,11 @@
</span><span class="cx">                     if (childNode &amp;&amp; childNode-&gt;renderer() &amp;&amp; childNode-&gt;renderer()-&gt;isBR()) {
</span><span class="cx">                         currentNode = childNode;
</span><span class="cx">                         hasReplacedNodeOrBR = true;
</span><del>-                    } else if (currentNode != previousNode)
</del><ins>+                    } else if (currentNode != previousNode) {
+                        // We should set the currentNode to previous one in case this is the last iteration.
+                        currentNode = previousNode;
</ins><span class="cx">                         continue;
</span><ins>+                    }
</ins><span class="cx">                 }
</span><span class="cx">             }
</span><span class="cx">             offsetSoFar += currentLength;
</span><span class="lines">@@ -1697,12 +1702,12 @@
</span><span class="cx">     Node* node = &amp;copyRange-&gt;startContainer();
</span><span class="cx">     if (node-&gt;offsetInCharacters()) {
</span><span class="cx">         copyRange = Range::create(range-&gt;ownerDocument(), &amp;range-&gt;startContainer(), range-&gt;startOffset(), &amp;range-&gt;endContainer(), range-&gt;endOffset());
</span><del>-        CharacterOffset nodeStartOffset = traverseToOffsetInRange(rangeForNodeContents(node), 0, false);
</del><ins>+        CharacterOffset nodeStartOffset = traverseToOffsetInRange(rangeForNodeContents(node), 0);
</ins><span class="cx">         offset = std::max(copyRange-&gt;startOffset() - nodeStartOffset.startIndex, 0);
</span><span class="cx">         copyRange-&gt;setStart(node, nodeStartOffset.startIndex);
</span><span class="cx">     }
</span><span class="cx">     
</span><del>-    return traverseToOffsetInRange(copyRange, offset, !isStart, stayWithinRange);
</del><ins>+    return traverseToOffsetInRange(copyRange, offset, isStart ? TraverseOptionDefault : TraverseOptionToNodeEnd, stayWithinRange);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void AXObjectCache::startOrEndTextMarkerDataForRange(TextMarkerData&amp; textMarkerData, RefPtr&lt;Range&gt; range, bool isStart)
</span><span class="lines">@@ -1750,13 +1755,13 @@
</span><span class="cx"> 
</span><span class="cx">     // Traverse the offset amount of characters forward and see if there's remaining offsets.
</span><span class="cx">     // Keep traversing to the next node when there's remaining offsets.
</span><del>-    CharacterOffset characterOffset = traverseToOffsetInRange(range, offset, toNodeEnd);
</del><ins>+    CharacterOffset characterOffset = traverseToOffsetInRange(range, offset, option);
</ins><span class="cx">     while (!characterOffset.isNull() &amp;&amp; characterOffset.remaining() &amp;&amp; !toNodeEnd) {
</span><span class="cx">         domNode = nextNode(domNode);
</span><span class="cx">         if (!domNode)
</span><span class="cx">             return CharacterOffset();
</span><span class="cx">         range = rangeForNodeContents(domNode);
</span><del>-        characterOffset = traverseToOffsetInRange(range, characterOffset.remaining(), toNodeEnd);
</del><ins>+        characterOffset = traverseToOffsetInRange(range, characterOffset.remaining(), option);
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     return characterOffset;
</span><span class="lines">@@ -1772,7 +1777,7 @@
</span><span class="cx"> {
</span><span class="cx">     CharacterOffset next = characterOffset;
</span><span class="cx">     do {
</span><del>-        next = nextCharacterOffset(next);
</del><ins>+        next = nextCharacterOffset(next, false);
</ins><span class="cx">         textMarkerDataForCharacterOffset(textMarkerData, next);
</span><span class="cx">     } while (textMarkerData.ignored);
</span><span class="cx"> }
</span><span class="lines">@@ -1781,7 +1786,7 @@
</span><span class="cx"> {
</span><span class="cx">     CharacterOffset previous = characterOffset;
</span><span class="cx">     do {
</span><del>-        previous = previousCharacterOffset(previous);
</del><ins>+        previous = previousCharacterOffset(previous, false);
</ins><span class="cx">         textMarkerDataForCharacterOffset(textMarkerData, previous);
</span><span class="cx">     } while (textMarkerData.ignored);
</span><span class="cx"> }
</span><span class="lines">@@ -1818,8 +1823,11 @@
</span><span class="cx">     // nextVisiblePosition means advancing one character. Use this to calculate the character offset.
</span><span class="cx">     VisiblePositionRange vpRange = obj-&gt;visiblePositionRange();
</span><span class="cx">     VisiblePosition start = vpRange.start;
</span><ins>+    
+    // Sometimes vpRange.start will be the previous node's end position and VisiblePosition will count the leading line break as 1 offset.
+    int characterCount = start.deepEquivalent().deprecatedNode() == characterOffset.node ? characterOffset.offset : characterOffset.offset + characterOffset.startIndex;
</ins><span class="cx">     VisiblePosition result = start;
</span><del>-    for (int i = 0; i &lt; characterOffset.offset; i++)
</del><ins>+    for (int i = 0; i &lt; characterCount; i++)
</ins><span class="cx">         result = obj-&gt;nextVisiblePosition(result);
</span><span class="cx">     
</span><span class="cx">     return result;
</span><span class="lines">@@ -1854,9 +1862,16 @@
</span><span class="cx">         if (vpDeepPos.equals(previousVisiblePos.deepEquivalent()))
</span><span class="cx">             break;
</span><span class="cx">         characterOffset++;
</span><ins>+        
+        // When VisiblePostion moves to next node, it will count the leading line break as
+        // 1 offset, which we shouldn't include in CharacterOffset.
+        if (vpDeepPos.deprecatedNode() != previousVisiblePos.deepEquivalent().deprecatedNode()) {
+            if (vp.characterBefore() == '\n')
+                characterOffset--;
+        }
</ins><span class="cx">     }
</span><span class="cx">     
</span><del>-    return traverseToOffsetInRange(rangeForNodeContents(obj-&gt;node()), characterOffset, false);
</del><ins>+    return traverseToOffsetInRange(rangeForNodeContents(obj-&gt;node()), characterOffset);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> AccessibilityObject* AXObjectCache::accessibilityObjectForTextMarkerData(TextMarkerData&amp; textMarkerData)
</span><span class="lines">@@ -1903,20 +1918,31 @@
</span><span class="cx">     cache-&gt;setNodeInUse(domNode);
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset&amp; characterOffset, bool ignoreStart)
</del><ins>+CharacterOffset AXObjectCache::nextCharacterOffset(const CharacterOffset&amp; characterOffset, bool ignoreNextNodeStart)
</ins><span class="cx"> {
</span><span class="cx">     if (characterOffset.isNull())
</span><span class="cx">         return CharacterOffset();
</span><span class="cx">     
</span><del>-    return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset + 1, ignoreStart ? TraverseOptionDefault : TraverseOptionIncludeStart);
</del><ins>+    CharacterOffset next = characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset + 1);
+    
+    // To be consistent with VisiblePosition, we should consider the case that current node end to next node start counts 1 offset.
+    bool isReplacedOrBR = isReplacedNodeOrBR(characterOffset.node) || isReplacedNodeOrBR(next.node);
+    if (!ignoreNextNodeStart &amp;&amp; !next.isNull() &amp;&amp; !isReplacedOrBR &amp;&amp; next.node != characterOffset.node)
+        next = characterOffsetForNodeAndOffset(*next.node, 0, TraverseOptionIncludeStart);
+    
+    return next;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-CharacterOffset AXObjectCache::previousCharacterOffset(const CharacterOffset&amp; characterOffset, bool ignoreStart)
</del><ins>+CharacterOffset AXObjectCache::previousCharacterOffset(const CharacterOffset&amp; characterOffset, bool ignorePreviousNodeEnd)
</ins><span class="cx"> {
</span><span class="cx">     if (characterOffset.isNull())
</span><span class="cx">         return CharacterOffset();
</span><span class="cx">     
</span><del>-    return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset - 1, ignoreStart ? TraverseOptionDefault : TraverseOptionIncludeStart);
</del><ins>+    // To be consistent with VisiblePosition, we should consider the case that current node start to previous node end counts 1 offset.
+    if (!ignorePreviousNodeEnd &amp;&amp; !characterOffset.offset)
+        return characterOffsetForNodeAndOffset(*characterOffset.node, 0);
+    
+    return characterOffsetForNodeAndOffset(*characterOffset.node, characterOffset.offset - 1, TraverseOptionIncludeStart);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> CharacterOffset AXObjectCache::startCharacterOffsetOfWord(const CharacterOffset&amp; characterOffset, EWordSide side)
</span><span class="lines">@@ -1949,9 +1975,13 @@
</span><span class="cx">         if (c.isEqual(startOfParagraph))
</span><span class="cx">             return c;
</span><span class="cx">         
</span><del>-        c = previousCharacterOffset(characterOffset, false);
</del><ins>+        c = previousCharacterOffset(characterOffset);
</ins><span class="cx">         if (c.isNull())
</span><span class="cx">             return characterOffset;
</span><ins>+    } else {
+        CharacterOffset endOfParagraph = endCharacterOffsetOfParagraph(characterOffset);
+        if (characterOffset.isEqual(endOfParagraph))
+            return characterOffset;
</ins><span class="cx">     }
</span><span class="cx">     
</span><span class="cx">     return nextBoundary(c, endWordBoundary);
</span><span class="lines">@@ -1962,7 +1992,7 @@
</span><span class="cx">     if (characterOffset.isNull())
</span><span class="cx">         return CharacterOffset();
</span><span class="cx">     
</span><del>-    CharacterOffset previousOffset = previousCharacterOffset(characterOffset, false);
</del><ins>+    CharacterOffset previousOffset = previousCharacterOffset(characterOffset);
</ins><span class="cx">     if (previousOffset.isNull())
</span><span class="cx">         return CharacterOffset();
</span><span class="cx">     
</span><span class="lines">@@ -2207,11 +2237,11 @@
</span><span class="cx"> CharacterOffset AXObjectCache::previousParagraphStartCharacterOffset(const CharacterOffset&amp; characterOffset)
</span><span class="cx"> {
</span><span class="cx">     // make sure we move off of a paragraph start
</span><del>-    CharacterOffset previous = previousCharacterOffset(characterOffset, false);
</del><ins>+    CharacterOffset previous = previousCharacterOffset(characterOffset);
</ins><span class="cx">     
</span><span class="cx">     // We should skip the preceding BR node.
</span><span class="cx">     if (characterOffsetNodeIsBR(previous) &amp;&amp; !characterOffsetNodeIsBR(characterOffset))
</span><del>-        previous = previousCharacterOffset(previous, false);
</del><ins>+        previous = previousCharacterOffset(previous);
</ins><span class="cx">     
</span><span class="cx">     return startCharacterOffsetOfParagraph(previous);
</span><span class="cx"> }
</span><span class="lines">@@ -2242,11 +2272,11 @@
</span><span class="cx"> CharacterOffset AXObjectCache::previousSentenceStartCharacterOffset(const CharacterOffset&amp; characterOffset)
</span><span class="cx"> {
</span><span class="cx">     // Make sure we move off of a sentence start.
</span><del>-    CharacterOffset previous = previousCharacterOffset(characterOffset, false);
</del><ins>+    CharacterOffset previous = previousCharacterOffset(characterOffset);
</ins><span class="cx">     
</span><span class="cx">     // We should skip the preceding BR node.
</span><del>-    if (!previous.isNull() &amp;&amp; previous.node-&gt;hasTagName(brTag) &amp;&amp; !characterOffset.node-&gt;hasTagName(brTag))
-        previous = previousCharacterOffset(previous, false);
</del><ins>+    if (characterOffsetNodeIsBR(previous) &amp;&amp; !characterOffsetNodeIsBR(characterOffset))
+        previous = previousCharacterOffset(previous);
</ins><span class="cx">     
</span><span class="cx">     return startCharacterOffsetOfSentence(previous);
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoreaccessibilityAXObjectCacheh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/accessibility/AXObjectCache.h (196823 => 196824)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/accessibility/AXObjectCache.h        2016-02-19 18:06:32 UTC (rev 196823)
+++ trunk/Source/WebCore/accessibility/AXObjectCache.h        2016-02-19 18:58:31 UTC (rev 196824)
</span><span class="lines">@@ -196,8 +196,9 @@
</span><span class="cx">     void textMarkerDataForPreviousCharacterOffset(TextMarkerData&amp;, const CharacterOffset&amp;);
</span><span class="cx">     VisiblePosition visiblePositionForTextMarkerData(TextMarkerData&amp;);
</span><span class="cx">     CharacterOffset characterOffsetForTextMarkerData(TextMarkerData&amp;);
</span><del>-    CharacterOffset nextCharacterOffset(const CharacterOffset&amp;, bool ignoreStart = true);
-    CharacterOffset previousCharacterOffset(const CharacterOffset&amp;, bool ignoreStart = true);
</del><ins>+    // Use ignoreNextNodeStart/ignorePreviousNodeEnd to determine the behavior when we are at node boundary. 
+    CharacterOffset nextCharacterOffset(const CharacterOffset&amp;, bool ignoreNextNodeStart = true);
+    CharacterOffset previousCharacterOffset(const CharacterOffset&amp;, bool ignorePreviousNodeEnd = true);
</ins><span class="cx">     void startOrEndTextMarkerDataForRange(TextMarkerData&amp;, RefPtr&lt;Range&gt;, bool);
</span><span class="cx">     AccessibilityObject* accessibilityObjectForTextMarkerData(TextMarkerData&amp;);
</span><span class="cx">     RefPtr&lt;Range&gt; rangeForUnorderedCharacterOffsets(const CharacterOffset&amp;, const CharacterOffset&amp;);
</span><span class="lines">@@ -314,7 +315,7 @@
</span><span class="cx">     enum TraverseOption { TraverseOptionDefault = 1 &lt;&lt; 0, TraverseOptionToNodeEnd = 1 &lt;&lt; 1, TraverseOptionIncludeStart = 1 &lt;&lt; 2 };
</span><span class="cx">     Node* nextNode(Node*) const;
</span><span class="cx">     Node* previousNode(Node*) const;
</span><del>-    CharacterOffset traverseToOffsetInRange(RefPtr&lt;Range&gt;, int, bool, bool stayWithinRange = false);
</del><ins>+    CharacterOffset traverseToOffsetInRange(RefPtr&lt;Range&gt;, int, TraverseOption = TraverseOptionDefault, bool stayWithinRange = false);
</ins><span class="cx">     VisiblePosition visiblePositionFromCharacterOffset(const CharacterOffset&amp;);
</span><span class="cx">     CharacterOffset characterOffsetFromVisiblePosition(const VisiblePosition&amp;);
</span><span class="cx">     void setTextMarkerDataWithCharacterOffset(TextMarkerData&amp;, const CharacterOffset&amp;);
</span></span></pre></div>
<a id="trunkSourceWebCoreaccessibilitymacWebAccessibilityObjectWrapperMacmm"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm (196823 => 196824)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm        2016-02-19 18:06:32 UTC (rev 196823)
+++ trunk/Source/WebCore/accessibility/mac/WebAccessibilityObjectWrapperMac.mm        2016-02-19 18:58:31 UTC (rev 196824)
</span><span class="lines">@@ -1293,19 +1293,9 @@
</span><span class="cx">     if (!m_object)
</span><span class="cx">         return nil;
</span><span class="cx">     
</span><del>-    // extract the start and end VisiblePosition
-    VisiblePosition startVisiblePosition = visiblePositionForStartOfTextMarkerRange(m_object-&gt;axObjectCache(), textMarkerRange);
-    if (startVisiblePosition.isNull())
-        return nil;
-    
-    VisiblePosition endVisiblePosition = visiblePositionForEndOfTextMarkerRange(m_object-&gt;axObjectCache(), textMarkerRange);
-    if (endVisiblePosition.isNull())
-        return nil;
-    
-    VisiblePositionRange visiblePositionRange(startVisiblePosition, endVisiblePosition);
-    // iterate over the range to build the AX attributed string
</del><ins>+    RefPtr&lt;Range&gt; range = [self rangeForTextMarkerRange:textMarkerRange];
</ins><span class="cx">     NSMutableAttributedString* attrString = [[NSMutableAttributedString alloc] init];
</span><del>-    TextIterator it(makeRange(startVisiblePosition, endVisiblePosition).get());
</del><ins>+    TextIterator it(range.get());
</ins><span class="cx">     while (!it.atEnd()) {
</span><span class="cx">         // locate the node and starting offset for this range
</span><span class="cx">         Node&amp; node = it.range()-&gt;startContainer();
</span></span></pre>
</div>
</div>

</body>
</html>