Bug 1322537 - Increase tolerance when splitting Bezier curves to prevent hang. r=bas
authorKevin Hsieh <kevin.hsieh@ucla.edu>
Fri, 28 Jul 2017 23:57:04 -0700
changeset 371938 6df0dd7948ada7f73e7583d10d35778419bc038d
parent 371937 5566083502700db4fde066b0c299c4589087bca2
child 371939 3bcd8efdd376d4d3dfcc5dda64631f048417c661
push id47626
push userdholbert@mozilla.com
push dateMon, 31 Jul 2017 05:57:49 +0000
treeherderautoland@6df0dd7948ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbas
bugs1322537
milestone56.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1322537 - Increase tolerance when splitting Bezier curves to prevent hang. r=bas MozReview-Commit-ID: 3qhj9He65Bh
gfx/2d/Path.cpp
layout/svg/crashtests/1322537-1.html
layout/svg/crashtests/1322537-2.html
layout/svg/crashtests/crashtests.list
--- a/gfx/2d/Path.cpp
+++ b/gfx/2d/Path.cpp
@@ -253,32 +253,40 @@ FlattenBezierCurveSegment(const BezierCo
    * The basic premise is that for a small t the third order term in the
    * equation of a cubic bezier curve is insignificantly small. This can
    * then be approximated by a quadratic equation for which the maximum
    * difference from a linear approximation can be much more easily determined.
    */
   BezierControlPoints currentCP = aControlPoints;
 
   double t = 0;
+  double currentTolerance = aTolerance;
   while (t < 1.0) {
     PointD cp21 = currentCP.mCP2 - currentCP.mCP1;
     PointD cp31 = currentCP.mCP3 - currentCP.mCP1;
 
     /* To remove divisions and check for divide-by-zero, this is optimized from:
      * Float s3 = (cp31.x * cp21.y - cp31.y * cp21.x) / hypotf(cp21.x, cp21.y);
      * t = 2 * Float(sqrt(aTolerance / (3. * std::abs(s3))));
      */
     double cp21x31 = cp31.x * cp21.y - cp31.y * cp21.x;
     double h = hypot(cp21.x, cp21.y);
     if (cp21x31 * h == 0) {
       break;
     }
 
     double s3inv = h / cp21x31;
-    t = 2 * sqrt(aTolerance * std::abs(s3inv) / 3.);
+    t = 2 * sqrt(currentTolerance * std::abs(s3inv) / 3.);
+    currentTolerance *= 1 + aTolerance;
+    // Increase tolerance every iteration to prevent this loop from executing
+    // too many times. This approximates the length of large curves more
+    // roughly. In practice, aTolerance is the constant kFlatteningTolerance
+    // which has value 0.0001. With this value, it takes 6,932 splits to double
+    // currentTolerance (to 0.0002) and 23,028 splits to increase
+    // currentTolerance by an order of magnitude (to 0.001).
     if (t >= 1.0) {
       break;
     }
 
     SplitBezier(currentCP, nullptr, &currentCP, t);
 
     aSink->LineTo(currentCP.mCP1.ToPoint());
   }
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/1322537-1.html
@@ -0,0 +1,2 @@
+<svg>
+<animateMotion path='M8,0l69,97m45,-17592186044414A71,23 46,0,0 16382,98Q50,10 48,72T21,0Z'/>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/layout/svg/crashtests/1322537-2.html
@@ -0,0 +1,15 @@
+<!DOCTYPE html>
+<head>
+  <script>
+    function go() {
+      var path = document.getElementById("myPath");
+      var len = path.getTotalLength();
+      console.log(len);
+    }
+  </script>
+</head>
+<body onload="go()">
+  <svg>
+    <path id="myPath" d="M45,-17592186044414A71,23 46,0,0 16382,98"/>
+  </svg>
+</body>
--- a/layout/svg/crashtests/crashtests.list
+++ b/layout/svg/crashtests/crashtests.list
@@ -193,11 +193,13 @@ load 993443.svg
 load 1016145.svg
 load 1028512.svg
 load 1140080-1.svg
 load 1149542-1.svg
 load 1156581-1.svg
 load 1182496-1.html
 load 1209525-1.svg
 load 1223281-1.svg
+load 1322537-1.html
+load 1322537-2.html
 load 1348564.svg
 load conditional-outer-svg-nondirty-reflow-assert.xhtml
 load extref-test-1.xhtml