<!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>[182828] 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/182828">182828</a></dd>
<dt>Author</dt> <dd>commit-queue@webkit.org</dd>
<dt>Date</dt> <dd>2015-04-14 18:34:25 -0700 (Tue, 14 Apr 2015)</dd>
</dl>

<h3>Log Message</h3>
<pre>textPath layout performance improvement.
https://bugs.webkit.org/show_bug.cgi?id=141570.

Patch by Said Abou-Hallawa &lt;sabouhallawa@apple.com&gt; on 2015-04-14
Reviewed by Darin Adler.

PerformanceTests:

Cut down the time spent in traversing the path for text by 50%. Instead
of traversing the path twice at a certain length: one time for the position
and the second time for the angle, we can merge these two passes into one.

* SVG/TextOnPathSimple.html: Added.
* SVG/resources/TextOnPathSimple.svg: Added.

Source/WebCore:

The bottleneck of the text-on-path performance is the position and angle
calculations for every single character. If the number of characters is
'n' and the number of path elements is 'm', the total number of processing
the path elements is O(2 x n x m). What makes it really worse is, for every
curve we keep splitting the curve till the split curve is almost a straight
line. The changes we need to do are:
1. Merge the position and the angle traversals in one pass since they are
   returning info for the same length on the path. There is a degenerate
   case for the starting point when calculating the angle. The original
   code was solving this problem by passing an epsilon instead of zero but
   because traversing the path for position and angle are now merged, we
   will pass zero for the starting point as is. All we need is to move one
   step ahead without moving the position. We need the extra step forward
   to calculate the slope of the path at the starting point.
2. We need to add a new mode to traversing a path. The new mode will take
   a vector of lengths and returns a vector of arrow vectors. Every arrow
   vector represents a position and an angle on the path at a certain length.
   This requires changing the SVGTextLayoutEngine to calculate the lengths
   of the characters on the curve first and then passing all of them to the
   path traversal function. Instead of traversing the path for every length,
   we are going to get the required point and angle from the vector of arrow
   vectors.

This patch is addressing the first fix only. The second one will require
refactoring the SVGTextLayoutEngine so I am going to address it in a
different patch.

* platform/graphics/Path.cpp:
(WebCore::pathLengthApplierFunction): It is cleaner to move the function
of this method to PathTraversalState::processPathElement().

(WebCore::Path::length): Use new enum Action value and access methods.

(WebCore::Path::traversalStateAtLength): New function which returns the
traversalState at a certain length on a path.

(WebCore::Path::pointAtLength):
(WebCore::Path::normalAngleAtLength): Use traversalStateAtLength() to get
the traversalState and from it return either the position or the angle.

* platform/graphics/Path.h: Define traversalStateAtLength().

* platform/graphics/PathTraversalState.cpp:
(WebCore::distanceLine): Code clean up.

(WebCore::curveLength): Make the setting of m_previous and m_current happens
only in this function.

(WebCore::PathTraversalState::PathTraversalState): Add an optional parameter
for the desired length and move the initialization of the other members to
the class definition.

(WebCore::PathTraversalState::closeSubpath):
(WebCore::PathTraversalState::moveTo):
(WebCore::PathTraversalState::lineTo): Add the distance to the m_totalLength
instead of returning it since this is what all the callers were doing.

(WebCore::PathTraversalState::quadraticBezierTo):
(WebCore::PathTraversalState::cubicBezierTo): Add the distance to the
m_totalLength. Move the setting of m_previous and m_current to curveLength().
Remove unused members m_control1 and m_control2.

(WebCore::PathTraversalState::processSegment): Deleted.
(WebCore::PathTraversalState::finalizeAppendPathElement): Create a new
name for the function. Handle the case of the angle at the starting point
where m_desiredLength is set to zero. The new flag m_isZeroVector will be
set to notify the caller that the  next iteration will be the last one and
it is only needed for the calculating the angle of a zero vector. m_current
should not change by this last iteration.

(WebCore::PathTraversalState::appendPathElement): This code is moved from
pathLengthApplierFunction().

(WebCore::PathTraversalState::processPathElement): This function is used
by the class Path. It is a wrapper for appendPathElement(). If m_isZeroVector
is set we append the new element to a copy for the PathTraversalState just
to get the angle for the zero vector.

* platform/graphics/PathTraversalState.h: Change the enum values to not
not include the class or the enum class. Make the data members private and
expose the needed ones through access methods. Make all the internal methods
to be private.

(WebCore::PathTraversalState::processPathElement):  Another wrapper for
appendPathElement() which is used by SVGPathTraversalStateBuilder.

(WebCore::PathTraversalState::action):
(WebCore::PathTraversalState::setAction):
(WebCore::PathTraversalState::desiredLength):
(WebCore::PathTraversalState::setDesiredLength):
(WebCore::PathTraversalState::success):
(WebCore::PathTraversalState::totalLength):
(WebCore::PathTraversalState::current):
(WebCore::PathTraversalState::normalAngle): New access methods which are now
needed after making the data members private.

* rendering/svg/SVGRootInlineBox.cpp:
(WebCore::SVGRootInlineBox::layoutCharactersInTextBoxes): Make the casting
of the renderer on the caller side.

* rendering/svg/SVGTextChunk.cpp:
(WebCore::SVGTextChunk::SVGTextChunk): The constructor should append the
elements of m_boxes instead of making this from outside the class.

(WebCore::SVGTextChunk::totalCharacters):
(WebCore::SVGTextChunk::totalLength):
(WebCore::SVGTextChunk::calculateLength): Deleted.
Replace calculateLength() by totalCharacters() and totalLength() to make
the interface cleaner.

(WebCore::SVGTextChunk::totalAnchorShift):
(WebCore::SVGTextChunk::calculateTextAnchorShift): Deleted.
Rename the function name.

(WebCore::SVGTextChunk::layout):
(WebCore::SVGTextChunk::processTextLengthSpacingCorrection):
(WebCore::SVGTextChunk::buildBoxTransformations):
(WebCore::SVGTextChunk::boxSpacingAndGlyphsTransform):
(WebCore::SVGTextChunk::processTextAnchorCorrection):
Move the chunk layout code from SVGTextChunkBuilder::layoutTextChunks()
to the SVGTextChunk::layout(). Move all the helper functions as well.

* rendering/svg/SVGTextChunk.h:
(WebCore::SVGTextChunk::hasTextAnchor):
(WebCore::SVGTextChunk::boxes): Deleted.
Add the new methods and change most of the public methods to be private.

* rendering/svg/SVGTextChunkBuilder.cpp:
(WebCore::SVGTextChunkBuilder::totalCharacters):
(WebCore::SVGTextChunkBuilder::totalLength):
(WebCore::SVGTextChunkBuilder::totalAnchorShift): This code is moved from
SVGTextLayoutEngine. It scans the boxes stored in the SVGTextChunkBuilder
and sums up the total values.

(WebCore::SVGTextChunkBuilder::transformationForTextBox):
(WebCore::SVGTextChunkBuilder::buildTextChunks):
(WebCore::SVGTextChunkBuilder::layoutTextChunks): Code clean up.

(WebCore::SVGTextChunkBuilder::addTextChunk): Deleted.
(WebCore::SVGTextChunkBuilder::processTextChunk): Deleted.
(WebCore::SVGTextChunkBuilder::processTextLengthSpacingCorrection): Deleted.
(WebCore::SVGTextChunkBuilder::processTextAnchorCorrection): Deleted.
(WebCore::SVGTextChunkBuilder::buildSpacingAndGlyphsTransform): Deleted.
This code now lives in SVGTextChunk.

* rendering/svg/SVGTextChunkBuilder.h: Add new methods for code which was
moved from SVGTextLayoutEngine and remove methods for code which was removed
to SVGTextChunk.

* rendering/svg/SVGTextLayoutEngine.cpp:
(WebCore::SVGTextLayoutEngine::beginTextPathLayout): Use the sum up methods
from SVGTextChunkBuilder instead of looping through the chunks. Also get a
clean order for defining variables and doing the calculations.

(WebCore::SVGTextLayoutEngine::finalizeTransformMatrices): Code clean up.

(WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath): Do a single path
traversal to get the position and the angle for a length on a path.

* svg/SVGAnimateMotionElement.cpp:
(WebCore::SVGAnimateMotionElement::buildTransformForProgress): Do a single
path traversal to get the position and the angle at a length on a path.

* svg/SVGPathTraversalStateBuilder.cpp:
(WebCore::SVGPathTraversalStateBuilder::SVGPathTraversalStateBuilder):
(WebCore::SVGPathTraversalStateBuilder::moveTo):
(WebCore::SVGPathTraversalStateBuilder::lineTo):
(WebCore::SVGPathTraversalStateBuilder::curveToCubic):
(WebCore::SVGPathTraversalStateBuilder::closePath):
(WebCore::SVGPathTraversalStateBuilder::setDesiredLength):
(WebCore::SVGPathTraversalStateBuilder::continueConsuming):
(WebCore::SVGPathTraversalStateBuilder::totalLength):
(WebCore::SVGPathTraversalStateBuilder::currentPoint):
(WebCore::SVGPathTraversalStateBuilder::incrementPathSegmentCount): Deleted.
(WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex): Deleted.
* svg/SVGPathTraversalStateBuilder.h:
(WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex):
Code clean up.

* svg/SVGPathUtilities.cpp:
(WebCore::getSVGPathSegAtLengthFromSVGPathByteStream):
(WebCore::getTotalLengthOfSVGPathByteStream):
(WebCore::getPointAtLengthOfSVGPathByteStream): Use new TraversalState::Action
enum values.</pre>

<h3>Modified Paths</h3>
<ul>
<li><a href="#trunkPerformanceTestsChangeLog">trunk/PerformanceTests/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreChangeLog">trunk/Source/WebCore/ChangeLog</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsPathcpp">trunk/Source/WebCore/platform/graphics/Path.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsPathh">trunk/Source/WebCore/platform/graphics/Path.h</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsPathTraversalStatecpp">trunk/Source/WebCore/platform/graphics/PathTraversalState.cpp</a></li>
<li><a href="#trunkSourceWebCoreplatformgraphicsPathTraversalStateh">trunk/Source/WebCore/platform/graphics/PathTraversalState.h</a></li>
<li><a href="#trunkSourceWebCorerenderingsvgSVGTextChunkcpp">trunk/Source/WebCore/rendering/svg/SVGTextChunk.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingsvgSVGTextChunkh">trunk/Source/WebCore/rendering/svg/SVGTextChunk.h</a></li>
<li><a href="#trunkSourceWebCorerenderingsvgSVGTextChunkBuildercpp">trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp</a></li>
<li><a href="#trunkSourceWebCorerenderingsvgSVGTextChunkBuilderh">trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h</a></li>
<li><a href="#trunkSourceWebCorerenderingsvgSVGTextLayoutEnginecpp">trunk/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp</a></li>
<li><a href="#trunkSourceWebCoresvgSVGAnimateMotionElementcpp">trunk/Source/WebCore/svg/SVGAnimateMotionElement.cpp</a></li>
<li><a href="#trunkSourceWebCoresvgSVGPathTraversalStateBuildercpp">trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp</a></li>
<li><a href="#trunkSourceWebCoresvgSVGPathTraversalStateBuilderh">trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.h</a></li>
<li><a href="#trunkSourceWebCoresvgSVGPathUtilitiescpp">trunk/Source/WebCore/svg/SVGPathUtilities.cpp</a></li>
</ul>

<h3>Added Paths</h3>
<ul>
<li><a href="#trunkPerformanceTestsSVGTextOnPathSimplehtml">trunk/PerformanceTests/SVG/TextOnPathSimple.html</a></li>
<li><a href="#trunkPerformanceTestsSVGresourcesTextOnPathSimplesvg">trunk/PerformanceTests/SVG/resources/TextOnPathSimple.svg</a></li>
</ul>

</div>
<div id="patch">
<h3>Diff</h3>
<a id="trunkPerformanceTestsChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/PerformanceTests/ChangeLog (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/ChangeLog        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/PerformanceTests/ChangeLog        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -1,3 +1,17 @@
</span><ins>+2015-04-14  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        textPath layout performance improvement.
+        https://bugs.webkit.org/show_bug.cgi?id=141570.
+
+        Reviewed by Darin Adler.
+
+        Cut down the time spent in traversing the path for text by 50%. Instead
+        of traversing the path twice at a certain length: one time for the position
+        and the second time for the angle, we can merge these two passes into one.
+
+        * SVG/TextOnPathSimple.html: Added.
+        * SVG/resources/TextOnPathSimple.svg: Added.
+
</ins><span class="cx"> 2015-04-13  Zalan Bujtas  &lt;zalan@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Clear up the test content when test is done.
</span></span></pre></div>
<a id="trunkPerformanceTestsSVGTextOnPathSimplehtml"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/SVG/TextOnPathSimple.html (0 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/SVG/TextOnPathSimple.html                                (rev 0)
+++ trunk/PerformanceTests/SVG/TextOnPathSimple.html        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -0,0 +1,9 @@
</span><ins>+&lt;!DOCTYPE html&gt;
+&lt;body&gt;
+&lt;script src=&quot;../resources/runner.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+  window.onload = function() {
+    PerfTestRunner.measurePageLoadTime({path: &quot;resources/TextOnPathSimple.svg&quot;});
+  }
+&lt;/script&gt;
+&lt;/body&gt;
</ins></span></pre></div>
<a id="trunkPerformanceTestsSVGresourcesTextOnPathSimplesvg"></a>
<div class="addfile"><h4>Added: trunk/PerformanceTests/SVG/resources/TextOnPathSimple.svg (0 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/PerformanceTests/SVG/resources/TextOnPathSimple.svg                                (rev 0)
+++ trunk/PerformanceTests/SVG/resources/TextOnPathSimple.svg        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -0,0 +1,42 @@
</span><ins>+&lt;svg width=&quot;100%&quot; height=&quot;100%&quot; viewBox=&quot;0 0 4000 2500&quot;
+     xmlns=&quot;http://www.w3.org/2000/svg&quot; 
+     xmlns:xlink=&quot;http://www.w3.org/1999/xlink&quot;&gt;
+  &lt;defs&gt;
+    &lt;path id=&quot;SimplePath&quot;
+      d=&quot;M 100  200 
+         C 200  100 300    0  400  100
+         C 500  200 600  300  700  200
+         C 800  100 900    0  1000 100
+         C 1100 200 1200 300  1300 200
+         C 1400 100 1500   0  1600 100
+         C 1700 200 1800 300  1900 200&quot;/&gt;
+  &lt;/defs&gt;
+
+  &lt;g id=&quot;TextOnPath25&quot;&gt;
+    &lt;g id=&quot;TextOnPath5&quot;&gt;
+      &lt;g id=&quot;TextOnPath&quot;&gt;
+        &lt;use xlink:href=&quot;#SimplePath&quot; fill=&quot;none&quot; stroke=&quot;red&quot;/&gt;
+        &lt;text font-family=&quot;Verdana&quot; font-size=&quot;42.5&quot;&gt;
+          &lt;textPath xlink:href=&quot;#SimplePath&quot;&gt;
+            We go up, then we go down, then up again, then we go down, then we up again, then we go down.
+          &lt;/textPath&gt;
+        &lt;/text&gt;
+      &lt;/g&gt;
+      &lt;use xlink:href=&quot;#TextOnPath&quot; transform=&quot;translate(0,40)&quot;/&gt;
+      &lt;use xlink:href=&quot;#TextOnPath&quot; transform=&quot;translate(0,80)&quot;/&gt;
+      &lt;use xlink:href=&quot;#TextOnPath&quot; transform=&quot;translate(0,120)&quot;/&gt;
+      &lt;use xlink:href=&quot;#TextOnPath&quot; transform=&quot;translate(0,160)&quot;/&gt;
+    &lt;/g&gt;
+  
+    &lt;use xlink:href=&quot;#TextOnPath5&quot; transform=&quot;translate(0,200)&quot;/&gt;
+    &lt;use xlink:href=&quot;#TextOnPath5&quot; transform=&quot;translate(0,400)&quot;/&gt;
+    &lt;use xlink:href=&quot;#TextOnPath5&quot; transform=&quot;translate(0,600)&quot;/&gt;
+    &lt;use xlink:href=&quot;#TextOnPath5&quot; transform=&quot;translate(0,800)&quot;/&gt;
+  &lt;/g&gt;
+
+  &lt;use xlink:href=&quot;#TextOnPath25&quot; transform=&quot;translate(2000,0)&quot;/&gt;
+  &lt;use xlink:href=&quot;#TextOnPath25&quot; transform=&quot;translate(0,1200)&quot;/&gt;
+  &lt;use xlink:href=&quot;#TextOnPath25&quot; transform=&quot;translate(2000,1200)&quot;/&gt;
+
+  &lt;rect x=&quot;1&quot; y=&quot;1&quot; width=&quot;3999&quot; height=&quot;2499&quot; fill=&quot;none&quot; stroke=&quot;black&quot; stroke-width=&quot;2&quot; /&gt;
+&lt;/svg&gt;
</ins></span></pre></div>
<a id="trunkSourceWebCoreChangeLog"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/ChangeLog (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/ChangeLog        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/ChangeLog        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -1,3 +1,195 @@
</span><ins>+2015-04-14  Said Abou-Hallawa  &lt;sabouhallawa@apple.com&gt;
+
+        textPath layout performance improvement.
+        https://bugs.webkit.org/show_bug.cgi?id=141570.
+
+        Reviewed by Darin Adler.
+
+        The bottleneck of the text-on-path performance is the position and angle
+        calculations for every single character. If the number of characters is
+        'n' and the number of path elements is 'm', the total number of processing
+        the path elements is O(2 x n x m). What makes it really worse is, for every
+        curve we keep splitting the curve till the split curve is almost a straight
+        line. The changes we need to do are:
+        1. Merge the position and the angle traversals in one pass since they are
+           returning info for the same length on the path. There is a degenerate
+           case for the starting point when calculating the angle. The original
+           code was solving this problem by passing an epsilon instead of zero but
+           because traversing the path for position and angle are now merged, we
+           will pass zero for the starting point as is. All we need is to move one
+           step ahead without moving the position. We need the extra step forward
+           to calculate the slope of the path at the starting point.
+        2. We need to add a new mode to traversing a path. The new mode will take
+           a vector of lengths and returns a vector of arrow vectors. Every arrow
+           vector represents a position and an angle on the path at a certain length.
+           This requires changing the SVGTextLayoutEngine to calculate the lengths
+           of the characters on the curve first and then passing all of them to the
+           path traversal function. Instead of traversing the path for every length,
+           we are going to get the required point and angle from the vector of arrow
+           vectors.
+
+        This patch is addressing the first fix only. The second one will require
+        refactoring the SVGTextLayoutEngine so I am going to address it in a
+        different patch.
+
+        * platform/graphics/Path.cpp:
+        (WebCore::pathLengthApplierFunction): It is cleaner to move the function
+        of this method to PathTraversalState::processPathElement().
+        
+        (WebCore::Path::length): Use new enum Action value and access methods.
+        
+        (WebCore::Path::traversalStateAtLength): New function which returns the
+        traversalState at a certain length on a path.
+        
+        (WebCore::Path::pointAtLength):
+        (WebCore::Path::normalAngleAtLength): Use traversalStateAtLength() to get
+        the traversalState and from it return either the position or the angle.
+        
+        * platform/graphics/Path.h: Define traversalStateAtLength().
+        
+        * platform/graphics/PathTraversalState.cpp:
+        (WebCore::distanceLine): Code clean up.
+        
+        (WebCore::curveLength): Make the setting of m_previous and m_current happens
+        only in this function.
+        
+        (WebCore::PathTraversalState::PathTraversalState): Add an optional parameter
+        for the desired length and move the initialization of the other members to
+        the class definition.
+        
+        (WebCore::PathTraversalState::closeSubpath):
+        (WebCore::PathTraversalState::moveTo):
+        (WebCore::PathTraversalState::lineTo): Add the distance to the m_totalLength
+        instead of returning it since this is what all the callers were doing.
+        
+        (WebCore::PathTraversalState::quadraticBezierTo):
+        (WebCore::PathTraversalState::cubicBezierTo): Add the distance to the
+        m_totalLength. Move the setting of m_previous and m_current to curveLength().
+        Remove unused members m_control1 and m_control2.
+
+        (WebCore::PathTraversalState::processSegment): Deleted.        
+        (WebCore::PathTraversalState::finalizeAppendPathElement): Create a new
+        name for the function. Handle the case of the angle at the starting point
+        where m_desiredLength is set to zero. The new flag m_isZeroVector will be
+        set to notify the caller that the  next iteration will be the last one and
+        it is only needed for the calculating the angle of a zero vector. m_current
+        should not change by this last iteration.
+        
+        (WebCore::PathTraversalState::appendPathElement): This code is moved from
+        pathLengthApplierFunction().
+        
+        (WebCore::PathTraversalState::processPathElement): This function is used
+        by the class Path. It is a wrapper for appendPathElement(). If m_isZeroVector
+        is set we append the new element to a copy for the PathTraversalState just
+        to get the angle for the zero vector.
+
+        * platform/graphics/PathTraversalState.h: Change the enum values to not
+        not include the class or the enum class. Make the data members private and
+        expose the needed ones through access methods. Make all the internal methods
+        to be private.
+        
+        (WebCore::PathTraversalState::processPathElement):  Another wrapper for
+        appendPathElement() which is used by SVGPathTraversalStateBuilder.
+        
+        (WebCore::PathTraversalState::action):
+        (WebCore::PathTraversalState::setAction):
+        (WebCore::PathTraversalState::desiredLength):
+        (WebCore::PathTraversalState::setDesiredLength):
+        (WebCore::PathTraversalState::success):
+        (WebCore::PathTraversalState::totalLength):
+        (WebCore::PathTraversalState::current):
+        (WebCore::PathTraversalState::normalAngle): New access methods which are now
+        needed after making the data members private.
+        
+        * rendering/svg/SVGRootInlineBox.cpp:
+        (WebCore::SVGRootInlineBox::layoutCharactersInTextBoxes): Make the casting
+        of the renderer on the caller side.
+        
+        * rendering/svg/SVGTextChunk.cpp:
+        (WebCore::SVGTextChunk::SVGTextChunk): The constructor should append the
+        elements of m_boxes instead of making this from outside the class.
+        
+        (WebCore::SVGTextChunk::totalCharacters):
+        (WebCore::SVGTextChunk::totalLength):
+        (WebCore::SVGTextChunk::calculateLength): Deleted.
+        Replace calculateLength() by totalCharacters() and totalLength() to make
+        the interface cleaner.
+        
+        (WebCore::SVGTextChunk::totalAnchorShift):
+        (WebCore::SVGTextChunk::calculateTextAnchorShift): Deleted.
+        Rename the function name.
+        
+        (WebCore::SVGTextChunk::layout):
+        (WebCore::SVGTextChunk::processTextLengthSpacingCorrection):
+        (WebCore::SVGTextChunk::buildBoxTransformations):
+        (WebCore::SVGTextChunk::boxSpacingAndGlyphsTransform):
+        (WebCore::SVGTextChunk::processTextAnchorCorrection):
+        Move the chunk layout code from SVGTextChunkBuilder::layoutTextChunks()
+        to the SVGTextChunk::layout(). Move all the helper functions as well.
+        
+        * rendering/svg/SVGTextChunk.h:
+        (WebCore::SVGTextChunk::hasTextAnchor):
+        (WebCore::SVGTextChunk::boxes): Deleted.
+        Add the new methods and change most of the public methods to be private.
+        
+        * rendering/svg/SVGTextChunkBuilder.cpp:
+        (WebCore::SVGTextChunkBuilder::totalCharacters):
+        (WebCore::SVGTextChunkBuilder::totalLength):
+        (WebCore::SVGTextChunkBuilder::totalAnchorShift): This code is moved from
+        SVGTextLayoutEngine. It scans the boxes stored in the SVGTextChunkBuilder
+        and sums up the total values.
+        
+        (WebCore::SVGTextChunkBuilder::transformationForTextBox):
+        (WebCore::SVGTextChunkBuilder::buildTextChunks):
+        (WebCore::SVGTextChunkBuilder::layoutTextChunks): Code clean up.
+        
+        (WebCore::SVGTextChunkBuilder::addTextChunk): Deleted.
+        (WebCore::SVGTextChunkBuilder::processTextChunk): Deleted.
+        (WebCore::SVGTextChunkBuilder::processTextLengthSpacingCorrection): Deleted.
+        (WebCore::SVGTextChunkBuilder::processTextAnchorCorrection): Deleted.
+        (WebCore::SVGTextChunkBuilder::buildSpacingAndGlyphsTransform): Deleted.
+        This code now lives in SVGTextChunk.
+        
+        * rendering/svg/SVGTextChunkBuilder.h: Add new methods for code which was
+        moved from SVGTextLayoutEngine and remove methods for code which was removed
+        to SVGTextChunk.
+        
+        * rendering/svg/SVGTextLayoutEngine.cpp:
+        (WebCore::SVGTextLayoutEngine::beginTextPathLayout): Use the sum up methods
+        from SVGTextChunkBuilder instead of looping through the chunks. Also get a
+        clean order for defining variables and doing the calculations.
+        
+        (WebCore::SVGTextLayoutEngine::finalizeTransformMatrices): Code clean up.
+        
+        (WebCore::SVGTextLayoutEngine::layoutTextOnLineOrPath): Do a single path
+        traversal to get the position and the angle for a length on a path.
+        
+        * svg/SVGAnimateMotionElement.cpp:
+        (WebCore::SVGAnimateMotionElement::buildTransformForProgress): Do a single
+        path traversal to get the position and the angle at a length on a path.
+        
+        * svg/SVGPathTraversalStateBuilder.cpp:
+        (WebCore::SVGPathTraversalStateBuilder::SVGPathTraversalStateBuilder):
+        (WebCore::SVGPathTraversalStateBuilder::moveTo):
+        (WebCore::SVGPathTraversalStateBuilder::lineTo):
+        (WebCore::SVGPathTraversalStateBuilder::curveToCubic):
+        (WebCore::SVGPathTraversalStateBuilder::closePath):
+        (WebCore::SVGPathTraversalStateBuilder::setDesiredLength):
+        (WebCore::SVGPathTraversalStateBuilder::continueConsuming):
+        (WebCore::SVGPathTraversalStateBuilder::totalLength):
+        (WebCore::SVGPathTraversalStateBuilder::currentPoint):
+        (WebCore::SVGPathTraversalStateBuilder::incrementPathSegmentCount): Deleted.
+        (WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex): Deleted.
+        * svg/SVGPathTraversalStateBuilder.h:
+        (WebCore::SVGPathTraversalStateBuilder::pathSegmentIndex):
+        Code clean up.
+        
+        * svg/SVGPathUtilities.cpp:
+        (WebCore::getSVGPathSegAtLengthFromSVGPathByteStream):
+        (WebCore::getTotalLengthOfSVGPathByteStream):
+        (WebCore::getPointAtLengthOfSVGPathByteStream): Use new TraversalState::Action
+        enum values.
+
</ins><span class="cx"> 2015-04-14  Simon Fraser  &lt;simon.fraser@apple.com&gt;
</span><span class="cx"> 
</span><span class="cx">         Re-enable custom dilation for antialiased fonts
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsPathcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/Path.cpp (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/Path.cpp        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/platform/graphics/Path.cpp        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -42,56 +42,34 @@
</span><span class="cx"> static void pathLengthApplierFunction(void* info, const PathElement* element)
</span><span class="cx"> {
</span><span class="cx">     PathTraversalState&amp; traversalState = *static_cast&lt;PathTraversalState*&gt;(info);
</span><del>-    if (traversalState.m_success)
-        return;
-    FloatPoint* points = element-&gt;points;
-    float segmentLength = 0;
-    switch (element-&gt;type) {
-        case PathElementMoveToPoint:
-            segmentLength = traversalState.moveTo(points[0]);
-            break;
-        case PathElementAddLineToPoint:
-            segmentLength = traversalState.lineTo(points[0]);
-            break;
-        case PathElementAddQuadCurveToPoint:
-            segmentLength = traversalState.quadraticBezierTo(points[0], points[1]);
-            break;
-        case PathElementAddCurveToPoint:
-            segmentLength = traversalState.cubicBezierTo(points[0], points[1], points[2]);
-            break;
-        case PathElementCloseSubpath:
-            segmentLength = traversalState.closeSubpath();
-            break;
-    }
-    traversalState.m_totalLength += segmentLength; 
-    traversalState.processSegment();
</del><ins>+    traversalState.processPathElement(element);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> float Path::length() const
</span><span class="cx"> {
</span><del>-    PathTraversalState traversalState(PathTraversalState::TraversalTotalLength);
</del><ins>+    PathTraversalState traversalState(PathTraversalState::Action::TotalLength);
</ins><span class="cx">     apply(&amp;traversalState, pathLengthApplierFunction);
</span><del>-    return traversalState.m_totalLength;
</del><ins>+    return traversalState.totalLength();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-FloatPoint Path::pointAtLength(float length, bool&amp; ok) const
</del><ins>+PathTraversalState Path::traversalStateAtLength(float length, bool&amp; success) const
</ins><span class="cx"> {
</span><del>-    PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength);
-    traversalState.m_desiredLength = length;
</del><ins>+    PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength, length);
</ins><span class="cx">     apply(&amp;traversalState, pathLengthApplierFunction);
</span><del>-    ok = traversalState.m_success;
-    return traversalState.m_current;
</del><ins>+    success = traversalState.success();
+    return traversalState;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-float Path::normalAngleAtLength(float length, bool&amp; ok) const
</del><ins>+FloatPoint Path::pointAtLength(float length, bool&amp; success) const
</ins><span class="cx"> {
</span><del>-    PathTraversalState traversalState(PathTraversalState::TraversalNormalAngleAtLength);
-    traversalState.m_desiredLength = length ? length : std::numeric_limits&lt;float&gt;::epsilon();
-    apply(&amp;traversalState, pathLengthApplierFunction);
-    ok = traversalState.m_success;
-    return traversalState.m_normalAngle;
</del><ins>+    return traversalStateAtLength(length, success).current();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+float Path::normalAngleAtLength(float length, bool&amp; success) const
+{
+    return traversalStateAtLength(length, success).normalAngle();
+}
+
</ins><span class="cx"> void Path::addRoundedRect(const FloatRect&amp; rect, const FloatSize&amp; roundingRadii, RoundedRectStrategy strategy)
</span><span class="cx"> {
</span><span class="cx">     if (rect.isEmpty())
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsPathh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/Path.h (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/Path.h        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/platform/graphics/Path.h        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -60,6 +60,7 @@
</span><span class="cx">     class FloatRoundedRect;
</span><span class="cx">     class FloatSize;
</span><span class="cx">     class GraphicsContext;
</span><ins>+    class PathTraversalState;
</ins><span class="cx">     class RoundedRect;
</span><span class="cx">     class StrokeStyleApplier;
</span><span class="cx"> 
</span><span class="lines">@@ -100,10 +101,11 @@
</span><span class="cx">         FloatRect boundingRect() const;
</span><span class="cx">         FloatRect fastBoundingRect() const;
</span><span class="cx">         FloatRect strokeBoundingRect(StrokeStyleApplier* = 0) const;
</span><del>-        
</del><ins>+
</ins><span class="cx">         float length() const;
</span><del>-        FloatPoint pointAtLength(float length, bool&amp; ok) const;
-        float normalAngleAtLength(float length, bool&amp; ok) const;
</del><ins>+        PathTraversalState traversalStateAtLength(float length, bool&amp; success) const;
+        FloatPoint pointAtLength(float length, bool&amp; success) const;
+        float normalAngleAtLength(float length, bool&amp; success) const;
</ins><span class="cx"> 
</span><span class="cx">         WEBCORE_EXPORT void clear();
</span><span class="cx">         bool isNull() const { return !m_path; }
</span></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsPathTraversalStatecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/PathTraversalState.cpp (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/PathTraversalState.cpp        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/platform/graphics/PathTraversalState.cpp        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) 2006, 2007 Eric Seidel &lt;eric@webkit.org&gt;
</span><ins>+ * Copyright (C) 2015 Apple Inc.  All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * This library is free software; you can redistribute it and/or
</span><span class="cx">  * modify it under the terms of the GNU Library General Public
</span><span class="lines">@@ -34,7 +35,9 @@
</span><span class="cx"> 
</span><span class="cx"> static inline float distanceLine(const FloatPoint&amp; start, const FloatPoint&amp; end)
</span><span class="cx"> {
</span><del>-    return sqrtf((end.x() - start.x()) * (end.x() - start.x()) + (end.y() - start.y()) * (end.y() - start.y()));
</del><ins>+    float dx = end.x() - start.x();
+    float dy = end.y() - start.y();
+    return sqrtf(dx * dx + dy * dy);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> struct QuadraticBezier {
</span><span class="lines">@@ -114,111 +117,150 @@
</span><span class="cx"> // Another check which is possible up-front (to send us down the fast path) would be to check if
</span><span class="cx"> // approximateDistance() + current total distance &gt; desired distance
</span><span class="cx"> template&lt;class CurveType&gt;
</span><del>-static float curveLength(PathTraversalState&amp; traversalState, CurveType curve)
</del><ins>+static float curveLength(const PathTraversalState&amp; traversalState, const CurveType&amp; originalCurve, FloatPoint&amp; previous, FloatPoint&amp; current)
</ins><span class="cx"> {
</span><span class="cx">     static const unsigned curveStackDepthLimit = 20;
</span><ins>+    CurveType curve = originalCurve;
+    Vector&lt;CurveType, curveStackDepthLimit&gt; curveStack;
+    float totalLength = 0;
</ins><span class="cx"> 
</span><del>-    Vector&lt;CurveType&gt; curveStack;
-    curveStack.append(curve);
</del><ins>+    while (true) {
+        float length = curve.approximateDistance();
</ins><span class="cx"> 
</span><del>-    float totalLength = 0;
-    do {
-        float length = curve.approximateDistance();
-        if ((length - distanceLine(curve.start, curve.end)) &gt; kPathSegmentLengthTolerance &amp;&amp; curveStack.size() &lt;= curveStackDepthLimit) {
</del><ins>+        if ((length - distanceLine(curve.start, curve.end)) &gt; kPathSegmentLengthTolerance &amp;&amp; curveStack.size() &lt; curveStackDepthLimit) {
</ins><span class="cx">             CurveType leftCurve;
</span><span class="cx">             CurveType rightCurve;
</span><span class="cx">             curve.split(leftCurve, rightCurve);
</span><span class="cx">             curve = leftCurve;
</span><span class="cx">             curveStack.append(rightCurve);
</span><del>-        } else {
-            totalLength += length;
-            if (traversalState.m_action == PathTraversalState::TraversalPointAtLength
-             || traversalState.m_action == PathTraversalState::TraversalNormalAngleAtLength) {
-                traversalState.m_previous = curve.start;
-                traversalState.m_current = curve.end;
-                if (traversalState.m_totalLength + totalLength &gt; traversalState.m_desiredLength)
-                    return totalLength;
-            }
-            curve = curveStack.last();
-            curveStack.removeLast();
</del><ins>+            continue;
</ins><span class="cx">         }
</span><del>-    } while (!curveStack.isEmpty());
-    
</del><ins>+
+        totalLength += length;
+        if (traversalState.action() == PathTraversalState::Action::VectorAtLength) {
+            previous = curve.start;
+            current = curve.end;
+            if (traversalState.totalLength() + totalLength &gt; traversalState.desiredLength())
+                break;
+        }
+
+        if (curveStack.isEmpty())
+            break;
+
+        curve = curveStack.last();
+        curveStack.removeLast();
+    }
+
+    if (traversalState.action() != PathTraversalState::Action::VectorAtLength) {
+        ASSERT(curve.end == originalCurve.end);
+        previous = curve.start;
+        current = curve.end;
+    }
+
</ins><span class="cx">     return totalLength;
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-PathTraversalState::PathTraversalState(PathTraversalAction action)
</del><ins>+PathTraversalState::PathTraversalState(Action action, float desiredLength)
</ins><span class="cx">     : m_action(action)
</span><del>-    , m_success(false)
-    , m_totalLength(0)
-    , m_segmentIndex(0)
-    , m_desiredLength(0)
-    , m_normalAngle(0)
</del><ins>+    , m_desiredLength(desiredLength)
</ins><span class="cx"> {
</span><ins>+    ASSERT(action != Action::TotalLength || !desiredLength);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-float PathTraversalState::closeSubpath()
</del><ins>+void PathTraversalState::closeSubpath()
</ins><span class="cx"> {
</span><del>-    float distance = distanceLine(m_current, m_start);
-    m_current = m_control1 = m_control2 = m_start;
-    return distance;
</del><ins>+    m_totalLength += distanceLine(m_current, m_start);
+    m_current = m_start;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-float PathTraversalState::moveTo(const FloatPoint&amp; point)
</del><ins>+void PathTraversalState::moveTo(const FloatPoint&amp; point)
</ins><span class="cx"> {
</span><del>-    m_current = m_start = m_control1 = m_control2 = point;
-    return 0;
</del><ins>+    m_previous = m_current = m_start = point;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-float PathTraversalState::lineTo(const FloatPoint&amp; point)
</del><ins>+void PathTraversalState::lineTo(const FloatPoint&amp; point)
</ins><span class="cx"> {
</span><del>-    float distance = distanceLine(m_current, point);
-    m_current = m_control1 = m_control2 = point;
-    return distance;
</del><ins>+    m_totalLength += distanceLine(m_current, point);
+    m_current = point;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-float PathTraversalState::quadraticBezierTo(const FloatPoint&amp; newControl, const FloatPoint&amp; newEnd)
</del><ins>+void PathTraversalState::quadraticBezierTo(const FloatPoint&amp; newControl, const FloatPoint&amp; newEnd)
</ins><span class="cx"> {
</span><del>-    float distance = curveLength&lt;QuadraticBezier&gt;(*this, QuadraticBezier(m_current, newControl, newEnd));
</del><ins>+    m_totalLength += curveLength&lt;QuadraticBezier&gt;(*this, QuadraticBezier(m_current, newControl, newEnd), m_previous, m_current);
+}
</ins><span class="cx"> 
</span><del>-    m_control1 = newControl;
-    m_control2 = newEnd;
-
-    if (m_action != TraversalPointAtLength &amp;&amp; m_action != TraversalNormalAngleAtLength) 
-        m_current = newEnd;
-
-    return distance;
</del><ins>+void PathTraversalState::cubicBezierTo(const FloatPoint&amp; newControl1, const FloatPoint&amp; newControl2, const FloatPoint&amp; newEnd)
+{
+    m_totalLength += curveLength&lt;CubicBezier&gt;(*this, CubicBezier(m_current, newControl1, newControl2, newEnd), m_previous, m_current);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-float PathTraversalState::cubicBezierTo(const FloatPoint&amp; newControl1, const FloatPoint&amp; newControl2, const FloatPoint&amp; newEnd)
</del><ins>+bool PathTraversalState::finalizeAppendPathElement()
</ins><span class="cx"> {
</span><del>-    float distance = curveLength&lt;CubicBezier&gt;(*this, CubicBezier(m_current, newControl1, newControl2, newEnd));
</del><ins>+    if (m_action == Action::TotalLength)
+        return false;
</ins><span class="cx"> 
</span><del>-    m_control1 = newEnd;
-    m_control2 = newControl2;

-    if (m_action != TraversalPointAtLength &amp;&amp; m_action != TraversalNormalAngleAtLength) 
-        m_current = newEnd;
</del><ins>+    if (m_action == Action::SegmentAtLength) {
+        if (m_totalLength &gt;= m_desiredLength)
+            m_success = true;
+        return m_success;
+    }
</ins><span class="cx"> 
</span><del>-    return distance;
-}
</del><ins>+    ASSERT(m_action == Action::VectorAtLength);
</ins><span class="cx"> 
</span><del>-void PathTraversalState::processSegment()
-{
-    if (m_action == TraversalSegmentAtLength &amp;&amp; m_totalLength &gt;= m_desiredLength)
-        m_success = true;
-        
-    if ((m_action == TraversalPointAtLength || m_action == TraversalNormalAngleAtLength) &amp;&amp; m_totalLength &gt;= m_desiredLength) {
</del><ins>+    if (m_totalLength &gt;= m_desiredLength) {
</ins><span class="cx">         float slope = FloatPoint(m_current - m_previous).slopeAngleRadians();
</span><del>-        if (m_action == TraversalPointAtLength) {
-            float offset = m_desiredLength - m_totalLength;
-            m_current.move(offset * cosf(slope), offset * sinf(slope));
-        } else
</del><ins>+        float offset = m_desiredLength - m_totalLength;
+        m_current.move(offset * cosf(slope), offset * sinf(slope));
+
+        if (!m_isZeroVector &amp;&amp; !m_desiredLength)
+            m_isZeroVector = true;
+        else {
+            m_success = true;
</ins><span class="cx">             m_normalAngle = rad2deg(slope);
</span><del>-        m_success = true;
</del><ins>+        }
</ins><span class="cx">     }
</span><ins>+
</ins><span class="cx">     m_previous = m_current;
</span><ins>+    return m_success;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool PathTraversalState::appendPathElement(PathElementType type, const FloatPoint* points)
+{
+    switch (type) {
+    case PathElementMoveToPoint:
+        moveTo(points[0]);
+        break;
+    case PathElementAddLineToPoint:
+        lineTo(points[0]);
+        break;
+    case PathElementAddQuadCurveToPoint:
+        quadraticBezierTo(points[0], points[1]);
+        break;
+    case PathElementAddCurveToPoint:
+        cubicBezierTo(points[0], points[1], points[2]);
+        break;
+    case PathElementCloseSubpath:
+        closeSubpath();
+        break;
+    }
+    
+    return finalizeAppendPathElement();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><ins>+bool PathTraversalState::processPathElement(PathElementType type, const FloatPoint* points)
+{
+    if (m_success)
+        return true;
+
+    if (m_isZeroVector) {
+        PathTraversalState traversalState(*this);
+        m_success = traversalState.appendPathElement(type, points);
+        m_normalAngle = traversalState.m_normalAngle;
+        return m_success;
+    }
+
+    return appendPathElement(type, points);
+}
+
+}
+
</ins></span></pre></div>
<a id="trunkSourceWebCoreplatformgraphicsPathTraversalStateh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/platform/graphics/PathTraversalState.h (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/platform/graphics/PathTraversalState.h        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/platform/graphics/PathTraversalState.h        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) 2006, 2007 Eric Seidel &lt;eric@webkit.org&gt;
</span><ins>+ * Copyright (C) 2015 Apple Inc.  All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * Redistribution and use in source and binary forms, with or without
</span><span class="cx">  * modification, are permitted provided that the following conditions
</span><span class="lines">@@ -27,45 +28,60 @@
</span><span class="cx"> #define PathTraversalState_h
</span><span class="cx"> 
</span><span class="cx"> #include &quot;FloatPoint.h&quot;
</span><ins>+#include &quot;Path.h&quot;
</ins><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><span class="cx"> class PathTraversalState {
</span><span class="cx"> public:
</span><del>-    enum PathTraversalAction {
-        TraversalTotalLength,
-        TraversalPointAtLength,
-        TraversalSegmentAtLength,
-        TraversalNormalAngleAtLength
</del><ins>+    enum class Action {
+        TotalLength,
+        VectorAtLength,
+        SegmentAtLength,
</ins><span class="cx">     };
</span><span class="cx"> 
</span><del>-    PathTraversalState(PathTraversalAction);
</del><ins>+    PathTraversalState(Action, float desiredLength = 0);
</ins><span class="cx"> 
</span><del>-    float closeSubpath();
-    float moveTo(const FloatPoint&amp;);
-    float lineTo(const FloatPoint&amp;);
-    float quadraticBezierTo(const FloatPoint&amp; newControl, const FloatPoint&amp; newEnd);
-    float cubicBezierTo(const FloatPoint&amp; newControl1, const FloatPoint&amp; newControl2, const FloatPoint&amp; newEnd);
-    
-    void processSegment();
-
</del><span class="cx"> public:
</span><del>-    PathTraversalAction m_action;
-    bool m_success;
</del><ins>+    bool processPathElement(PathElementType, const FloatPoint*);
+    bool processPathElement(const PathElement* element) { return processPathElement(element-&gt;type, element-&gt;points); }
</ins><span class="cx"> 
</span><ins>+    Action action() const { return m_action; }
+    void setAction(Action action) { m_action = action; }
+    float desiredLength() const { return m_desiredLength; }
+    void setDesiredLength(float desiredLength) { m_desiredLength = desiredLength; }
+
+    // Traversing output -- should be read only
+    bool success() const { return m_success; }
+    float totalLength() const { return m_totalLength; }
+    FloatPoint current() const { return m_current; }
+    float normalAngle() const { return m_normalAngle; }
+
+private:
+    void closeSubpath();
+    void moveTo(const FloatPoint&amp;);
+    void lineTo(const FloatPoint&amp;);
+    void quadraticBezierTo(const FloatPoint&amp;, const FloatPoint&amp;);
+    void cubicBezierTo(const FloatPoint&amp;, const FloatPoint&amp;, const FloatPoint&amp;);
+
+    bool finalizeAppendPathElement();
+    bool appendPathElement(PathElementType, const FloatPoint*);
+
+private:
+    Action m_action;
+    bool m_success { false };
+
</ins><span class="cx">     FloatPoint m_current;
</span><span class="cx">     FloatPoint m_start;
</span><del>-    FloatPoint m_control1;
-    FloatPoint m_control2;
</del><span class="cx"> 
</span><del>-    float m_totalLength;
-    unsigned m_segmentIndex;
-    float m_desiredLength;
</del><ins>+    float m_totalLength { 0 };
+    float m_desiredLength { 0 };
</ins><span class="cx"> 
</span><span class="cx">     // For normal calculations
</span><span class="cx">     FloatPoint m_previous;
</span><del>-    float m_normalAngle; // degrees
-};    
</del><ins>+    float m_normalAngle { 0 }; // degrees
+    bool m_isZeroVector { false };
+};
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> #endif
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingsvgSVGTextChunkcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/svg/SVGTextChunk.cpp (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/svg/SVGTextChunk.cpp        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextChunk.cpp        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
</span><ins>+ * Copyright (C) 2015 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * This library is free software; you can redistribute it and/or
</span><span class="cx">  * modify it under the terms of the GNU Library General Public
</span><span class="lines">@@ -24,52 +25,96 @@
</span><span class="cx"> 
</span><span class="cx"> namespace WebCore {
</span><span class="cx"> 
</span><del>-SVGTextChunk::SVGTextChunk(unsigned chunkStyle, float desiredTextLength)
-    : m_chunkStyle(chunkStyle)
-    , m_desiredTextLength(desiredTextLength)
</del><ins>+SVGTextChunk::SVGTextChunk(const Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes, unsigned first, unsigned limit)
</ins><span class="cx"> {
</span><del>-}
</del><ins>+    ASSERT(first &lt; limit);
+    ASSERT(first &gt;= 0 &amp;&amp; limit &lt;= lineLayoutBoxes.size());
</ins><span class="cx"> 
</span><del>-void SVGTextChunk::calculateLength(float&amp; length, unsigned&amp; characters) const
-{
-    SVGTextFragment* lastFragment = 0;
</del><ins>+    const SVGInlineTextBox* box = lineLayoutBoxes[first];
+    const RenderStyle&amp; style = box-&gt;renderer().style();
+    const SVGRenderStyle&amp; svgStyle = style.svgStyle();
</ins><span class="cx"> 
</span><del>-    unsigned boxCount = m_boxes.size();
-    for (unsigned boxPosition = 0; boxPosition &lt; boxCount; ++boxPosition) {
-        SVGInlineTextBox* textBox = m_boxes.at(boxPosition);
-        Vector&lt;SVGTextFragment&gt;&amp; fragments = textBox-&gt;textFragments();
</del><ins>+    if (!style.isLeftToRightDirection())
+        m_chunkStyle |= SVGTextChunk::RightToLeftText;
</ins><span class="cx"> 
</span><del>-        unsigned size = fragments.size();
-        if (!size)
-            continue;
</del><ins>+    if (svgStyle.isVerticalWritingMode())
+        m_chunkStyle |= SVGTextChunk::VerticalText;
+    
+    switch (svgStyle.textAnchor()) {
+    case TA_START:
+        break;
+    case TA_MIDDLE:
+        m_chunkStyle |= MiddleAnchor;
+        break;
+    case TA_END:
+        m_chunkStyle |= EndAnchor;
+        break;
+    }
</ins><span class="cx"> 
</span><del>-        for (unsigned i = 0; i &lt; size; ++i) {
-            SVGTextFragment&amp; fragment = fragments.at(i);
-            characters += fragment.length;
</del><ins>+    if (auto* textContentElement = SVGTextContentElement::elementFromRenderer(box-&gt;renderer().parent())) {
+        SVGLengthContext lengthContext(textContentElement);
+        m_desiredTextLength = textContentElement-&gt;specifiedTextLength().value(lengthContext);
</ins><span class="cx"> 
</span><del>-            if (m_chunkStyle &amp; VerticalText)
-                length += fragment.height;
-            else
-                length += fragment.width;
</del><ins>+        switch (textContentElement-&gt;lengthAdjust()) {
+        case SVGLengthAdjustUnknown:
+            break;
+        case SVGLengthAdjustSpacing:
+            m_chunkStyle |= LengthAdjustSpacing;
+            break;
+        case SVGLengthAdjustSpacingAndGlyphs:
+            m_chunkStyle |= LengthAdjustSpacingAndGlyphs;
+            break;
+        }
+    }
</ins><span class="cx"> 
</span><del>-            if (!lastFragment) {
-                lastFragment = &amp;fragment;
-                continue;
-            }
</del><ins>+    for (unsigned i = first; i &lt; limit; ++i)
+        m_boxes.append(lineLayoutBoxes[i]);
+}
</ins><span class="cx"> 
</span><del>-            // Resepect gap between chunks.
-            if (m_chunkStyle &amp; VerticalText)
-                 length += fragment.y - (lastFragment-&gt;y + lastFragment-&gt;height);
-            else
-                 length += fragment.x - (lastFragment-&gt;x + lastFragment-&gt;width);
</del><ins>+unsigned SVGTextChunk::totalCharacters() const
+{
+    unsigned characters = 0;
+    for (auto* box : m_boxes) {
+        for (auto&amp; fragment : box-&gt;textFragments())
+            characters += fragment.length;
+    }
+    return characters;
+}
</ins><span class="cx"> 
</span><del>-            lastFragment = &amp;fragment;
</del><ins>+float SVGTextChunk::totalLength() const
+{
+    const SVGTextFragment* firstFragment = nullptr;
+    const SVGTextFragment* lastFragment = nullptr;
+
+    for (auto* box : m_boxes) {
+        auto&amp; fragments = box-&gt;textFragments();
+        if (fragments.size()) {
+            firstFragment = &amp;(*fragments.begin());
+            break;
</ins><span class="cx">         }
</span><span class="cx">     }
</span><ins>+
+    for (auto it = m_boxes.rbegin(), end = m_boxes.rend(); it != end; ++it) {
+        auto&amp; fragments = (*it)-&gt;textFragments();
+        if (fragments.size()) {
+            lastFragment = &amp;(*fragments.rbegin());
+            break;
+        }
+    }
+
+    ASSERT(!firstFragment == !lastFragment);
+    if (!firstFragment)
+        return 0;
+
+    if (m_chunkStyle &amp; VerticalText)
+        return (lastFragment-&gt;y + lastFragment-&gt;height) - firstFragment-&gt;y;
+
+    return (lastFragment-&gt;x + lastFragment-&gt;width) - firstFragment-&gt;x;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-float SVGTextChunk::calculateTextAnchorShift(float length) const
</del><ins>+float SVGTextChunk::totalAnchorShift() const
</ins><span class="cx"> {
</span><ins>+    float length = totalLength();
</ins><span class="cx">     if (m_chunkStyle &amp; MiddleAnchor)
</span><span class="cx">         return -length / 2;
</span><span class="cx">     if (m_chunkStyle &amp; EndAnchor)
</span><span class="lines">@@ -77,4 +122,88 @@
</span><span class="cx">     return m_chunkStyle &amp; RightToLeftText ? -length : 0;
</span><span class="cx"> }
</span><span class="cx"> 
</span><ins>+void SVGTextChunk::layout(HashMap&lt;SVGInlineTextBox*, AffineTransform&gt;&amp; textBoxTransformations) const
+{
+    if (hasDesiredTextLength()) {
+        if (hasLengthAdjustSpacing())
+            processTextLengthSpacingCorrection();
+        else {
+            ASSERT(hasLengthAdjustSpacingAndGlyphs());
+            buildBoxTransformations(textBoxTransformations);
+        }
+    }
+
+    if (hasTextAnchor())
+        processTextAnchorCorrection();
+}
+
+void SVGTextChunk::processTextLengthSpacingCorrection() const
+{
+    float textLengthShift = (desiredTextLength() - totalLength()) / totalCharacters();
+    bool isVerticalText = m_chunkStyle &amp; VerticalText;
+    unsigned atCharacter = 0;
+
+    for (auto* box : m_boxes) {
+        for (auto&amp; fragment : box-&gt;textFragments()) {
+            if (isVerticalText)
+                fragment.y += textLengthShift * atCharacter;
+            else
+                fragment.x += textLengthShift * atCharacter;
+            
+            atCharacter += fragment.length;
+        }
+    }
+}
+
+void SVGTextChunk::buildBoxTransformations(HashMap&lt;SVGInlineTextBox*, AffineTransform&gt;&amp; textBoxTransformations) const
+{
+    AffineTransform spacingAndGlyphsTransform;
+    bool foundFirstFragment = false;
+
+    for (auto* box : m_boxes) {
+        if (!foundFirstFragment) {
+            if (!boxSpacingAndGlyphsTransform(box, spacingAndGlyphsTransform))
+                continue;
+            foundFirstFragment = true;
+        }
+
+        textBoxTransformations.set(box, spacingAndGlyphsTransform);
+    }
+}
+
+bool SVGTextChunk::boxSpacingAndGlyphsTransform(const SVGInlineTextBox* box, AffineTransform&amp; spacingAndGlyphsTransform) const
+{
+    auto&amp; fragments = box-&gt;textFragments();
+    if (fragments.isEmpty())
+        return false;
+
+    const SVGTextFragment&amp; fragment = fragments.first();
+    float scale = desiredTextLength() / totalLength();
+
+    spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
+
+    if (m_chunkStyle &amp; VerticalText)
+        spacingAndGlyphsTransform.scaleNonUniform(1, scale);
+    else
+        spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
+
+    spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
+    return true;
+}
+
+void SVGTextChunk::processTextAnchorCorrection() const
+{
+    float textAnchorShift = totalAnchorShift();
+    bool isVerticalText = m_chunkStyle &amp; VerticalText;
+
+    for (auto* box : m_boxes) {
+        for (auto&amp; fragment : box-&gt;textFragments()) {
+            if (isVerticalText)
+                fragment.y += textAnchorShift;
+            else
+                fragment.x += textAnchorShift;
+        }
+    }
+}
+
</ins><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingsvgSVGTextChunkh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/svg/SVGTextChunk.h (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/svg/SVGTextChunk.h        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextChunk.h        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
</span><ins>+ * Copyright (C) 2015 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * This library is free software; you can redistribute it and/or
</span><span class="cx">  * modify it under the terms of the GNU Library General Public
</span><span class="lines">@@ -40,28 +41,34 @@
</span><span class="cx">         LengthAdjustSpacingAndGlyphs = 1 &lt;&lt; 6
</span><span class="cx">     };
</span><span class="cx"> 
</span><del>-    SVGTextChunk(unsigned chunkStyle, float desiredTextLength);
</del><ins>+    SVGTextChunk(const Vector&lt;SVGInlineTextBox*&gt;&amp;, unsigned first, unsigned limit);
</ins><span class="cx"> 
</span><del>-    void calculateLength(float&amp; length, unsigned&amp; characters) const;
-    float calculateTextAnchorShift(float length) const;
</del><ins>+    unsigned totalCharacters() const;
+    float totalLength() const;
+    float totalAnchorShift() const;
+    void layout(HashMap&lt;SVGInlineTextBox*, AffineTransform&gt;&amp;) const;
</ins><span class="cx"> 
</span><ins>+private:
+    void processTextAnchorCorrection() const;
+    void buildBoxTransformations(HashMap&lt;SVGInlineTextBox*, AffineTransform&gt;&amp;) const;
+    void processTextLengthSpacingCorrection() const;
+
</ins><span class="cx">     bool isVerticalText() const { return m_chunkStyle &amp; VerticalText; }
</span><span class="cx">     float desiredTextLength() const { return m_desiredTextLength; }
</span><span class="cx"> 
</span><del>-    Vector&lt;SVGInlineTextBox*&gt;&amp; boxes() { return m_boxes; }
-    const Vector&lt;SVGInlineTextBox*&gt;&amp; boxes() const { return m_boxes; }
-
</del><span class="cx">     bool hasDesiredTextLength() const { return m_desiredTextLength &gt; 0 &amp;&amp; ((m_chunkStyle &amp; LengthAdjustSpacing) || (m_chunkStyle &amp; LengthAdjustSpacingAndGlyphs)); }
</span><del>-    bool hasTextAnchor() const {  return m_chunkStyle &amp; RightToLeftText ? !(m_chunkStyle &amp; EndAnchor) : (m_chunkStyle &amp; MiddleAnchor) || (m_chunkStyle &amp; EndAnchor); }
</del><ins>+    bool hasTextAnchor() const {  return m_chunkStyle &amp; RightToLeftText ? !(m_chunkStyle &amp; EndAnchor) : (m_chunkStyle &amp; (MiddleAnchor | EndAnchor)); }
</ins><span class="cx">     bool hasLengthAdjustSpacing() const { return m_chunkStyle &amp; LengthAdjustSpacing; }
</span><span class="cx">     bool hasLengthAdjustSpacingAndGlyphs() const { return m_chunkStyle &amp; LengthAdjustSpacingAndGlyphs; }
</span><span class="cx"> 
</span><ins>+    bool boxSpacingAndGlyphsTransform(const SVGInlineTextBox*, AffineTransform&amp;) const;
+
</ins><span class="cx"> private:
</span><span class="cx">     // Contains all SVGInlineTextBoxes this chunk spans.
</span><span class="cx">     Vector&lt;SVGInlineTextBox*&gt; m_boxes;
</span><span class="cx"> 
</span><del>-    unsigned m_chunkStyle;
-    float m_desiredTextLength;
</del><ins>+    unsigned m_chunkStyle { DefaultStyle };
+    float m_desiredTextLength { 0 };
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingsvgSVGTextChunkBuildercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.cpp        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
</span><ins>+ * Copyright (C) 2015 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * This library is free software; you can redistribute it and/or
</span><span class="cx">  * modify it under the terms of the GNU Library General Public
</span><span class="lines">@@ -30,227 +31,71 @@
</span><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox, AffineTransform&amp; transform) const
</del><ins>+unsigned SVGTextChunkBuilder::totalCharacters() const
</ins><span class="cx"> {
</span><del>-    DEPRECATED_DEFINE_STATIC_LOCAL(const AffineTransform, s_identityTransform, ());
-    if (!m_textBoxTransformations.contains(textBox)) {
-        transform = s_identityTransform;
-        return;
-    }
-
-    transform = m_textBoxTransformations.get(textBox);
</del><ins>+    unsigned characters = 0;
+    for (const auto&amp; chunk : m_textChunks)
+        characters += chunk.totalCharacters();
+    return characters;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SVGTextChunkBuilder::buildTextChunks(Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes)
</del><ins>+float SVGTextChunkBuilder::totalLength() const
</ins><span class="cx"> {
</span><del>-    if (lineLayoutBoxes.isEmpty())
-        return;
-
-    bool foundStart = false;
-    unsigned lastChunkStartPosition = 0;
-    unsigned boxPosition = 0;
-    unsigned boxCount = lineLayoutBoxes.size();
-    for (; boxPosition &lt; boxCount; ++boxPosition) {
-        SVGInlineTextBox* textBox = lineLayoutBoxes[boxPosition];
-        if (!textBox-&gt;startsNewTextChunk())
-            continue;
-
-        if (!foundStart) {
-            lastChunkStartPosition = boxPosition;
-            foundStart = true;
-        } else {
-            ASSERT_WITH_SECURITY_IMPLICATION(boxPosition &gt; lastChunkStartPosition);
-            addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
-            lastChunkStartPosition = boxPosition;
-        }
-    }
-
-    if (!foundStart)
-        return;
-
-    if (boxPosition - lastChunkStartPosition &gt; 0)
-        addTextChunk(lineLayoutBoxes, lastChunkStartPosition, boxPosition - lastChunkStartPosition);
</del><ins>+    float length = 0;
+    for (const auto&amp; chunk : m_textChunks)
+        length += chunk.totalLength();
+    return length;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SVGTextChunkBuilder::layoutTextChunks(Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes)
</del><ins>+float SVGTextChunkBuilder::totalAnchorShift() const
</ins><span class="cx"> {
</span><del>-    buildTextChunks(lineLayoutBoxes);
-    if (m_textChunks.isEmpty())
-        return;
-
-    unsigned chunkCount = m_textChunks.size();
-    for (unsigned i = 0; i &lt; chunkCount; ++i)
-        processTextChunk(m_textChunks[i]);
-
-    m_textChunks.clear();
</del><ins>+    float anchorShift = 0;
+    for (const auto&amp; chunk : m_textChunks)
+        anchorShift += chunk.totalAnchorShift();
+    return anchorShift;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SVGTextChunkBuilder::addTextChunk(Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes, unsigned boxStart, unsigned boxCount)
</del><ins>+AffineTransform SVGTextChunkBuilder::transformationForTextBox(SVGInlineTextBox* textBox) const
</ins><span class="cx"> {
</span><del>-    SVGInlineTextBox* textBox = lineLayoutBoxes[boxStart];
-    ASSERT(textBox);
-
-    const RenderStyle&amp; style = textBox-&gt;renderer().style();
-
-    const SVGRenderStyle&amp; svgStyle = style.svgStyle();
-
-    // Build chunk style flags.
-    unsigned chunkStyle = SVGTextChunk::DefaultStyle;
-
-    // Handle 'direction' property.
-    if (!style.isLeftToRightDirection())
-        chunkStyle |= SVGTextChunk::RightToLeftText;
-
-    // Handle 'writing-mode' property.
-    if (svgStyle.isVerticalWritingMode())
-        chunkStyle |= SVGTextChunk::VerticalText;
-
-    // Handle 'text-anchor' property.
-    switch (svgStyle.textAnchor()) {
-    case TA_START:
-        break;
-    case TA_MIDDLE:
-        chunkStyle |= SVGTextChunk::MiddleAnchor;
-        break;
-    case TA_END:
-        chunkStyle |= SVGTextChunk::EndAnchor;
-        break;
-    };
-
-    // Handle 'lengthAdjust' property.
-    float desiredTextLength = 0;
-    if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(textBox-&gt;renderer().parent())) {
-        SVGLengthContext lengthContext(textContentElement);
-        desiredTextLength = textContentElement-&gt;specifiedTextLength().value(lengthContext);
-
-        switch (textContentElement-&gt;lengthAdjust()) {
-        case SVGLengthAdjustUnknown:
-            break;
-        case SVGLengthAdjustSpacing:
-            chunkStyle |= SVGTextChunk::LengthAdjustSpacing;
-            break;
-        case SVGLengthAdjustSpacingAndGlyphs:
-            chunkStyle |= SVGTextChunk::LengthAdjustSpacingAndGlyphs;
-            break;
-        };
-    }
-
-    SVGTextChunk chunk(chunkStyle, desiredTextLength);
-
-    Vector&lt;SVGInlineTextBox*&gt;&amp; boxes = chunk.boxes();
-    for (unsigned i = boxStart; i &lt; boxStart + boxCount; ++i)
-        boxes.append(lineLayoutBoxes[i]);
-
-    m_textChunks.append(chunk);
</del><ins>+    auto it = m_textBoxTransformations.find(textBox);
+    return it == m_textBoxTransformations.end() ? AffineTransform() : it-&gt;value;
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SVGTextChunkBuilder::processTextChunk(const SVGTextChunk&amp; chunk)
</del><ins>+void SVGTextChunkBuilder::buildTextChunks(const Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes)
</ins><span class="cx"> {
</span><del>-    bool processTextLength = chunk.hasDesiredTextLength();
-    bool processTextAnchor = chunk.hasTextAnchor();
-    if (!processTextAnchor &amp;&amp; !processTextLength)
</del><ins>+    if (lineLayoutBoxes.isEmpty())
</ins><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    const Vector&lt;SVGInlineTextBox*&gt;&amp; boxes = chunk.boxes();
-    unsigned boxCount = boxes.size();
-    if (!boxCount)
-        return;
</del><ins>+    unsigned limit = lineLayoutBoxes.size();
+    unsigned first = limit;
</ins><span class="cx"> 
</span><del>-    // Calculate absolute length of whole text chunk (starting from text box 'start', spanning 'length' text boxes).
-    float chunkLength = 0;
-    unsigned chunkCharacters = 0;
-    chunk.calculateLength(chunkLength, chunkCharacters);
</del><ins>+    for (unsigned i = 0; i &lt; limit; ++i) {
+        if (!lineLayoutBoxes[i]-&gt;startsNewTextChunk())
+            continue;
</ins><span class="cx"> 
</span><del>-    bool isVerticalText = chunk.isVerticalText();
-    if (processTextLength) {
-        if (chunk.hasLengthAdjustSpacing()) {
-            float textLengthShift = (chunk.desiredTextLength() - chunkLength) / chunkCharacters;
-            unsigned atCharacter = 0;
-            for (unsigned boxPosition = 0; boxPosition &lt; boxCount; ++boxPosition) {
-                Vector&lt;SVGTextFragment&gt;&amp; fragments = boxes[boxPosition]-&gt;textFragments();
-                if (fragments.isEmpty())
-                    continue;
-                processTextLengthSpacingCorrection(isVerticalText, textLengthShift, fragments, atCharacter);
-            }
-        } else {
-            ASSERT(chunk.hasLengthAdjustSpacingAndGlyphs());
-            float textLengthScale = chunk.desiredTextLength() / chunkLength;
-            AffineTransform spacingAndGlyphsTransform;
-
-            bool foundFirstFragment = false;
-            for (unsigned boxPosition = 0; boxPosition &lt; boxCount; ++boxPosition) {
-                SVGInlineTextBox* textBox = boxes[boxPosition];
-                Vector&lt;SVGTextFragment&gt;&amp; fragments = textBox-&gt;textFragments();
-                if (fragments.isEmpty())
-                    continue;
-
-                if (!foundFirstFragment) {
-                    foundFirstFragment = true;
-                    buildSpacingAndGlyphsTransform(isVerticalText, textLengthScale, fragments.first(), spacingAndGlyphsTransform);
-                }
-
-                m_textBoxTransformations.set(textBox, spacingAndGlyphsTransform);
-            }
</del><ins>+        if (first == limit)
+            first = i;
+        else {
+            ASSERT_WITH_SECURITY_IMPLICATION(first != i);
+            m_textChunks.append(SVGTextChunk(lineLayoutBoxes, first, i));
+            first = i;
</ins><span class="cx">         }
</span><span class="cx">     }
</span><span class="cx"> 
</span><del>-    if (!processTextAnchor)
-        return;
-
-    // If we previously applied a lengthAdjust=&quot;spacing&quot; correction, we have to recalculate the chunk length, to be able to apply the text-anchor shift.
-    if (processTextLength &amp;&amp; chunk.hasLengthAdjustSpacing()) {
-        chunkLength = 0;
-        chunkCharacters = 0;
-        chunk.calculateLength(chunkLength, chunkCharacters);
-    }
-
-    float textAnchorShift = chunk.calculateTextAnchorShift(chunkLength);
-    for (unsigned boxPosition = 0; boxPosition &lt; boxCount; ++boxPosition) {
-        Vector&lt;SVGTextFragment&gt;&amp; fragments = boxes[boxPosition]-&gt;textFragments();
-        if (fragments.isEmpty())
-            continue;
-        processTextAnchorCorrection(isVerticalText, textAnchorShift, fragments);
-    }
</del><ins>+    if (first != limit)
+        m_textChunks.append(SVGTextChunk(lineLayoutBoxes, first, limit));
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SVGTextChunkBuilder::processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector&lt;SVGTextFragment&gt;&amp; fragments, unsigned&amp; atCharacter)
</del><ins>+void SVGTextChunkBuilder::layoutTextChunks(const Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes)
</ins><span class="cx"> {
</span><del>-    unsigned fragmentCount = fragments.size();
-    for (unsigned i = 0; i &lt; fragmentCount; ++i) {
-        SVGTextFragment&amp; fragment = fragments[i];
</del><ins>+    buildTextChunks(lineLayoutBoxes);
+    if (m_textChunks.isEmpty())
+        return;
</ins><span class="cx"> 
</span><del>-        if (isVerticalText)
-            fragment.y += textLengthShift * atCharacter;
-        else
-            fragment.x += textLengthShift * atCharacter;
</del><ins>+    for (const auto&amp; chunk : m_textChunks)
+        chunk.layout(m_textBoxTransformations);
</ins><span class="cx"> 
</span><del>-        atCharacter += fragment.length;
-    }
</del><ins>+    m_textChunks.clear();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SVGTextChunkBuilder::processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector&lt;SVGTextFragment&gt;&amp; fragments)
-{
-    unsigned fragmentCount = fragments.size();
-    for (unsigned i = 0; i &lt; fragmentCount; ++i) {
-        SVGTextFragment&amp; fragment = fragments[i];
-
-        if (isVerticalText)
-            fragment.y += textAnchorShift;
-        else
-            fragment.x += textAnchorShift;
-    }
</del><span class="cx"> }
</span><del>-
-void SVGTextChunkBuilder::buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment&amp; fragment, AffineTransform&amp; spacingAndGlyphsTransform)
-{
-    spacingAndGlyphsTransform.translate(fragment.x, fragment.y);
-
-    if (isVerticalText)
-        spacingAndGlyphsTransform.scaleNonUniform(1, scale);
-    else
-        spacingAndGlyphsTransform.scaleNonUniform(scale, 1);
-
-    spacingAndGlyphsTransform.translate(-fragment.x, -fragment.y);
-}
-
-}
</del></span></pre></div>
<a id="trunkSourceWebCorerenderingsvgSVGTextChunkBuilderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextChunkBuilder.h        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -1,5 +1,6 @@
</span><span class="cx"> /*
</span><span class="cx">  * Copyright (C) Research In Motion Limited 2010. All rights reserved.
</span><ins>+ * Copyright (C) 2015 Apple Inc. All rights reserved.
</ins><span class="cx">  *
</span><span class="cx">  * This library is free software; you can redistribute it and/or
</span><span class="cx">  * modify it under the terms of the GNU Library General Public
</span><span class="lines">@@ -40,20 +41,15 @@
</span><span class="cx">     SVGTextChunkBuilder();
</span><span class="cx"> 
</span><span class="cx">     const Vector&lt;SVGTextChunk&gt;&amp; textChunks() const { return m_textChunks; }
</span><del>-    void transformationForTextBox(SVGInlineTextBox*, AffineTransform&amp;) const;
</del><ins>+    unsigned totalCharacters() const;
+    float totalLength() const;
+    float totalAnchorShift() const;
+    AffineTransform transformationForTextBox(SVGInlineTextBox*) const;
</ins><span class="cx"> 
</span><del>-    void buildTextChunks(Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes);
-    void layoutTextChunks(Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes);
</del><ins>+    void buildTextChunks(const Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes);
+    void layoutTextChunks(const Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes);
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><del>-    void addTextChunk(Vector&lt;SVGInlineTextBox*&gt;&amp; lineLayoutBoxes, unsigned boxPosition, unsigned boxCount);
-    void processTextChunk(const SVGTextChunk&amp;);
-
-    void processTextLengthSpacingCorrection(bool isVerticalText, float textLengthShift, Vector&lt;SVGTextFragment&gt;&amp;, unsigned&amp; atCharacter);
-    void processTextAnchorCorrection(bool isVerticalText, float textAnchorShift, Vector&lt;SVGTextFragment&gt;&amp;);
-    void buildSpacingAndGlyphsTransform(bool isVerticalText, float scale, const SVGTextFragment&amp;, AffineTransform&amp;);
-
-private:
</del><span class="cx">     Vector&lt;SVGTextChunk&gt; m_textChunks;
</span><span class="cx">     HashMap&lt;SVGInlineTextBox*, AffineTransform&gt; m_textBoxTransformations;
</span><span class="cx"> };
</span></span></pre></div>
<a id="trunkSourceWebCorerenderingsvgSVGTextLayoutEnginecpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/rendering/svg/SVGTextLayoutEngine.cpp        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -20,6 +20,7 @@
</span><span class="cx"> #include &quot;config.h&quot;
</span><span class="cx"> #include &quot;SVGTextLayoutEngine.h&quot;
</span><span class="cx"> 
</span><ins>+#include &quot;PathTraversalState.h&quot;
</ins><span class="cx"> #include &quot;RenderSVGTextPath.h&quot;
</span><span class="cx"> #include &quot;SVGElement.h&quot;
</span><span class="cx"> #include &quot;SVGInlineTextBox.h&quot;
</span><span class="lines">@@ -170,48 +171,32 @@
</span><span class="cx">     m_textPath = textPath.layoutPath();
</span><span class="cx">     if (m_textPath.isEmpty())
</span><span class="cx">         return;
</span><ins>+
</ins><span class="cx">     m_textPathStartOffset = textPath.startOffset();
</span><span class="cx">     m_textPathLength = m_textPath.length();
</span><span class="cx">     if (m_textPathStartOffset &gt; 0 &amp;&amp; m_textPathStartOffset &lt;= 1)
</span><span class="cx">         m_textPathStartOffset *= m_textPathLength;
</span><span class="cx"> 
</span><del>-    float totalLength = 0;
-    unsigned totalCharacters = 0;
-
</del><span class="cx">     lineLayout.m_chunkLayoutBuilder.buildTextChunks(lineLayout.m_lineLayoutBoxes);
</span><del>-    const Vector&lt;SVGTextChunk&gt;&amp; textChunks = lineLayout.m_chunkLayoutBuilder.textChunks();
</del><span class="cx"> 
</span><del>-    unsigned size = textChunks.size();
-    for (unsigned i = 0; i &lt; size; ++i) {
-        const SVGTextChunk&amp; chunk = textChunks.at(i);
-
-        float length = 0;
-        unsigned characters = 0;
-        chunk.calculateLength(length, characters);
-
-        // Handle text-anchor as additional start offset for text paths.
-        m_textPathStartOffset += chunk.calculateTextAnchorShift(length);
-
-        totalLength += length;
-        totalCharacters += characters;
-    }
-
</del><ins>+    // Handle text-anchor as additional start offset for text paths.
+    m_textPathStartOffset += lineLayout.m_chunkLayoutBuilder.totalAnchorShift();
</ins><span class="cx">     m_textPathCurrentOffset = m_textPathStartOffset;
</span><span class="cx"> 
</span><span class="cx">     // Eventually handle textLength adjustments.
</span><del>-    SVGLengthAdjustType lengthAdjust = SVGLengthAdjustUnknown;
-    float desiredTextLength = 0;
</del><ins>+    auto* textContentElement = SVGTextContentElement::elementFromRenderer(&amp;textPath);
+    if (!textContentElement)
+        return;
</ins><span class="cx"> 
</span><del>-    if (SVGTextContentElement* textContentElement = SVGTextContentElement::elementFromRenderer(&amp;textPath)) {
-        SVGLengthContext lengthContext(textContentElement);
-        lengthAdjust = textContentElement-&gt;lengthAdjust();
-        desiredTextLength = textContentElement-&gt;specifiedTextLength().value(lengthContext);
-    }
-
</del><ins>+    SVGLengthContext lengthContext(textContentElement);
+    float desiredTextLength = textContentElement-&gt;specifiedTextLength().value(lengthContext);
</ins><span class="cx">     if (!desiredTextLength)
</span><span class="cx">         return;
</span><span class="cx"> 
</span><del>-    if (lengthAdjust == SVGLengthAdjustSpacing)
</del><ins>+    float totalLength = lineLayout.m_chunkLayoutBuilder.totalLength();
+    unsigned totalCharacters = lineLayout.m_chunkLayoutBuilder.totalCharacters();
+
+    if (textContentElement-&gt;lengthAdjust() == SVGLengthAdjustSpacing)
</ins><span class="cx">         m_textPathSpacing = (desiredTextLength - totalLength) / totalCharacters;
</span><span class="cx">     else
</span><span class="cx">         m_textPathScaling = desiredTextLength / totalLength;
</span><span class="lines">@@ -290,7 +275,7 @@
</span><span class="cx"> 
</span><span class="cx">         unsigned fragmentCount = fragments.size();
</span><span class="cx">         for (unsigned i = 0; i &lt; fragmentCount; ++i) {
</span><del>-            m_chunkLayoutBuilder.transformationForTextBox(textBox, textBoxTransformation);
</del><ins>+            textBoxTransformation = m_chunkLayoutBuilder.transformationForTextBox(textBox);
</ins><span class="cx">             if (textBoxTransformation.isIdentity())
</span><span class="cx">                 continue;
</span><span class="cx">             ASSERT(fragments[i].lengthAdjustTransform.isIdentity());
</span><span class="lines">@@ -553,15 +538,16 @@
</span><span class="cx">             if (textPathOffset &gt; m_textPathLength)
</span><span class="cx">                 break;
</span><span class="cx"> 
</span><del>-            bool ok = false;
-            FloatPoint point = m_textPath.pointAtLength(textPathOffset, ok);
-            ASSERT(ok);
</del><ins>+            bool success = false;
+            auto traversalState(m_textPath.traversalStateAtLength(textPathOffset, success));
+            ASSERT(success);
</ins><span class="cx"> 
</span><ins>+            FloatPoint point = traversalState.current();
</ins><span class="cx">             x = point.x();
</span><span class="cx">             y = point.y();
</span><del>-            angle = m_textPath.normalAngleAtLength(textPathOffset, ok);
-            ASSERT(ok);
</del><span class="cx"> 
</span><ins>+            angle = traversalState.normalAngle();
+
</ins><span class="cx">             // For vertical text on path, the actual angle has to be rotated 90 degrees anti-clockwise, not the orientation angle!
</span><span class="cx">             if (m_isVerticalText)
</span><span class="cx">                 angle -= 90;
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGAnimateMotionElementcpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGAnimateMotionElement.cpp (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGAnimateMotionElement.cpp        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/svg/SVGAnimateMotionElement.cpp        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -24,6 +24,7 @@
</span><span class="cx"> 
</span><span class="cx"> #include &quot;AffineTransform.h&quot;
</span><span class="cx"> #include &quot;ElementIterator.h&quot;
</span><ins>+#include &quot;PathTraversalState.h&quot;
</ins><span class="cx"> #include &quot;RenderSVGResource.h&quot;
</span><span class="cx"> #include &quot;SVGImageElement.h&quot;
</span><span class="cx"> #include &quot;SVGMPathElement.h&quot;
</span><span class="lines">@@ -212,16 +213,19 @@
</span><span class="cx"> {
</span><span class="cx">     ASSERT(!m_animationPath.isEmpty());
</span><span class="cx"> 
</span><del>-    bool ok = false;
</del><ins>+    bool success = false;
</ins><span class="cx">     float positionOnPath = m_animationPath.length() * percentage;
</span><del>-    FloatPoint position = m_animationPath.pointAtLength(positionOnPath, ok);
-    if (!ok)
</del><ins>+    auto traversalState(m_animationPath.traversalStateAtLength(positionOnPath, success));
+    if (!success)
</ins><span class="cx">         return;
</span><ins>+
+    FloatPoint position = traversalState.current();
+    float angle = traversalState.normalAngle();
+
</ins><span class="cx">     transform-&gt;translate(position.x(), position.y());
</span><span class="cx">     RotateMode rotateMode = this-&gt;rotateMode();
</span><span class="cx">     if (rotateMode != RotateAuto &amp;&amp; rotateMode != RotateAutoReverse)
</span><span class="cx">         return;
</span><del>-    float angle = m_animationPath.normalAngleAtLength(positionOnPath, ok);
</del><span class="cx">     if (rotateMode == RotateAutoReverse)
</span><span class="cx">         angle += 180;
</span><span class="cx">     transform-&gt;rotate(angle);
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGPathTraversalStateBuildercpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.cpp        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -29,68 +29,57 @@
</span><span class="cx"> 
</span><span class="cx"> SVGPathTraversalStateBuilder::SVGPathTraversalStateBuilder()
</span><span class="cx">     : m_traversalState(0)
</span><ins>+    , m_segmentIndex(0)
</ins><span class="cx"> {
</span><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void SVGPathTraversalStateBuilder::moveTo(const FloatPoint&amp; targetPoint, bool, PathCoordinateMode)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_traversalState);
</span><del>-    m_traversalState-&gt;m_totalLength += m_traversalState-&gt;moveTo(targetPoint);
</del><ins>+    m_traversalState-&gt;processPathElement(PathElementMoveToPoint, &amp;targetPoint);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void SVGPathTraversalStateBuilder::lineTo(const FloatPoint&amp; targetPoint, PathCoordinateMode)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_traversalState);
</span><del>-    m_traversalState-&gt;m_totalLength += m_traversalState-&gt;lineTo(targetPoint);
</del><ins>+    m_traversalState-&gt;processPathElement(PathElementAddLineToPoint, &amp;targetPoint);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void SVGPathTraversalStateBuilder::curveToCubic(const FloatPoint&amp; point1, const FloatPoint&amp; point2, const FloatPoint&amp; targetPoint, PathCoordinateMode)
</span><span class="cx"> {
</span><ins>+    FloatPoint points[] = { point1, point2, targetPoint };
</ins><span class="cx">     ASSERT(m_traversalState);
</span><del>-    m_traversalState-&gt;m_totalLength += m_traversalState-&gt;cubicBezierTo(point1, point2, targetPoint);
</del><ins>+    m_traversalState-&gt;processPathElement(PathElementAddCurveToPoint, points);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void SVGPathTraversalStateBuilder::closePath()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_traversalState);
</span><del>-    m_traversalState-&gt;m_totalLength += m_traversalState-&gt;closeSubpath();
</del><ins>+    m_traversalState-&gt;processPathElement(PathElementCloseSubpath, nullptr);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> void SVGPathTraversalStateBuilder::setDesiredLength(float desiredLength)
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_traversalState);
</span><del>-    m_traversalState-&gt;m_desiredLength = desiredLength;
</del><ins>+    m_traversalState-&gt;setDesiredLength(desiredLength);
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> bool SVGPathTraversalStateBuilder::continueConsuming()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_traversalState);    
</span><del>-    m_traversalState-&gt;processSegment();
-    return !m_traversalState-&gt;m_success;
</del><ins>+    return !m_traversalState-&gt;success();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><del>-void SVGPathTraversalStateBuilder::incrementPathSegmentCount()
-{
-    ASSERT(m_traversalState);
-    ++m_traversalState-&gt;m_segmentIndex;
-}
-
-unsigned SVGPathTraversalStateBuilder::pathSegmentIndex()
-{
-    ASSERT(m_traversalState);
-    return m_traversalState-&gt;m_segmentIndex;
-}
-
</del><span class="cx"> float SVGPathTraversalStateBuilder::totalLength()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_traversalState);
</span><del>-    return m_traversalState-&gt;m_totalLength;
</del><ins>+    return m_traversalState-&gt;totalLength();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> SVGPoint SVGPathTraversalStateBuilder::currentPoint()
</span><span class="cx"> {
</span><span class="cx">     ASSERT(m_traversalState);
</span><del>-    return m_traversalState-&gt;m_current;
</del><ins>+    return m_traversalState-&gt;current();
</ins><span class="cx"> }
</span><span class="cx"> 
</span><span class="cx"> }
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGPathTraversalStateBuilderh"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.h (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.h        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/svg/SVGPathTraversalStateBuilder.h        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -32,15 +32,15 @@
</span><span class="cx"> public:
</span><span class="cx">     SVGPathTraversalStateBuilder();
</span><span class="cx"> 
</span><del>-    unsigned pathSegmentIndex();
</del><ins>+    unsigned pathSegmentIndex() { return m_segmentIndex; }
</ins><span class="cx">     float totalLength();
</span><span class="cx">     SVGPoint currentPoint();
</span><span class="cx"> 
</span><span class="cx">     void setCurrentTraversalState(PathTraversalState* traversalState) { m_traversalState = traversalState; }
</span><span class="cx">     void setDesiredLength(float);
</span><del>-    virtual void incrementPathSegmentCount() override;
</del><ins>+    virtual void incrementPathSegmentCount() override { ++m_segmentIndex; }
</ins><span class="cx">     virtual bool continueConsuming() override;
</span><del>-    virtual void cleanup() override { m_traversalState = 0; }
</del><ins>+    virtual void cleanup() override { m_traversalState = nullptr, m_segmentIndex = 0; }
</ins><span class="cx"> 
</span><span class="cx"> private:
</span><span class="cx">     // Used in UnalteredParsing/NormalizedParsing modes.
</span><span class="lines">@@ -59,6 +59,7 @@
</span><span class="cx">     virtual void arcTo(float, float, float, bool, bool, const FloatPoint&amp;, PathCoordinateMode) override { ASSERT_NOT_REACHED(); }
</span><span class="cx"> 
</span><span class="cx">     PathTraversalState* m_traversalState;
</span><ins>+    unsigned m_segmentIndex;
</ins><span class="cx"> };
</span><span class="cx"> 
</span><span class="cx"> } // namespace WebCore
</span></span></pre></div>
<a id="trunkSourceWebCoresvgSVGPathUtilitiescpp"></a>
<div class="modfile"><h4>Modified: trunk/Source/WebCore/svg/SVGPathUtilities.cpp (182827 => 182828)</h4>
<pre class="diff"><span>
<span class="info">--- trunk/Source/WebCore/svg/SVGPathUtilities.cpp        2015-04-15 00:49:03 UTC (rev 182827)
+++ trunk/Source/WebCore/svg/SVGPathUtilities.cpp        2015-04-15 01:34:25 UTC (rev 182828)
</span><span class="lines">@@ -285,7 +285,7 @@
</span><span class="cx">     if (stream-&gt;isEmpty())
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    PathTraversalState traversalState(PathTraversalState::TraversalSegmentAtLength);
</del><ins>+    PathTraversalState traversalState(PathTraversalState::Action::SegmentAtLength);
</ins><span class="cx">     SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, length);
</span><span class="cx"> 
</span><span class="cx">     auto source = std::make_unique&lt;SVGPathByteStreamSource&gt;(stream);
</span><span class="lines">@@ -302,7 +302,7 @@
</span><span class="cx">     if (stream-&gt;isEmpty())
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    PathTraversalState traversalState(PathTraversalState::TraversalTotalLength);
</del><ins>+    PathTraversalState traversalState(PathTraversalState::Action::TotalLength);
</ins><span class="cx">     SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, 0);
</span><span class="cx"> 
</span><span class="cx">     auto source = std::make_unique&lt;SVGPathByteStreamSource&gt;(stream);
</span><span class="lines">@@ -319,7 +319,7 @@
</span><span class="cx">     if (stream-&gt;isEmpty())
</span><span class="cx">         return false;
</span><span class="cx"> 
</span><del>-    PathTraversalState traversalState(PathTraversalState::TraversalPointAtLength);
</del><ins>+    PathTraversalState traversalState(PathTraversalState::Action::VectorAtLength);
</ins><span class="cx">     SVGPathTraversalStateBuilder* builder = globalSVGPathTraversalStateBuilder(traversalState, length);
</span><span class="cx"> 
</span><span class="cx">     auto source = std::make_unique&lt;SVGPathByteStreamSource&gt;(stream);
</span></span></pre>
</div>
</div>

</body>
</html>