Bug 1322537 - Increase tolerance when splitting Bezier curves to prevent hang. r=bas draft
authorKevin Hsieh <kevin.hsieh@ucla.edu>
Fri, 28 Jul 2017 23:57:04 -0700
changeset 618009 63f37c20bec61aa21dbf6ff539a9b967a0703810
parent 617946 ec666e910442ae55686160c1bd0c93b00a08dead
child 639937 5410a55e04633433d94c73f73270aa9781355271
push id71185
push userbmo:kevin.hsieh@ucla.edu
push dateSat, 29 Jul 2017 07:00:44 +0000
reviewersbas
bugs1322537
milestone56.0a1
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