Bug 382721 - Part 4: Support dotted/dashed border-radiused corners. r=jrmuizel
☠☠ backed out by 22c98c018bef ☠ ☠
authorTooru Fujisawa <arai_a@mac.com>
Thu, 06 Aug 2015 11:42:09 +0900
changeset 376632 6f2840c13fb097e422df8b293cf6f6e590f4a6c6
parent 376631 e2881c49e123c204f835953d6c37f004f1549545
child 376633 e8d313145a591f5601a7b568194864b7dcddc11f
push id20634
push userbmo:mh+mozilla@glandium.org
push dateWed, 08 Jun 2016 12:25:06 +0000
reviewersjrmuizel
bugs382721
milestone50.0a1
Bug 382721 - Part 4: Support dotted/dashed border-radiused corners. r=jrmuizel
gfx/2d/BasePoint.h
gfx/2d/BezierUtils.cpp
gfx/2d/BezierUtils.h
gfx/2d/moz.build
layout/base/BorderCache.h
layout/base/DashedCornerFinder.cpp
layout/base/DashedCornerFinder.h
layout/base/DottedCornerFinder.cpp
layout/base/DottedCornerFinder.h
layout/base/RestyleManager.cpp
layout/base/TouchManager.cpp
layout/base/moz.build
layout/base/nsCSSRenderingBorders.cpp
layout/base/nsCSSRenderingBorders.h
--- a/gfx/2d/BasePoint.h
+++ b/gfx/2d/BasePoint.h
@@ -77,16 +77,20 @@ struct BasePoint {
   T DotProduct(const Sub& aPoint) const {
       return x * aPoint.x + y * aPoint.y;
   }
 
   T Length() const {
     return hypot(x, y);
   }
 
+  T LengthSquare() const {
+    return x * x + y * y;
+  }
+
   // Round() is *not* rounding to nearest integer if the values are negative.
   // They are always rounding as floor(n + 0.5).
   // See https://bugzilla.mozilla.org/show_bug.cgi?id=410748#c14
   Sub& Round() {
     x = Coord(floor(T(x) + T(0.5)));
     y = Coord(floor(T(y) + T(0.5)));
     return *static_cast<Sub*>(this);
   }
new file mode 100644
--- /dev/null
+++ b/gfx/2d/BezierUtils.cpp
@@ -0,0 +1,336 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "BezierUtils.h"
+
+#include "PathHelpers.h"
+
+namespace mozilla {
+namespace gfx {
+
+Point
+GetBezierPoint(const Bezier& aBezier, Float t)
+{
+  Float s = 1.0f - t;
+
+  return Point(
+    aBezier.mPoints[0].x * s * s * s +
+    3.0f * aBezier.mPoints[1].x * t * s * s +
+    3.0f * aBezier.mPoints[2].x * t * t * s +
+    aBezier.mPoints[3].x * t * t * t,
+    aBezier.mPoints[0].y * s * s * s +
+    3.0f * aBezier.mPoints[1].y * t * s * s +
+    3.0f * aBezier.mPoints[2].y * t * t * s +
+    aBezier.mPoints[3].y * t * t * t
+    );
+}
+
+Point
+GetBezierDifferential(const Bezier& aBezier, Float t)
+{
+  // Return P'(t).
+
+  Float s = 1.0f - t;
+
+  return Point(
+    -3.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s * s +
+             2.0f * (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * t * s +
+             (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t * t),
+    -3.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s * s +
+             2.0f * (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * t * s+
+             (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t * t)
+    );
+}
+
+Point
+GetBezierDifferential2(const Bezier& aBezier, Float t)
+{
+  // Return P''(t).
+
+  Float s = 1.0f - t;
+
+  return Point(
+    6.0f * ((aBezier.mPoints[0].x - aBezier.mPoints[1].x) * s -
+            (aBezier.mPoints[1].x - aBezier.mPoints[2].x) * (s - t) -
+            (aBezier.mPoints[2].x - aBezier.mPoints[3].x) * t),
+    6.0f * ((aBezier.mPoints[0].y - aBezier.mPoints[1].y) * s -
+            (aBezier.mPoints[1].y - aBezier.mPoints[2].y) * (s - t) -
+            (aBezier.mPoints[2].y - aBezier.mPoints[3].y) * t)
+    );
+}
+
+Float
+GetBezierLength(const Bezier& aBezier, Float a, Float b)
+{
+  if (a < 0.5f && b > 0.5f) {
+    // To increase the accuracy, split into two parts.
+    return GetBezierLength(aBezier, a, 0.5f) +
+           GetBezierLength(aBezier, 0.5f, b);
+  }
+
+  // Calculate length of simple bezier curve with Simpson's rule.
+  //            _
+  //           /  b
+  // length =  |    |P'(x)| dx
+  //          _/  a
+  //
+  //          b - a                   a + b
+  //        = ----- [ |P'(a)| + 4 |P'(-----)| + |P'(b)| ]
+  //            6                       2
+
+  Float fa = GetBezierDifferential(aBezier, a).Length();
+  Float fab = GetBezierDifferential(aBezier, (a + b) / 2.0f).Length();
+  Float fb = GetBezierDifferential(aBezier, b).Length();
+
+  return (b - a) / 6.0f * (fa + 4.0f * fab + fb);
+}
+
+static void
+SplitBezierA(Bezier* aSubBezier, const Bezier& aBezier, Float t)
+{
+  // Split bezier curve into [0,t] and [t,1] parts, and return [0,t] part.
+
+  Float s = 1.0f - t;
+
+  Point tmp1;
+  Point tmp2;
+
+  aSubBezier->mPoints[0] = aBezier.mPoints[0];
+
+  aSubBezier->mPoints[1] = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
+  tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
+  tmp2 = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
+
+  aSubBezier->mPoints[2] = aSubBezier->mPoints[1] * s + tmp1 * t;
+  tmp1 = tmp1 * s + tmp2 * t;
+
+  aSubBezier->mPoints[3] = aSubBezier->mPoints[2] * s + tmp1 * t;
+}
+
+static void
+SplitBezierB(Bezier* aSubBezier, const Bezier& aBezier, Float t)
+{
+  // Split bezier curve into [0,t] and [t,1] parts, and return [t,1] part.
+
+  Float s = 1.0f - t;
+
+  Point tmp1;
+  Point tmp2;
+
+  aSubBezier->mPoints[3] = aBezier.mPoints[3];
+
+  aSubBezier->mPoints[2] = aBezier.mPoints[2] * s + aBezier.mPoints[3] * t;
+  tmp1 = aBezier.mPoints[1] * s + aBezier.mPoints[2] * t;
+  tmp2 = aBezier.mPoints[0] * s + aBezier.mPoints[1] * t;
+
+  aSubBezier->mPoints[1] = tmp1 * s + aSubBezier->mPoints[2] * t;
+  tmp1 = tmp2 * s + tmp1 * t;
+
+  aSubBezier->mPoints[0] = tmp1 * s + aSubBezier->mPoints[1] * t;
+}
+
+void
+GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier, Float t1, Float t2)
+{
+  Bezier tmp;
+  SplitBezierB(&tmp, aBezier, t1);
+
+  Float range = 1.0f - t1;
+  if (range == 0.0f) {
+    *aSubBezier = tmp;
+  } else {
+    SplitBezierA(aSubBezier, tmp, (t2 - t1) / range);
+  }
+}
+
+static Point
+BisectBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+                         Float* aT)
+{
+  // Find a nearest point on bezier curve with Binary search.
+  // Called from FindBezierNearestPoint.
+
+  Float lower = 0.0f;
+  Float upper = 1.0f;
+  Float t;
+
+  Point P, lastP;
+  const size_t MAX_LOOP = 32;
+  const Float DIST_MARGIN = 0.1f;
+  const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
+  const Float DIFF = 0.0001f;
+  for (size_t i = 0; i < MAX_LOOP; i++) {
+    t = (upper + lower) / 2.0f;
+    P = GetBezierPoint(aBezier, t);
+
+    // Check if it converged.
+    if (i > 0 && (lastP - P).LengthSquare() < DIST_MARGIN_SQUARE) {
+      break;
+    }
+
+    Float distSquare = (P - aTarget).LengthSquare();
+    if ((GetBezierPoint(aBezier, t + DIFF) - aTarget).LengthSquare() <
+        distSquare) {
+      lower = t;
+    } else if ((GetBezierPoint(aBezier, t - DIFF) - aTarget).LengthSquare() <
+               distSquare) {
+      upper = t;
+    } else {
+      break;
+    }
+
+    lastP = P;
+  }
+
+  if (aT) {
+    *aT = t;
+  }
+
+  return P;
+}
+
+Point
+FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+                       Float aInitialT, Float* aT)
+{
+  // Find a nearest point on bezier curve with Newton's method.
+  // It converges within 4 iterations in most cases.
+  //
+  //                   f(t_n)
+  //  t_{n+1} = t_n - ---------
+  //                   f'(t_n)
+  //
+  //             d                     2
+  //     f(t) = ---- | P(t) - aTarget |
+  //             dt
+
+  Float t = aInitialT;
+  Point P;
+  Point lastP = GetBezierPoint(aBezier, t);
+
+  const size_t MAX_LOOP = 4;
+  const Float DIST_MARGIN = 0.1f;
+  const Float DIST_MARGIN_SQUARE = DIST_MARGIN * DIST_MARGIN;
+  for (size_t i = 0; i <= MAX_LOOP; i++) {
+    Point dP = GetBezierDifferential(aBezier, t);
+    Point ddP = GetBezierDifferential2(aBezier, t);
+    Float f = 2.0f * (lastP.DotProduct(dP) - aTarget.DotProduct(dP));
+    Float df = 2.0f * (dP.DotProduct(dP) + lastP.DotProduct(ddP) -
+                       aTarget.DotProduct(ddP));
+    t = t - f / df;
+    P = GetBezierPoint(aBezier, t);
+    if ((P - lastP).LengthSquare() < DIST_MARGIN_SQUARE) {
+      break;
+    }
+    lastP = P;
+
+    if (i == MAX_LOOP) {
+      // If aInitialT is too bad, it won't converge in a few iterations,
+      // fallback to binary search.
+      return BisectBezierNearestPoint(aBezier, aTarget, aT);
+    }
+  }
+
+  if (aT) {
+    *aT = t;
+  }
+
+  return P;
+}
+
+void
+GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
+                         const Point& aCornerPoint, const Size& aCornerSize)
+{
+  // Calculate bezier control points for elliptic arc.
+
+  const Float signsList[4][2] = {
+    { +1.0f, +1.0f },
+    { -1.0f, +1.0f },
+    { -1.0f, -1.0f },
+    { +1.0f, -1.0f }
+  };
+  const Float (& signs)[2] = signsList[aCorner];
+
+  aBezier->mPoints[0] = aCornerPoint;
+  aBezier->mPoints[0].x += signs[0] * aCornerSize.width;
+
+  aBezier->mPoints[1] = aBezier->mPoints[0];
+  aBezier->mPoints[1].x -= signs[0] * aCornerSize.width * kKappaFactor;
+
+  aBezier->mPoints[3] = aCornerPoint;
+  aBezier->mPoints[3].y += signs[1] * aCornerSize.height;
+
+  aBezier->mPoints[2] = aBezier->mPoints[3];
+  aBezier->mPoints[2].y -= signs[1] * aCornerSize.height * kKappaFactor;
+}
+
+Float
+GetQuarterEllipticArcLength(Float a, Float b)
+{
+  // Calculate the approximate length of a quarter elliptic arc formed by radii
+  // (a, b), by Ramanujan's approximation of the perimeter p of an ellipse.
+  //           _                                                     _
+  //          |                                      2                |
+  //          |                           3 * (a - b)                 |
+  //  p =  PI | (a + b) + ------------------------------------------- |
+  //          |                                 2                 2   |
+  //          |_           10 * (a + b) + sqrt(a  + 14 * a * b + b ) _|
+  //
+  //           _                                                            _
+  //          |                                         2                    |
+  //          |                              3 * (a - b)                     |
+  //    =  PI | (a + b) + -------------------------------------------------- |
+  //          |                                           2              2   |
+  //          |_           10 * (a + b) + sqrt(4 * (a + b)  - 3 * (a - b) ) _|
+  //
+  //           _                                          _
+  //          |                          2                 |
+  //          |                     3 * S                  |
+  //    =  PI | A + -------------------------------------- |
+  //          |                               2        2   |
+  //          |_           10 * A + sqrt(4 * A  - 3 * S ) _|
+  //
+  // where A = a + b, S = a - b
+
+  Float A = a + b, S = a - b;
+  Float A2 = A * A, S2 = S * S;
+  Float p = M_PI * (A + 3.0f * S2 / (10.0f * A + sqrt(4.0f * A2 - 3.0f * S2)));
+  return p / 4.0f;
+}
+
+Float
+CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
+                               const Point& origin, Float width, Float height)
+{
+  // Solve following equations with n and return smaller n.
+  //
+  // /  (x, y) = P + n * normal
+  // |
+  // <   _            _ 2   _            _ 2
+  // |  | x - origin.x |   | y - origin.y |
+  // |  | ------------ | + | ------------ | = 1
+  // \  |_   width    _|   |_   height   _|
+
+  Float a = (P.x - origin.x) / width;
+  Float b = normal.x / width;
+  Float c = (P.y - origin.y) / height;
+  Float d = normal.y / height;
+
+  Float A = b * b + d * d;
+  Float B = a * b + c * d;
+  Float C = a * a + c * c - 1;
+
+  Float S = sqrt(B * B - A * C);
+
+  Float n1 = (- B + S) / A;
+  Float n2 = (- B - S) / A;
+
+  return n1 < n2 ? n1 : n2;
+}
+
+} // namespace gfx
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/2d/BezierUtils.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BezierUtils_h_
+#define mozilla_BezierUtils_h_
+
+#include "mozilla/gfx/2D.h"
+#include "gfxRect.h"
+
+namespace mozilla {
+namespace gfx {
+
+// Control points for bezier curve
+//
+//                   mPoints[2]
+//                    +-----___---+ mPoints[3]
+//                      __--
+//                   _--
+//                  /
+//                /
+// mPoints[1] +  |
+//            | |
+//            ||
+//            ||
+//            |
+//            |
+//            |
+//            |
+// mPoints[0] +
+struct Bezier {
+  Point mPoints[4];
+};
+
+// Calculate a point or it's differential of a bezier curve formed by
+// aBezier and parameter t.
+//
+//   GetBezierPoint = P(t)
+//   GetBezierDifferential = P'(t)
+//   GetBezierDifferential2 = P''(t)
+//
+//                   mPoints[2]
+//                    +-----___---+ mPoints[3]
+//                      __--     P(1)
+//                   _--
+//                  +
+//                /  P(t)
+// mPoints[1] +  |
+//            | |
+//            ||
+//            ||
+//            |
+//            |
+//            |
+//            |
+// mPoints[0] + P(0)
+Point GetBezierPoint(const Bezier& aBezier, Float t);
+Point GetBezierDifferential(const Bezier& aBezier, Float t);
+Point GetBezierDifferential2(const Bezier& aBezier, Float t);
+
+// Calculate length of a simple bezier curve formed by aBezier and range [a, b].
+Float GetBezierLength(const Bezier& aBezier, Float a, Float b);
+
+// Split bezier curve formed by aBezier into [0,t1], [t1,t2], [t2,1] parts, and
+// stores control points for [t1,t2] to aSubBezier.
+//
+//                 ___---+
+//             __+-     P(1)
+//          _--   P(t2)
+//         -
+//       /  <-- aSubBezier
+//      |
+//     |
+//    +
+//    | P(t1)
+//   |
+//   |
+//   |
+//   |
+//   + P(0)
+void GetSubBezier(Bezier* aSubBezier, const Bezier& aBezier,
+                  Float t1, Float t2);
+
+// Find a nearest point on bezier curve formed by aBezier to a point aTarget.
+// aInitialT is a hint to find the parameter t for the nearest point.
+// If aT is non-null, parameter for the nearest point is stored to *aT.
+// This function expects a bezier curve to be an approximation of elliptic arc.
+// Otherwise it will return wrong point.
+//
+//  aTarget
+//    +            ___---+
+//             __--
+//          _--
+//         +
+//       /  nearest point = P(t = *aT)
+//      |
+//     |
+//    |
+//    + P(aInitialT)
+//   |
+//   |
+//   |
+//   |
+//   +
+Point FindBezierNearestPoint(const Bezier& aBezier, const Point& aTarget,
+                             Float aInitialT, Float* aT=nullptr);
+
+// Calculate control points for a bezier curve that is an approximation of
+// an elliptic arc.
+//
+//                                   aCornerSize.width
+//                                 |<----------------->|
+//                                 |                   |
+//                     aCornerPoint|      mPoints[2]   |
+//                    -------------+-------+-----___---+ mPoints[3]
+//                    ^            |         __--
+//                    |            |      _--
+//                    |            |     -
+//                    |            |   /
+// aCornerSize.height | mPoints[1] +  |
+//                    |            | |
+//                    |            ||
+//                    |            ||
+//                    |            |
+//                    |            |
+//                    |            |
+//                    v mPoints[0] |
+//                    -------------+
+void GetBezierPointsForCorner(Bezier* aBezier, mozilla::css::Corner aCorner,
+                              const Point& aCornerPoint,
+                              const Size& aCornerSize);
+
+// Calculate the approximate length of a quarter elliptic arc formed by radii
+// (a, b).
+//
+//                a
+//      |<----------------->|
+//      |                   |
+//   ---+-------------___---+
+//   ^  |         __--
+//   |  |      _--
+//   |  |     -
+//   |  |   /
+// b |  |  |
+//   |  | |
+//   |  ||
+//   |  ||
+//   |  |
+//   |  |
+//   |  |
+//   v  |
+//   ---+
+Float GetQuarterEllipticArcLength(Float a, Float b);
+
+// Calculate the distance between an elliptic arc formed by (origin, width,
+// height), and a point P, along a line formed by |P + n * normal|.
+// P should be outside of the ellipse, and the line should cross with the
+// ellipse twice at n > 0 points.
+//
+//                            width
+//                     |<----------------->|
+//              origin |                   |
+//          -----------+-------------___---+
+//          ^  normal  |         __--
+//          | P +->__  |      _--
+//          |        --__    -
+//          |          | --+
+//   height |          |  |
+//          |          | |
+//          |          ||
+//          |          ||
+//          |          |
+//          |          |
+//          |          |
+//          v          |
+//          -----------+
+Float CalculateDistanceToEllipticArc(const Point& P, const Point& normal,
+                                     const Point& origin,
+                                     Float width, Float height);
+
+} // namespace gfx
+} // namespace mozilla
+
+#endif /* mozilla_BezierUtils_h_ */
--- a/gfx/2d/moz.build
+++ b/gfx/2d/moz.build
@@ -12,16 +12,17 @@ EXPORTS.mozilla.gfx += [
     '2D.h',
     'BaseCoord.h',
     'BaseMargin.h',
     'BasePoint.h',
     'BasePoint3D.h',
     'BasePoint4D.h',
     'BaseRect.h',
     'BaseSize.h',
+    'BezierUtils.h',
     'Blur.h',
     'BorrowedContext.h',
     'Coord.h',
     'CriticalSection.h',
     'DataSurfaceHelpers.h',
     'DrawEventRecorder.h',
     'DrawTargetTiled.h',
     'Filters.h',
@@ -133,16 +134,17 @@ elif CONFIG['CPU_ARCH'].startswith('mips
         'BlurLS3.cpp',
     ]
     if CONFIG['MOZ_ENABLE_SKIA']:
         SOURCES += [
             'convolverLS3.cpp',
         ]
 
 UNIFIED_SOURCES += [
+    'BezierUtils.cpp',
     'Blur.cpp',
     'DataSourceSurface.cpp',
     'DataSurfaceHelpers.cpp',
     'DrawEventRecorder.cpp',
     'DrawingJob.cpp',
     'DrawTarget.cpp',
     'DrawTargetCairo.cpp',
     'DrawTargetCapture.cpp',
new file mode 100644
--- /dev/null
+++ b/layout/base/BorderCache.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_BorderCache_h_
+#define mozilla_BorderCache_h_
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/HashFunctions.h"
+#include "nsDataHashtable.h"
+#include "PLDHashTable.h"
+
+namespace mozilla {
+// Cache for best overlap and best dashLength.
+
+struct FourFloats
+{
+  typedef mozilla::gfx::Float Float;
+
+  Float n[4];
+
+  FourFloats()
+  {
+    n[0] = 0.0f;
+    n[1] = 0.0f;
+    n[2] = 0.0f;
+    n[3] = 0.0f;
+  }
+
+  FourFloats(Float a, Float b, Float c, Float d)
+  {
+    n[0] = a;
+    n[1] = b;
+    n[2] = c;
+    n[3] = d;
+  }
+
+  bool
+  operator==(const FourFloats& aOther) const
+  {
+    return n[0] == aOther.n[0] &&
+           n[1] == aOther.n[1] &&
+           n[2] == aOther.n[2] &&
+           n[3] == aOther.n[3];
+  }
+};
+
+class FourFloatsHashKey : public PLDHashEntryHdr
+{
+public:
+  typedef const FourFloats& KeyType;
+  typedef const FourFloats* KeyTypePointer;
+
+  explicit FourFloatsHashKey(KeyTypePointer aKey) : mValue(*aKey) {}
+  FourFloatsHashKey(const FourFloatsHashKey& aToCopy) : mValue(aToCopy.mValue) {}
+  ~FourFloatsHashKey() {}
+
+  KeyType GetKey() const { return mValue; }
+  bool KeyEquals(KeyTypePointer aKey) const { return *aKey == mValue; }
+
+  static KeyTypePointer KeyToPointer(KeyType aKey) { return &aKey; }
+  static PLDHashNumber HashKey(KeyTypePointer aKey)
+  {
+    return HashBytes(aKey->n, sizeof(mozilla::gfx::Float) * 4);
+  }
+  enum { ALLOW_MEMMOVE = true };
+
+private:
+  const FourFloats mValue;
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_BorderCache_h_ */
new file mode 100644
--- /dev/null
+++ b/layout/base/DashedCornerFinder.cpp
@@ -0,0 +1,427 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DashedCornerFinder.h"
+
+#include "mozilla/Move.h"
+#include "BorderCache.h"
+#include "BorderConsts.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+struct BestDashLength
+{
+  typedef mozilla::gfx::Float Float;
+
+  Float dashLength;
+  size_t count;
+
+  BestDashLength()
+   : dashLength(0.0f), count(0)
+  {}
+
+  BestDashLength(Float aDashLength, size_t aCount)
+   : dashLength(aDashLength), count(aCount)
+  {}
+};
+
+static const size_t DashedCornerCacheSize = 256;
+nsDataHashtable<FourFloatsHashKey, BestDashLength> DashedCornerCache;
+
+DashedCornerFinder::DashedCornerFinder(const Bezier& aOuterBezier,
+                                       const Bezier& aInnerBezier,
+                                       Float aBorderWidthH, Float aBorderWidthV,
+                                       const Size& aCornerDim)
+ : mOuterBezier(aOuterBezier),
+   mInnerBezier(aInnerBezier),
+   mLastOuterP(aOuterBezier.mPoints[0]), mLastInnerP(aInnerBezier.mPoints[0]),
+   mLastOuterT(0.0f), mLastInnerT(0.0f),
+   mBestDashLength(DOT_LENGTH * DASH_LENGTH),
+   mHasZeroBorderWidth(false), mHasMore(true),
+   mMaxCount(aCornerDim.width + aCornerDim.height),
+   mType(OTHER),
+   mI(0), mCount(0)
+{
+  NS_ASSERTION(aBorderWidthH > 0.0f || aBorderWidthV > 0.0f,
+               "At least one side should have non-zero width.");
+
+  DetermineType(aBorderWidthH, aBorderWidthV);
+
+  Reset();
+}
+
+void
+DashedCornerFinder::DetermineType(Float aBorderWidthH, Float aBorderWidthV)
+{
+  if (aBorderWidthH < aBorderWidthV) {
+    // Always draw from wider side to thinner side.
+    Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]);
+    Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]);
+    Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]);
+    Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]);
+    mLastOuterP = mOuterBezier.mPoints[0];
+    mLastInnerP = mInnerBezier.mPoints[0];
+  }
+
+  // See the comment at mType declaration for each condition.
+
+  Float borderRadiusA = fabs(mOuterBezier.mPoints[0].x -
+                             mOuterBezier.mPoints[3].x);
+  Float borderRadiusB = fabs(mOuterBezier.mPoints[0].y -
+                             mOuterBezier.mPoints[3].y);
+  if (aBorderWidthH == aBorderWidthV &&
+      borderRadiusA == borderRadiusB &&
+      borderRadiusA > aBorderWidthH * 2.0f) {
+    Float curveHeight = borderRadiusA - aBorderWidthH / 2.0;
+
+    mType = PERFECT;
+    Float borderLength = M_PI * curveHeight / 2.0f;
+
+    Float dashWidth = aBorderWidthH * DOT_LENGTH * DASH_LENGTH;
+    size_t count = ceil(borderLength / dashWidth);
+    if (count % 2) {
+      count++;
+    }
+    mCount = count / 2 + 1;
+    mBestDashLength = borderLength / (aBorderWidthH * count);
+  }
+
+  Float minBorderWidth = std::min(aBorderWidthH, aBorderWidthV);
+  if (minBorderWidth == 0.0f) {
+    mHasZeroBorderWidth = true;
+  }
+
+  if (mType == OTHER && !mHasZeroBorderWidth) {
+    Float minBorderRadius = std::min(borderRadiusA, borderRadiusB);
+    Float maxBorderRadius = std::max(borderRadiusA, borderRadiusB);
+    Float maxBorderWidth = std::max(aBorderWidthH, aBorderWidthV);
+
+    FindBestDashLength(minBorderWidth, maxBorderWidth,
+                       minBorderRadius, maxBorderRadius);
+  }
+}
+
+bool
+DashedCornerFinder::HasMore(void) const
+{
+  if (mHasZeroBorderWidth) {
+    return mI < mMaxCount && mHasMore;
+  }
+
+  return mI < mCount;
+}
+
+DashedCornerFinder::Result
+DashedCornerFinder::Next(void)
+{
+  Float lastOuterT, lastInnerT, outerT, innerT;
+
+  if (mI == 0) {
+    lastOuterT = 0.0f;
+    lastInnerT = 0.0f;
+  } else {
+    if (mType == PERFECT) {
+      lastOuterT = lastInnerT = (mI * 2.0f - 0.5f) / ((mCount - 1) * 2.0f);
+    } else {
+      Float last2OuterT = mLastOuterT;
+      Float last2InnerT = mLastInnerT;
+
+      (void)FindNext(mBestDashLength);
+
+      //
+      //          mLastOuterT   lastOuterT
+      //                    |   |
+      //                    v   v
+      //            +---+---+---+---+ <- last2OuterT
+      //            |   |###|###|   |
+      //            |   |###|###|   |
+      //            |   |###|###|   |
+      //            +---+---+---+---+ <- last2InnerT
+      //                    ^   ^
+      //                    |   |
+      //          mLastInnerT   lastInnerT
+      lastOuterT = (mLastOuterT + last2OuterT) / 2.0f;
+      lastInnerT = (mLastInnerT + last2InnerT) / 2.0f;
+    }
+  }
+
+  if ((!mHasZeroBorderWidth && mI == mCount - 1) ||
+      (mHasZeroBorderWidth && !mHasMore)) {
+    outerT = 1.0f;
+    innerT = 1.0f;
+  } else {
+    if (mType == PERFECT) {
+      outerT = innerT = (mI * 2.0f + 0.5f) / ((mCount - 1) * 2.0f);
+    } else {
+      Float last2OuterT = mLastOuterT;
+      Float last2InnerT = mLastInnerT;
+
+      (void)FindNext(mBestDashLength);
+
+      //
+      //               outerT   last2OuterT
+      //                    |   |
+      //                    v   v
+      // mLastOuterT -> +---+---+---+---+
+      //                |   |###|###|   |
+      //                |   |###|###|   |
+      //                |   |###|###|   |
+      // mLastInnerT -> +---+---+---+---+
+      //                    ^   ^
+      //                    |   |
+      //               innerT   last2InnerT
+      outerT = (mLastOuterT + last2OuterT) / 2.0f;
+      innerT = (mLastInnerT + last2InnerT) / 2.0f;
+    }
+  }
+
+  mI++;
+
+  Bezier outerSectionBezier;
+  Bezier innerSectionBezier;
+  GetSubBezier(&outerSectionBezier, mOuterBezier, lastOuterT, outerT);
+  GetSubBezier(&innerSectionBezier, mInnerBezier, lastInnerT, innerT);
+  return DashedCornerFinder::Result(outerSectionBezier, innerSectionBezier);
+}
+
+void
+DashedCornerFinder::Reset(void)
+{
+  mLastOuterP = mOuterBezier.mPoints[0];
+  mLastInnerP = mInnerBezier.mPoints[0];
+  mLastOuterT = 0.0f;
+  mLastInnerT = 0.0f;
+  mHasMore = true;
+}
+
+Float
+DashedCornerFinder::FindNext(Float dashLength)
+{
+  Float upper = 1.0f;
+  Float lower = mLastOuterT;
+
+  Point OuterP, InnerP;
+  // Start from upper bound to check if this is the last segment.
+  Float outerT = upper;
+  Float innerT;
+  Float W = 0.0f;
+  Float L = 0.0f;
+
+  const Float LENGTH_MARGIN = 0.1f;
+  for (size_t i = 0; i < MAX_LOOP; i++) {
+    OuterP = GetBezierPoint(mOuterBezier, outerT);
+    InnerP = FindBezierNearestPoint(mInnerBezier, OuterP, outerT, &innerT);
+
+    // Calculate approximate dash length.
+    //
+    //   W = (W1 + W2) / 2
+    //   L = (OuterL + InnerL) / 2
+    //   dashLength = L / W
+    //
+    //              ____----+----____
+    // OuterP ___---        |         ---___    mLastOuterP
+    //    +---              |               ---+
+    //    |                  |                 |
+    //    |                  |                 |
+    //     |               W |              W1 |
+    //     |                  |                |
+    //  W2 |                  |                |
+    //     |                  |    ______------+
+    //     |              ____+----             mLastInnerP
+    //      |       ___---
+    //      |  __---
+    //      +--
+    //    InnerP
+    //                     OuterL
+    //              ____---------____
+    // OuterP ___---                  ---___    mLastOuterP
+    //    +---                              ---+
+    //    |                  L                 |
+    //    |            ___----------______     |
+    //     |      __---                   -----+
+    //     |  __--                             |
+    //     +--                                 |
+    //     |                InnerL ______------+
+    //     |              ____-----             mLastInnerP
+    //      |       ___---
+    //      |  __---
+    //      +--
+    //    InnerP
+    Float W1 = (mLastOuterP - mLastInnerP).Length();
+    Float W2 = (OuterP - InnerP).Length();
+    Float OuterL = GetBezierLength(mOuterBezier, mLastOuterT, outerT);
+    Float InnerL = GetBezierLength(mInnerBezier, mLastInnerT, innerT);
+    W = (W1 + W2) / 2.0f;
+    L = (OuterL + InnerL) / 2.0f;
+    if (L > W * dashLength + LENGTH_MARGIN) {
+      if (i > 0) {
+        upper = outerT;
+      }
+    } else if (L < W * dashLength - LENGTH_MARGIN) {
+      if (i == 0) {
+        // This is the last segment with shorter dashLength.
+        mHasMore = false;
+        break;
+      }
+      lower = outerT;
+    } else {
+      break;
+    }
+
+    outerT = (upper + lower) / 2.0f;
+  }
+
+  mLastOuterP = OuterP;
+  mLastInnerP = InnerP;
+  mLastOuterT = outerT;
+  mLastInnerT = innerT;
+
+  if (W == 0.0f) {
+    return 1.0f;
+  }
+
+  return L / W;
+}
+
+void
+DashedCornerFinder::FindBestDashLength(Float aMinBorderWidth,
+                                       Float aMaxBorderWidth,
+                                       Float aMinBorderRadius,
+                                       Float aMaxBorderRadius)
+{
+  // If dashLength is not calculateable, find it with binary search,
+  // such that there exists i that OuterP_i == OuterP_n and
+  // InnerP_i == InnerP_n with given dashLength.
+
+  FourFloats key(aMinBorderWidth, aMaxBorderWidth,
+                 aMinBorderRadius, aMaxBorderRadius);
+  BestDashLength best;
+  if (DashedCornerCache.Get(key, &best)) {
+    mCount = best.count;
+    mBestDashLength = best.dashLength;
+    return;
+  }
+
+  Float lower = 1.0f;
+  Float upper = DOT_LENGTH * DASH_LENGTH;
+  Float dashLength = upper;
+  size_t targetCount = 0;
+
+  const Float LENGTH_MARGIN = 0.1f;
+  for (size_t j = 0; j < MAX_LOOP; j++) {
+    size_t count;
+    Float actualDashLength;
+    if (!GetCountAndLastDashLength(dashLength, &count, &actualDashLength)) {
+      if (j == 0) {
+        mCount = mMaxCount;
+        break;
+      }
+    }
+
+    if (j == 0) {
+      if (count == 1) {
+        // If only 1 segment fits, fill entire region
+        //
+        //   count = 1
+        //   mCount = 1
+        //   |   1   |
+        //   +---+---+
+        //   |###|###|
+        //   |###|###|
+        //   |###|###|
+        //   +---+---+
+        //       1
+        mCount = 1;
+        break;
+      }
+
+      // targetCount should be 2n.
+      //
+      //   targetCount = 2
+      //   mCount = 2
+      //   |   1   |   2   |
+      //   +---+---+---+---+
+      //   |###|   |   |###|
+      //   |###|   |   |###|
+      //   |###|   |   |###|
+      //   +---+---+---+---+
+      //     1           2
+      //
+      //   targetCount = 6
+      //   mCount = 4
+      //   |   1   |   2   |   3   |   4   |   5   |   6   |
+      //   +---+---+---+---+---+---+---+---+---+---+---+---+
+      //   |###|   |   |###|###|   |   |###|###|   |   |###|
+      //   |###|   |   |###|###|   |   |###|###|   |   |###|
+      //   |###|   |   |###|###|   |   |###|###|   |   |###|
+      //   +---+---+---+---+---+---+---+---+---+---+---+---+
+      //     1             2               3             4
+      if (count % 2) {
+        targetCount = count + 1;
+      } else {
+        targetCount = count;
+      }
+
+      mCount = targetCount / 2 + 1;
+    }
+
+    if (count == targetCount) {
+      mBestDashLength = dashLength;
+
+      // actualDashLength won't be greater than dashLength.
+      if (actualDashLength > dashLength - LENGTH_MARGIN) {
+        break;
+      }
+
+      // We started from upper bound, no need to update range when j == 0.
+      if (j > 0) {
+        upper = dashLength;
+      }
+    } else {
+      // |j == 0 && count != targetCount| means that |targetCount = count + 1|,
+      // and we started from upper bound, no need to update range when j == 0.
+      if (j > 0) {
+        if (count > targetCount) {
+          lower = dashLength;
+        } else {
+          upper = dashLength;
+        }
+      }
+    }
+
+    dashLength = (upper + lower) / 2.0f;
+  }
+
+  if (DashedCornerCache.Count() > DashedCornerCacheSize) {
+    DashedCornerCache.Clear();
+  }
+  DashedCornerCache.Put(key, BestDashLength(mBestDashLength, mCount));
+}
+
+bool
+DashedCornerFinder::GetCountAndLastDashLength(Float aDashLength,
+                                              size_t* aCount,
+                                              Float* aActualDashLength)
+{
+  // Return the number of segments and the last segment's dashLength for
+  // the given dashLength.
+
+  Reset();
+
+  for (size_t i = 0; i < mMaxCount; i++) {
+    Float actualDashLength = FindNext(aDashLength);
+    if (mLastOuterT >= 1.0f) {
+      *aCount = i + 1;
+      *aActualDashLength = actualDashLength;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/base/DashedCornerFinder.h
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DashedCornerFinder_h_
+#define mozilla_DashedCornerFinder_h_
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BezierUtils.h"
+
+namespace mozilla {
+
+// Calculate {OuterT_i, InnerT_i} for each 1 < i < n, that
+//   (OuterL_i + InnerL_i) / 2 == dashLength * (W_i + W_{i-1}) / 2
+// where
+//   OuterP_i: OuterCurve(OuterT_i)
+//   InnerP_i: InnerCurve(OuterT_i)
+//   OuterL_i: Elliptic arc length between OuterP_i - OuterP_{i-1}
+//   InnerL_i: Elliptic arc length between InnerP_i - InnerP_{i-1}
+//   W_i = |OuterP_i - InnerP_i|
+//   1.0 < dashLength < 3.0
+//
+//                                         OuterP_1          OuterP_0
+//                                         _+__-----------+ OuterCurve
+//                          OuterP_2 __---- |   OuterL_1  |
+//                             __+---       |             |
+//                        __---  | OuterL_2 |             |
+//            OuterP_3 _--       |           | W_1        | W_0
+//                   _+           |          |            |
+//                  /  \       W_2 |         |            |
+//                 /    \          |          | InnerL_1  |
+//                |      \          | InnerL_2|____-------+ InnerCurve
+//                |       \          |____----+          InnerP_0
+//               |     .   \    __---+       InnerP_1
+//               |          \ /     InnerP_2
+//              |     .     /+ InnerP_3
+//              |          |
+//              |    .    |
+//              |         |
+//              |        |
+//              |        |
+// OuterP_{n-1} +--------+ InnerP_{n-1}
+//              |        |
+//              |        |
+//              |        |
+//              |        |
+//              |        |
+//     OuterP_n +--------+ InnerP_n
+//
+// Returns region with [OuterCurve((OuterT_{2j} + OuterT_{2j-1}) / 2),
+//                      OuterCurve((OuterT_{2j} + OuterT_{2j-1}) / 2),
+//                      InnerCurve((OuterT_{2j} + OuterT_{2j+1}) / 2),
+//                      InnerCurve((OuterT_{2j} + OuterT_{2j+1}) / 2)],
+// to start and end with half segment.
+//
+//                                     _+__----+------+ OuterCurve
+//                               _+---- |      |######|
+//                         __+---#|     |      |######|
+//                    _+---##|####|     |      |######|
+//                 _-- |#####|#####|     |      |#####|
+//               _+     |#####|#####|    |      |#####|
+//              /  \     |#####|####|    |      |#####|
+//             /    \     |####|#####|    |     |#####|
+//            |      \     |####|####|    |____-+-----+ InnerCurve
+//            |       \     |####|____+---+
+//           |     .   \   __+---+
+//           |          \ /
+//          |     .     /+
+//          |          |
+//          |    .    |
+//          |         |
+//          |        |
+//          |        |
+//          +--------+
+//          |        |
+//          |        |
+//          +--------+
+//          |########|
+//          |########|
+//          +--------+
+
+class DashedCornerFinder
+{
+  typedef mozilla::gfx::Bezier Bezier;
+  typedef mozilla::gfx::Float Float;
+  typedef mozilla::gfx::Point Point;
+  typedef mozilla::gfx::Size Size;
+
+public:
+  struct Result
+  {
+    // Control points for the outer curve and the inner curve.
+    //
+    //   outerSectionBezier
+    //        |
+    //        v     _+ 3
+    //        ___---#|
+    //  0 +---#######|
+    //    |###########|
+    //     |###########|
+    //      |##########|
+    //       |##########|
+    //        |#########|
+    //         |#####____+ 3
+    //        0 +----
+    //              ^
+    //              |
+    //   innerSectionBezier
+    Bezier outerSectionBezier;
+    Bezier innerSectionBezier;
+
+    Result(const Bezier& aOuterSectionBezier,
+           const Bezier& aInnerSectionBezier)
+     : outerSectionBezier(aOuterSectionBezier),
+       innerSectionBezier(aInnerSectionBezier)
+    {}
+  };
+
+  //                       aCornerDim.width
+  //                     |<----------------->|
+  //                     |                   |
+  //                   --+-------------___---+--
+  //                   ^ |         __--      | ^
+  //                   | |       _-          | |
+  //                   | |     /             | | aBorderWidthH
+  //                   | |   /               | |
+  //                   | |  |                | v
+  //                   | | |             __--+--
+  // aCornerDim.height | ||            _-
+  //                   | ||           /
+  //                   | |           /
+  //                   | |          |
+  //                   | |          |
+  //                   | |         |
+  //                   | |         |
+  //                   v |         |
+  //                   --+---------+
+  //                     |         |
+  //                     |<------->|
+  //                     aBorderWidthV
+  DashedCornerFinder(const Bezier& aOuterBezier, const Bezier& aInnerBezier,
+                     Float aBorderWidthH, Float aBorderWidthV,
+                     const Size& aCornerDim);
+
+  bool HasMore(void) const;
+  Result Next(void);
+
+private:
+  static const size_t MAX_LOOP = 32;
+
+  // Bezier control points for the outer curve and the inner curve.
+  //
+  //               ___---+ outer curve
+  //           __--      |
+  //         _-          |
+  //       /             |
+  //     /               |
+  //    |                |
+  //   |             __--+ inner curve
+  //  |            _-
+  //  |           /
+  // |           /
+  // |          |
+  // |          |
+  // |         |
+  // |         |
+  // |         |
+  // +---------+
+  Bezier mOuterBezier;
+  Bezier mInnerBezier;
+
+  Point mLastOuterP;
+  Point mLastInnerP;
+  Float mLastOuterT;
+  Float mLastInnerT;
+
+  // Length for each segment, ratio of the border width at that point.
+  Float mBestDashLength;
+
+  // If one of border-widths is 0, do not calculate mBestDashLength, and draw
+  // segments until it reaches the other side or exceeds mMaxCount.
+  bool mHasZeroBorderWidth;
+  bool mHasMore;
+
+  // The maximum number of segments.
+  size_t mMaxCount;
+
+  enum {
+    //                      radius.width
+    //                 |<----------------->|
+    //                 |                   |
+    //               --+-------------___---+--
+    //               ^ |         __--      | ^
+    //               | |       _-          | |
+    //               | |     /             + | top-width
+    //               | |   /               | |
+    //               | |  |                | v
+    //               | | |             __--+--
+    // radius.height | ||            _-
+    //               | ||           /
+    //               | |           /
+    //               | |          |
+    //               | |          |
+    //               | |         |
+    //               | |         |
+    //               v |         |
+    //               --+----+----+
+    //                 |         |
+    //                 |<------->|
+    //                  left-width
+
+    // * top-width == left-width
+    // * radius.width == radius.height
+    // * top-width < radius.width * 2
+    //
+    // Split the perfect circle's arc into 2n segments, each segment's length is
+    // top-width * dashLength. Then split the inner curve and the outer curve
+    // with same angles.
+    //
+    //                        radius.width
+    //                 |<---------------------->|
+    //                 |                        | v
+    //               --+------------------------+--
+    //               ^ |                        | | top-width / 2
+    //               | | perfect                | |
+    //               | | circle's         ___---+--
+    //               | | arc          __-+      | ^
+    //               | |  |         _-   |      |
+    // radius.height | |  |       +       |     +--
+    //               | |  |     /  \      |     |
+    //               | |  |    |     \     |    |
+    //               | |  |   |       \     |   |
+    //               | |  +->|          \   |   |
+    //               | |    +---__       \   |  |
+    //               | |    |     --__     \  | |
+    //               | |    |         ---__ \ | |
+    //               v |    |              --_\||
+    //               --+----+----+--------------+
+    //                 |    |    |
+    //                 |<-->|    |
+    //               left-width / 2
+    PERFECT,
+
+    // Other cases.
+    //
+    // Split the outer curve and the inner curve into 2n segments, each segment
+    // satisfies following:
+    //   (OuterL_i + InnerL_i) / 2 == dashLength * (W_i + W_{i-1}) / 2
+    OTHER
+  } mType;
+
+  size_t mI;
+  size_t mCount;
+
+  // Determine mType from parameters.
+  void DetermineType(Float aBorderWidthH, Float aBorderWidthV);
+
+  // Reset calculation.
+  void Reset(void);
+
+  // Find next segment.
+  Float FindNext(Float dashLength);
+
+  // Find mBestDashLength for parameters.
+  void FindBestDashLength(Float aMinBorderWidth, Float aMaxBorderWidth,
+                          Float aMinBorderRadius, Float aMaxBorderRadius);
+
+  // Fill corner with dashes with given dash length, and return the number of
+  // segments and last segment's dash length.
+  bool GetCountAndLastDashLength(Float aDashLength,
+                                 size_t* aCount, Float* aActualDashLength);
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_DashedCornerFinder_h_ */
new file mode 100644
--- /dev/null
+++ b/layout/base/DottedCornerFinder.cpp
@@ -0,0 +1,553 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+// vim:cindent:ts=2:et:sw=2:
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "DottedCornerFinder.h"
+
+#include "mozilla/Move.h"
+#include "BorderCache.h"
+#include "BorderConsts.h"
+
+namespace mozilla {
+
+using namespace gfx;
+
+static inline Float
+Square(Float x)
+{
+  return x * x;
+}
+
+static Point
+PointRotateCCW90(const Point& aP)
+{
+  return Point(aP.y, -aP.x);
+}
+
+struct BestOverlap
+{
+  Float overlap;
+  size_t count;
+
+  BestOverlap()
+   : overlap(0.0f), count(0)
+  {}
+
+  BestOverlap(Float aOverlap, size_t aCount)
+   : overlap(aOverlap), count(aCount)
+  {}
+};
+
+static const size_t DottedCornerCacheSize = 256;
+nsDataHashtable<FourFloatsHashKey, BestOverlap> DottedCornerCache;
+
+DottedCornerFinder::DottedCornerFinder(const Bezier& aOuterBezier,
+                                       const Bezier& aInnerBezier,
+                                       mozilla::css::Corner aCorner,
+                                       Float aBorderRadiusX,
+                                       Float aBorderRadiusY,
+                                       const Point& aC0, Float aR0,
+                                       const Point& aCn, Float aRn,
+                                       const Size& aCornerDim)
+ : mOuterBezier(aOuterBezier),
+   mInnerBezier(aInnerBezier),
+   mCorner(aCorner),
+   mNormalSign((aCorner == C_TL || aCorner == C_BR) ? -1.0f : 1.0f),
+   mC0(aC0), mCn(aCn),
+   mR0(aR0), mRn(aRn), mMaxR(std::max(aR0, aRn)),
+   mCurveOrigin(mC0.x, mCn.y),
+   mBestOverlap(0.0f),
+   mHasZeroBorderWidth(false), mHasMore(true),
+   mMaxCount(aCornerDim.width + aCornerDim.height),
+   mType(OTHER),
+   mI(0), mCount(0)
+{
+  NS_ASSERTION(mR0 > 0.0f || mRn > 0.0f,
+               "At least one side should have non-zero radius.");
+
+  mInnerWidth = fabs(mInnerBezier.mPoints[0].x - mInnerBezier.mPoints[3].x);
+  mInnerHeight = fabs(mInnerBezier.mPoints[0].y - mInnerBezier.mPoints[3].y);
+
+  DetermineType(aBorderRadiusX, aBorderRadiusY);
+
+  Reset();
+}
+
+static bool
+IsSingleCurve(Float aMinR, Float aMaxR,
+              Float aMinBorderRadius, Float aMaxBorderRadius)
+{
+  return aMinR > 0.0f &&
+         aMinBorderRadius > aMaxR * 4.0f &&
+         aMinBorderRadius / aMaxBorderRadius > 0.5f;
+}
+
+void
+DottedCornerFinder::DetermineType(Float aBorderRadiusX, Float aBorderRadiusY)
+{
+  // Calculate parameters for the center curve before swap.
+  Float centerCurveWidth = fabs(mC0.x - mCn.x);
+  Float centerCurveHeight = fabs(mC0.y - mCn.y);
+  Point cornerPoint(mCn.x, mC0.y);
+
+  bool swapped = false;
+  if (mR0 < mRn) {
+    // Always draw from wider side to thinner side.
+    Swap(mC0, mCn);
+    Swap(mR0, mRn);
+    Swap(mInnerBezier.mPoints[0], mInnerBezier.mPoints[3]);
+    Swap(mInnerBezier.mPoints[1], mInnerBezier.mPoints[2]);
+    Swap(mOuterBezier.mPoints[0], mOuterBezier.mPoints[3]);
+    Swap(mOuterBezier.mPoints[1], mOuterBezier.mPoints[2]);
+    mNormalSign = -mNormalSign;
+    swapped = true;
+  }
+
+  // See the comment at mType declaration for each condition.
+
+  Float minR = std::min(mR0, mRn);
+  Float minBorderRadius = std::min(aBorderRadiusX, aBorderRadiusY);
+  Float maxBorderRadius = std::max(aBorderRadiusX, aBorderRadiusY);
+  if (IsSingleCurve(minR, mMaxR, minBorderRadius, maxBorderRadius)) {
+    if (mR0 == mRn) {
+      Float borderLength;
+      if (minBorderRadius == maxBorderRadius) {
+        mType = PERFECT;
+        borderLength = M_PI * centerCurveHeight / 2.0f;
+
+        mCenterCurveR = centerCurveWidth;
+      } else {
+        mType = SINGLE_CURVE_AND_RADIUS;
+        borderLength = GetQuarterEllipticArcLength(centerCurveWidth,
+                                                   centerCurveHeight);
+      }
+
+      Float diameter = mR0 * 2.0f;
+      size_t count = round(borderLength / diameter);
+      if (count % 2) {
+        count++;
+      }
+      mCount = count / 2 - 1;
+      if (mCount > 0) {
+        mBestOverlap = 1.0f - borderLength / (diameter * count);
+      }
+    } else {
+      mType = SINGLE_CURVE;
+    }
+  }
+
+  if (mType == SINGLE_CURVE_AND_RADIUS || mType == SINGLE_CURVE) {
+    Size cornerSize(centerCurveWidth, centerCurveHeight);
+    GetBezierPointsForCorner(&mCenterBezier, mCorner,
+                             cornerPoint, cornerSize);
+    if (swapped) {
+      Swap(mCenterBezier.mPoints[0], mCenterBezier.mPoints[3]);
+      Swap(mCenterBezier.mPoints[1], mCenterBezier.mPoints[2]);
+    }
+  }
+
+  if (minR == 0.0f) {
+    mHasZeroBorderWidth = true;
+  }
+
+  if ((mType == SINGLE_CURVE || mType == OTHER) && !mHasZeroBorderWidth) {
+    FindBestOverlap(minR, minBorderRadius, maxBorderRadius);
+  }
+}
+
+
+bool
+DottedCornerFinder::HasMore(void) const
+{
+  if (mHasZeroBorderWidth) {
+    return mI < mMaxCount && mHasMore;
+  }
+
+  return mI < mCount;
+}
+
+DottedCornerFinder::Result
+DottedCornerFinder::Next(void)
+{
+  mI++;
+
+  if (mType == PERFECT) {
+    Float phi = mI * 4.0f * mR0 * (1 - mBestOverlap) / mCenterCurveR;
+    if (mCorner == C_TL) {
+      phi = -M_PI / 2.0f - phi;
+    } else if (mCorner == C_TR) {
+      phi = -M_PI / 2.0f + phi;
+    } else if (mCorner == C_BR) {
+      phi = M_PI / 2.0f - phi;
+    } else {
+      phi = M_PI / 2.0f + phi;
+    }
+
+    Point C(mCurveOrigin.x + mCenterCurveR * cos(phi),
+            mCurveOrigin.y + mCenterCurveR * sin(phi));
+    return DottedCornerFinder::Result(C, mR0);
+  }
+
+  // Find unfilled and filled circles.
+  (void)FindNext(mBestOverlap);
+  (void)FindNext(mBestOverlap);
+  return Result(mLastC, mLastR);
+}
+
+void
+DottedCornerFinder::Reset(void)
+{
+  mLastC = mC0;
+  mLastR = mR0;
+  mLastT = 0.0f;
+  mHasMore = true;
+}
+
+void
+DottedCornerFinder::FindPointAndRadius(Point& C, Float& r,
+                                       const Point& innerTangent,
+                                       const Point& normal, Float t)
+{
+  // Find radius for the given tangent point on the inner curve such that the
+  // circle is also tangent to the outer curve.
+
+  NS_ASSERTION(mType == OTHER, "Wrong mType");
+
+  Float lower = 0.0f;
+  Float upper = mMaxR;
+  const Float DIST_MARGIN = 0.1f;
+  for (size_t i = 0; i < MAX_LOOP; i++) {
+    r = (upper + lower) / 2.0f;
+    C = innerTangent + normal * r;
+
+    Point Near = FindBezierNearestPoint(mOuterBezier, C, t);
+    Float distSquare = (C - Near).LengthSquare();
+
+    if (distSquare > Square(r + DIST_MARGIN)) {
+      lower = r;
+    } else if (distSquare < Square(r - DIST_MARGIN)) {
+      upper = r;
+    } else {
+      break;
+    }
+  }
+}
+
+Float
+DottedCornerFinder::FindNext(Float overlap)
+{
+  Float lower = mLastT;
+  Float upper = 1.0f;
+  Float t;
+
+  Point C = mLastC;
+  Float r = 0.0f;
+
+  Float factor = (1.0f - overlap);
+
+  Float circlesDist = 0.0f;
+  Float expectedDist = 0.0f;
+
+  const Float DIST_MARGIN = 0.1f;
+  if (mType == SINGLE_CURVE_AND_RADIUS) {
+    r = mR0;
+
+    expectedDist = (r + mLastR) * factor;
+
+    // Find C_i on the center curve.
+    for (size_t i = 0; i < MAX_LOOP; i++) {
+      t = (upper + lower) / 2.0f;
+      C = GetBezierPoint(mCenterBezier, t);
+
+      // Check overlap along arc.
+      circlesDist = GetBezierLength(mCenterBezier, mLastT, t);
+      if (circlesDist < expectedDist - DIST_MARGIN) {
+        lower = t;
+      } else if (circlesDist > expectedDist + DIST_MARGIN) {
+        upper = t;
+      } else {
+        break;
+      }
+    }
+  } else if (mType == SINGLE_CURVE) {
+    // Find C_i on the center curve, and calculate r_i.
+    for (size_t i = 0; i < MAX_LOOP; i++) {
+      t = (upper + lower) / 2.0f;
+      C = GetBezierPoint(mCenterBezier, t);
+
+      Point Diff = GetBezierDifferential(mCenterBezier, t);
+      Float DiffLength = Diff.Length();
+      if (DiffLength == 0.0f) {
+        // Basically this shouldn't happen.
+        // If differential is 0, we cannot calculate tangent circle,
+        // skip this point.
+        t = (t + upper) / 2.0f;
+        continue;
+      }
+
+      Point normal = PointRotateCCW90(Diff / DiffLength) * (-mNormalSign);
+      r = CalculateDistanceToEllipticArc(C, normal, mCurveOrigin,
+                                         mInnerWidth, mInnerHeight);
+
+      // Check overlap along arc.
+      circlesDist = GetBezierLength(mCenterBezier, mLastT, t);
+      expectedDist = (r + mLastR) * factor;
+      if (circlesDist < expectedDist - DIST_MARGIN) {
+        lower = t;
+      } else if (circlesDist > expectedDist + DIST_MARGIN) {
+        upper = t;
+      } else {
+        break;
+      }
+    }
+  } else {
+    Float distSquareMax = Square(mMaxR * 3.0f);
+    Float circlesDistSquare = 0.0f;
+
+    // Find C_i and r_i.
+    for (size_t i = 0; i < MAX_LOOP; i++) {
+      t = (upper + lower) / 2.0f;
+      Point innerTangent = GetBezierPoint(mInnerBezier, t);
+      if ((innerTangent - mLastC).LengthSquare() > distSquareMax) {
+        // It's clear that this tangent point is too far, skip it.
+        upper = t;
+        continue;
+      }
+
+      Point Diff = GetBezierDifferential(mInnerBezier, t);
+      Float DiffLength = Diff.Length();
+      if (DiffLength == 0.0f) {
+        // Basically this shouldn't happen.
+        // If differential is 0, we cannot calculate tangent circle,
+        // skip this point.
+        t = (t + upper) / 2.0f;
+        continue;
+      }
+
+      Point normal = PointRotateCCW90(Diff / DiffLength) * mNormalSign;
+      FindPointAndRadius(C, r, innerTangent, normal, t);
+
+      // Check overlap with direct distance.
+      circlesDistSquare = (C - mLastC).LengthSquare();
+      expectedDist = (r + mLastR) * factor;
+      if (circlesDistSquare < Square(expectedDist - DIST_MARGIN)) {
+        lower = t;
+      } else if (circlesDistSquare > Square(expectedDist + DIST_MARGIN)) {
+        upper = t;
+      } else {
+        break;
+      }
+    }
+
+    circlesDist = sqrt(circlesDistSquare);
+  }
+
+  mLastT = t;
+  mLastC = C;
+  mLastR = r;
+
+  if (mHasZeroBorderWidth) {
+    const Float T_MARGIN = 0.01f;
+    if (mLastT >= 1.0f - T_MARGIN ||
+        (mLastC - mCn).LengthSquare() < Square(mLastR)) {
+      mHasMore = false;
+    }
+  }
+
+  if (expectedDist == 0.0f) {
+    return 0.0f;
+  }
+
+  return 1.0f - circlesDist * factor / expectedDist;
+}
+
+void
+DottedCornerFinder::FindBestOverlap(Float aMinR, Float aMinBorderRadius,
+                                    Float aMaxBorderRadius)
+{
+  // If overlap is not calculateable, find it with binary search,
+  // such that there exists i that C_i == C_n with the given overlap.
+
+  FourFloats key(aMinR, mMaxR,
+                 aMinBorderRadius, aMaxBorderRadius);
+  BestOverlap best;
+  if (DottedCornerCache.Get(key, &best)) {
+    mCount = best.count;
+    mBestOverlap = best.overlap;
+    return;
+  }
+
+  Float lower = 0.0f;
+  Float upper = 0.5f;
+  // Start from lower bound to find the minimum number of circles.
+  Float overlap = 0.0f;
+  mBestOverlap = overlap;
+  size_t targetCount = 0;
+
+  const Float OVERLAP_MARGIN = 0.1f;
+  for (size_t j = 0; j < MAX_LOOP; j++) {
+    Reset();
+
+    size_t count;
+    Float actualOverlap;
+    if (!GetCountAndLastOverlap(overlap, &count, &actualOverlap)) {
+      if (j == 0) {
+        mCount = mMaxCount;
+        break;
+      }
+    }
+
+    if (j == 0) {
+      if (count < 3 || (count == 3 && actualOverlap > 0.5f)) {
+        // |count == 3 && actualOverlap > 0.5f| means there could be
+        // a circle but it is too near from both ends.
+        //
+        // if actualOverlap == 0.0
+        //               1       2       3
+        //   +-------+-------+-------+-------+
+        //   | ##### | ***** | ##### | ##### |
+        //   |#######|*******|#######|#######|
+        //   |###+###|***+***|###+###|###+###|
+        //   |# C_0 #|* C_1 *|# C_2 #|# C_n #|
+        //   | ##### | ***** | ##### | ##### |
+        //   +-------+-------+-------+-------+
+        //                   |
+        //                   V
+        //   +-------+---+-------+---+-------+
+        //   | ##### |   | ##### |   | ##### |
+        //   |#######|   |#######|   |#######|
+        //   |###+###|   |###+###|   |###+###| Find the best overlap to place
+        //   |# C_0 #|   |# C_1 #|   |# C_n #| C_1 at the middle of them
+        //   | ##### |   | ##### |   | ##### |
+        //   +-------+---+-------+---|-------+
+        //
+        // if actualOverlap == 0.5
+        //               1       2     3
+        //   +-------+-------+-------+---+
+        //   | ##### | ***** | ##### |## |
+        //   |#######|*******|##### C_n #|
+        //   |###+###|***+***|###+###+###|
+        //   |# C_0 #|* C_1 *|# C_2 #|###|
+        //   | ##### | ***** | ##### |## |
+        //   +-------+-------+-------+---+
+        //                 |
+        //                 V
+        //   +-------+-+-------+-+-------+
+        //   | ##### | | ##### | | ##### |
+        //   |#######| |#######| |#######|
+        //   |###+###| |###+###| |###+###| Even if we place C_1 at the middle
+        //   |# C_0 #| |# C_1 #| |# C_n #| of them, it's too near from them
+        //   | ##### | | ##### | | ##### |
+        //   +-------+-+-------+-|-------+
+        //                 |
+        //                 V
+        //   +-------+-----------+-------+
+        //   | ##### |           | ##### |
+        //   |#######|           |#######|
+        //   |###+###|           |###+###| Do not draw any circle
+        //   |# C_0 #|           |# C_n #|
+        //   | ##### |           | ##### |
+        //   +-------+-----------+-------+
+        mCount = 0;
+        break;
+      }
+
+      // targetCount should be 2n, as we're searching C_1 to C_n.
+      //
+      //   targetCount = 4
+      //   mCount = 1
+      //               1       2       3       4
+      //   +-------+-------+-------+-------+-------+
+      //   | ##### | ***** | ##### | ***** | ##### |
+      //   |#######|*******|#######|*******|#######|
+      //   |###+###|***+***|###+###|***+***|###+###|
+      //   |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_n #|
+      //   | ##### | ***** | ##### | ***** | ##### |
+      //   +-------+-------+-------+-------+-------+
+      //                       1
+      //
+      //   targetCount = 6
+      //   mCount = 2
+      //               1       2       3       4       5       6
+      //   +-------+-------+-------+-------+-------+-------+-------+
+      //   | ##### | ***** | ##### | ***** | ##### | ***** | ##### |
+      //   |#######|*******|#######|*******|#######|*******|#######|
+      //   |###+###|***+***|###+###|***+***|###+###|***+***|###+###|
+      //   |# C_0 #|* C_1 *|# C_2 #|* C_3 *|# C_4 #|* C_5 *|# C_n #|
+      //   | ##### | ***** | ##### | ***** | ##### | ***** | ##### |
+      //   +-------+-------+-------+-------+-------+-------+-------+
+      //                       1               2
+      if (count % 2) {
+        targetCount = count + 1;
+      } else {
+        targetCount = count;
+      }
+
+      mCount = targetCount / 2 - 1;
+    }
+
+    if (count == targetCount) {
+      mBestOverlap = overlap;
+
+      if (fabs(actualOverlap - overlap) < OVERLAP_MARGIN) {
+        break;
+      }
+
+      // We started from upper bound, no need to update range when j == 0.
+      if (j > 0) {
+        if (actualOverlap > overlap) {
+          lower = overlap;
+        } else {
+          upper = overlap;
+        }
+      }
+    } else {
+      // |j == 0 && count != targetCount| means that |targetCount = count + 1|,
+      // and we started from upper bound, no need to update range when j == 0.
+      if (j > 0) {
+        if (count > targetCount) {
+          upper = overlap;
+        } else {
+          lower = overlap;
+        }
+      }
+    }
+
+    overlap = (upper + lower) / 2.0f;
+  }
+
+  if (DottedCornerCache.Count() > DottedCornerCacheSize) {
+    DottedCornerCache.Clear();
+  }
+  DottedCornerCache.Put(key, BestOverlap(mBestOverlap, mCount));
+}
+
+bool
+DottedCornerFinder::GetCountAndLastOverlap(Float aOverlap,
+                                           size_t* aCount,
+                                           Float* aActualOverlap)
+{
+  // Return the number of circles and the last circles' overlap for the
+  // given overlap.
+
+  Reset();
+
+  const Float T_MARGIN = 0.01f;
+  const Float DIST_MARGIN = 0.1f;
+  const Float DIST_MARGIN_SQUARE = Square(DIST_MARGIN);
+  for (size_t i = 0; i < mMaxCount; i++) {
+    Float actualOverlap = FindNext(aOverlap);
+    if (mLastT >= 1.0f - T_MARGIN ||
+        (mLastC - mCn).LengthSquare() < DIST_MARGIN_SQUARE) {
+      *aCount = i + 1;
+      *aActualOverlap = actualOverlap;
+      return true;
+    }
+  }
+
+  return false;
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/layout/base/DottedCornerFinder.h
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_DottedCornerFinder_h_
+#define mozilla_DottedCornerFinder_h_
+
+#include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BezierUtils.h"
+#include "gfxRect.h"
+
+namespace mozilla {
+
+// Calculate C_i and r_i for each filled/unfilled circles in dotted corner.
+// Returns circle with C_{2j} and r_{2j} where 0 < 2j < n.
+//
+//                            ____-----------+
+//                      __----  *****     ###|
+//                __----      *********  ####|
+//           __---  #####    ***********#####|
+//        _--     #########  *****+*****#####+ C_0
+//      _-       ########### *** C_1****#####|
+//     /         #####+#####  *********  ####|
+//    /       .  ### C_2 ###    *****     ###|
+//   |            #########       ____-------+
+//   |     .        #####____-----
+//  |              __----
+//  |    .       /
+// |           /
+// |  *****   |
+// | ******* |
+// |*********|
+// |****+****|
+// | C_{n-1} |
+// | ******* |
+// |  *****  |
+// |  #####  |
+// | ####### |
+// |#########|
+// +----+----+
+//     C_n
+
+class DottedCornerFinder
+{
+  typedef mozilla::gfx::Bezier Bezier;
+  typedef mozilla::gfx::Float Float;
+  typedef mozilla::gfx::Point Point;
+  typedef mozilla::gfx::Size Size;
+
+public:
+  struct Result
+  {
+    // Center point of dot and its radius.
+    Point C;
+    Float r;
+
+    Result(const Point& aC, Float aR)
+     : C(aC), r(aR)
+    {}
+  };
+
+  //                        aBorderRadiusX
+  //                       aCornerDim.width
+  //                     |<----------------->|
+  //                     |                   | v
+  //                   --+-------------___---+--
+  //                   ^ |         __--      | |
+  //                   | |       _-          | | aR0
+  //                   | |     /         aC0 +--
+  //                   | |   /               | ^
+  //                   | |  |                |
+  //  aBorderRadiusY   | | |             __--+
+  // aCornerDim.height | ||            _-
+  //                   | ||           /
+  //                   | |           /
+  //                   | |          |
+  //                   | |          |
+  //                   | |         |
+  //                   | |         |
+  //                   v |    aCn  |
+  //                   --+----+----+
+  //                     |    |
+  //                     |<-->|
+  //                      aRn
+  //
+  // aCornerDim and (aBorderRadiusX, aBorderRadiusY) can be different when
+  // aBorderRadiusX is smaller than aRn*2 or
+  // aBorderRadiusY is smaller than aR0*2.
+  //
+  //                                        aCornerDim.width
+  //                                      |<----------------->|
+  //                                      |                   |
+  //                                      | aBorderRadiusX    |
+  //                                      |<--------->|       |
+  //                                      |           |       |
+  //                   -------------------+-------__--+-------+--
+  //                   ^                ^ |     _-            | ^
+  //                   |                | |   /               | |
+  //                   |                | |  /                | |
+  //                   | aBorderRadiusY | | |                 | | aR0
+  //                   |                | ||                  | |
+  //                   |                | |                   | |
+  // aCornerDim.height |                v |                   | v
+  //                   |                --+               aC0 +--
+  //                   |                  |                   |
+  //                   |                  |                   |
+  //                   |                  |                   |
+  //                   |                  |                   |
+  //                   |                  |                   |
+  //                   v                  |        aCn        |
+  //                   -------------------+---------+---------+
+  //                                      |         |
+  //                                      |<------->|
+  //                                          aRn
+  DottedCornerFinder(const Bezier& aOuterBezier, const Bezier& aInnerBezier,
+                     mozilla::css::Corner aCorner,
+                     Float aBorderRadiusX, Float aBorderRadiusY,
+                     const Point& aC0, Float aR0, const Point& aCn, Float aRn,
+                     const Size& aCornerDim);
+
+  bool HasMore(void) const;
+  Result Next(void);
+
+private:
+  static const size_t MAX_LOOP = 32;
+
+  // Bezier control points for the outer curve, the inner curve, and a curve
+  // that center points of circles are on (center curve).
+  //
+  //               ___---+ outer curve
+  //           __--      |
+  //         _-          |
+  //       /        __---+ center curve
+  //     /      __--     |
+  //    |     /          |
+  //   |    /        __--+ inner curve
+  //  |    |       _-
+  //  |    |      /
+  // |     |     /
+  // |    |     |
+  // |    |     |
+  // |    |    |
+  // |    |    |
+  // |    |    |
+  // +----+----+
+  Bezier mOuterBezier;
+  Bezier mInnerBezier;
+  Bezier mCenterBezier;
+
+  mozilla::css::Corner mCorner;
+
+  // Sign of the normal vector used in radius calculation, flipped depends on
+  // corner and start and end radii.
+  Float mNormalSign;
+
+  // Center points and raii for start and end circles, mR0 >= mRn.
+  // mMaxR = max(mR0, mRn)
+  //
+  //                           v
+  //               ___---+------
+  //           __--     #|#    | mRn
+  //         _-        ##|##   |
+  //       /           ##+## ---
+  //     /              mCn    ^
+  //    |               #|#
+  //   |             __--+
+  //  |            _-
+  //  |           /
+  // |           /
+  // |          |
+  // |          |
+  // |  #####  |
+  // | ####### |
+  // |#########|
+  // +----+----+
+  // |## mC0 ##|
+  // | ####### |
+  // |  #####  |
+  // |    |
+  // |<-->|
+  //
+  //  mR0
+  //
+  Point mC0;
+  Point mCn;
+  Float mR0;
+  Float mRn;
+  Float mMaxR;
+
+  // Parameters for the center curve with perfect circle and the inner curve.
+  //
+  //               ___---+
+  //           __--      |
+  //         _-          |
+  //       /        __---+
+  //     /      __--     |
+  //    |     /          |
+  //   |    /        __--+--
+  //  |    |       _-    | ^
+  //  |    |      /      | |
+  // |     |     /       | |
+  // |    |     |        | |
+  // |    |     |        | | mInnerHeight
+  // |    |    |         | |
+  // |    |    |         | |
+  // |    |    |         | v
+  // +----+----+---------+
+  //      |    |         | mCurveOrigin
+  //      |    |<------->|
+  //      |  mInnerWidth |
+  //      |              |
+  //      |<------------>|
+  //        mCenterCurveR
+  //
+  Point mCurveOrigin;
+  Float mCenterCurveR;
+  Float mInnerWidth;
+  Float mInnerHeight;
+
+  Point mLastC;
+  Float mLastR;
+  Float mLastT;
+
+  // Overlap between two circles.
+  // It uses arc length on PERFECT, SINGLE_CURVE_AND_RADIUS, and SINGLE_CURVE,
+  // and direct distance on OTHER.
+  Float mBestOverlap;
+
+  // If one of border-widths is 0, do not calculate overlap, and draw circles
+  // until it reaches the other side or exceeds mMaxCount.
+  bool mHasZeroBorderWidth;
+  bool mHasMore;
+
+  // The maximum number of filled/unfilled circles.
+  size_t mMaxCount;
+
+  enum {
+    //                      radius.width
+    //                 |<----------------->|
+    //                 |                   |
+    //               --+-------------___---+----
+    //               ^ |         __--     #|#  ^
+    //               | |       _-        ##|## |
+    //               | |     /           ##+## | top-width
+    //               | |   /             ##|## |
+    //               | |  |               #|#  v
+    //               | | |             __--+----
+    // radius.height | ||            _-
+    //               | ||           /
+    //               | |           /
+    //               | |          |
+    //               | |          |
+    //               | |  #####  |
+    //               | | ####### |
+    //               v |#########|
+    //               --+----+----+
+    //                 |#########|
+    //                 | ####### |
+    //                 |  #####  |
+    //                 |         |
+    //                 |<------->|
+    //                  left-width
+
+    // * top-width == left-width
+    // * radius.width == radius.height
+    // * top-width < radius.width * 2
+    //
+    // All circles has same radii and are on single perfect circle's arc.
+    // Overlap is known.
+    //
+    // Split the perfect circle's arc into 2n segments, each segment's length is
+    // top-width * (1 - overlap).  Place each circle's center point C_i on each
+    // end of the segment, each circle's radius r_i is top-width / 2
+    //
+    //                       #####
+    //                      #######
+    // perfect             #########
+    // circle's          ___---+####
+    // arc     ##### __--  ## C_0 ##
+    //   |    #####_-       ###|###
+    //   |   ####+####       ##|##
+    //   |   ##/C_i ##         |
+    //   |    |######          |
+    //   |   | #####           |
+    //   +->|                  |
+    //     |                   |
+    //   ##|##                 |
+    //  ###|###                |
+    // ####|####               |
+    // ####+-------------------+
+    // ## C_n ##
+    //  #######
+    //   #####
+    PERFECT,
+
+    // * top-width == left-width
+    // * 0.5 < radius.width / radius.height < 2.0
+    // * top-width < min(radius.width, radius.height) * 2
+    //
+    // All circles has same radii and are on single elliptic arc.
+    // Overlap is known.
+    //
+    // Split the elliptic arc into 2n segments, each segment's length is
+    // top-width * (1 - overlap).  Place each circle's center point C_i on each
+    // end of the segment, each circle's radius r_i is top-width / 2
+    //
+    //                            #####
+    //                           #######
+    //             #####        #########
+    //            #######   ____----+####
+    // elliptic  ######__---    ## C_0 ##
+    // arc       ##__+-###       ###|###
+    //   |      / # C_i #         ##|##
+    //   +--> /    #####            |
+    //       |                      |
+    //   ###|#                      |
+    //  ###|###                     |
+    // ####|####                    |
+    // ####+------------------------+
+    // ## C_n ##
+    //  #######
+    //   #####
+    SINGLE_CURVE_AND_RADIUS,
+
+    // * top-width != left-width
+    // * 0 < min(top-width, left-width)
+    // * 0.5 < radius.width / radius.height < 2.0
+    // * max(top-width, left-width) < min(radius.width, radius.height) * 2
+    //
+    // All circles are on single elliptic arc.
+    // Overlap is unknown.
+    //
+    // Place each circle's center point C_i on elliptic arc, each circle's
+    // radius r_i is the distance between the center point and the inner curve.
+    // The arc segment's length between C_i and C_{i-1} is
+    // (r_i + r_{i-1}) * (1 - overlap).
+    //
+    //  outer curve
+    //           /
+    //          /
+    //         /         / center curve
+    //        / ####### /
+    //       /##       /#
+    //      +#        /  #
+    //     /#        /    #
+    //    / #   C_i /     #
+    //   /  #      +      #  /
+    //  /   #     /  \    # / inner curve
+    //      #    /     \  #/
+    //       #  /   r_i  \+
+    //        #/       ##/
+    //        / ####### /
+    //                 /
+    SINGLE_CURVE,
+
+    // Other cases.
+    // Circles are not on single elliptic arc.
+    // Overlap are unknown.
+    //
+    // Place tangent point innerTangent on the inner curve and find circle's
+    // center point C_i and radius r_i where the circle is also tangent to the
+    // outer curve.
+    // Distance between C_i and C_{i-1} is (r_i + r_{i-1}) * (1 - overlap).
+    //
+    //  outer curve
+    //           /
+    //          /
+    //         /
+    //        / #######
+    //       /##       ##
+    //      +#           #
+    //     /# \           #
+    //    / #    \        #
+    //   /  #      +      #  /
+    //  /   #   C_i  \    # / inner curve
+    //      #          \  #/
+    //       #      r_i  \+
+    //        ##       ##/ innerTangent
+    //          ####### /
+    //                 /
+    OTHER
+  } mType;
+
+  size_t mI;
+  size_t mCount;
+
+  // Determine mType from parameters.
+  void DetermineType(Float aBorderRadiusX, Float aBorderRadiusY);
+
+  // Reset calculation.
+  void Reset(void);
+
+  // Find radius for the given tangent point on the inner curve such that the
+  // circle is also tangent to the outer curve.
+  void FindPointAndRadius(Point& C, Float& r, const Point& innerTangent,
+                          const Point& normal, Float t);
+
+  // Find next dot.
+  Float FindNext(Float overlap);
+
+  // Find mBestOverlap for parameters.
+  void FindBestOverlap(Float aMinR,
+                       Float aMinBorderRadius, Float aMaxBorderRadius);
+
+  // Fill corner with dots with given overlap, and return the number of dots
+  // and last two dots's overlap.
+  bool GetCountAndLastOverlap(Float aOverlap,
+                              size_t* aCount, Float* aActualOverlap);
+};
+
+} // namespace mozilla
+
+#endif /* mozilla_DottedCornerFinder_h_ */
--- a/layout/base/RestyleManager.cpp
+++ b/layout/base/RestyleManager.cpp
@@ -6,16 +6,17 @@
 /**
  * Code responsible for managing style changes: tracking what style
  * changes need to happen, scheduling them, and doing them.
  */
 
 #include "mozilla/RestyleManager.h"
 
 #include <algorithm> // For std::max
+#include "mozilla/EffectSet.h"
 #include "mozilla/EventStates.h"
 #include "nsLayoutUtils.h"
 #include "AnimationCommon.h" // For GetLayerAnimationInfo
 #include "FrameLayerBuilder.h"
 #include "GeckoProfiler.h"
 #include "LayerAnimationInfo.h" // For LayerAnimationInfo::sRecords
 #include "nsStyleChangeList.h"
 #include "nsRuleProcessorData.h"
--- a/layout/base/TouchManager.cpp
+++ b/layout/base/TouchManager.cpp
@@ -10,18 +10,16 @@
 #include "mozilla/TouchEvents.h"
 #include "mozilla/dom/EventTarget.h"
 #include "nsIFrame.h"
 #include "nsPresShell.h"
 #include "nsView.h"
 
 namespace mozilla {
 
-using EventTarget = ::mozilla::dom::EventTarget;
-
 nsRefPtrHashtable<nsUint32HashKey, dom::Touch>* TouchManager::gCaptureTouchList;
 
 /*static*/ void
 TouchManager::InitializeStatics()
 {
   NS_ASSERTION(!gCaptureTouchList, "InitializeStatics called multiple times!");
   gCaptureTouchList = new nsRefPtrHashtable<nsUint32HashKey, dom::Touch>;
 }
--- a/layout/base/moz.build
+++ b/layout/base/moz.build
@@ -113,19 +113,21 @@ EXPORTS.mozilla += [
     'StaticPresData.h',
 ]
 
 UNIFIED_SOURCES += [
     'AccessibleCaret.cpp',
     'AccessibleCaretEventHub.cpp',
     'AccessibleCaretManager.cpp',
     'ActiveLayerTracker.cpp',
+    'DashedCornerFinder.cpp',
     'DisplayItemClip.cpp',
     'DisplayItemScrollClip.cpp',
     'DisplayListClipState.cpp',
+    'DottedCornerFinder.cpp',
     'FrameLayerBuilder.cpp',
     'FramePropertyTable.cpp',
     'GeometryUtils.cpp',
     'LayoutLogging.cpp',
     'MaskLayerImageCache.cpp',
     'MobileViewportManager.cpp',
     'nsBidi.cpp',
     'nsBidiPresUtils.cpp',
--- a/layout/base/nsCSSRenderingBorders.cpp
+++ b/layout/base/nsCSSRenderingBorders.cpp
@@ -7,16 +7,18 @@
 #include "nsCSSRenderingBorders.h"
 
 #include "gfxUtils.h"
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Helpers.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "BorderConsts.h"
+#include "DashedCornerFinder.h"
+#include "DottedCornerFinder.h"
 #include "nsLayoutUtils.h"
 #include "nsStyleConsts.h"
 #include "nsCSSColorUtils.h"
 #include "GeckoProfiler.h"
 #include "nsExpirationTracker.h"
 #include "RoundedRect.h"
 #include "nsClassHashtable.h"
 #include "nsStyleStruct.h"
@@ -56,16 +58,20 @@ static void ComputeBorderCornerDimension
                                           const Rect& aInnerRect,
                                           const RectCornerRadii& aRadii,
                                           RectCornerRadii *aDimsResult);
 
 // given a side index, get the previous and next side index
 #define NEXT_SIDE(_s) mozilla::css::Side(((_s) + 1) & 3)
 #define PREV_SIDE(_s) mozilla::css::Side(((_s) + 3) & 3)
 
+// given a corner index, get the previous and next corner index
+#define NEXT_CORNER(_s) mozilla::css::Corner(((_s) + 1) & 3)
+#define PREV_CORNER(_s) mozilla::css::Corner(((_s) + 3) & 3)
+
 // from the given base color and the background color, turn
 // color into a color for the given border pattern style
 static Color MakeBorderColor(nscolor aColor,
                              nscolor aBackgroundColor,
                              BorderColorStyle aBorderColorStyle);
 
 
 // Given a line index (an index starting from the outside of the
@@ -318,18 +324,16 @@ nsCSSBorderRenderer::AreBorderSideFinalS
 
   return true;
 }
 
 bool
 nsCSSBorderRenderer::IsSolidCornerStyle(uint8_t aStyle, mozilla::css::Corner aCorner)
 {
   switch (aStyle) {
-    case NS_STYLE_BORDER_STYLE_DOTTED:
-    case NS_STYLE_BORDER_STYLE_DASHED:
     case NS_STYLE_BORDER_STYLE_SOLID:
       return true;
 
     case NS_STYLE_BORDER_STYLE_INSET:
     case NS_STYLE_BORDER_STYLE_OUTSET:
       return (aCorner == NS_CORNER_TOP_LEFT || aCorner == NS_CORNER_BOTTOM_RIGHT);
 
     case NS_STYLE_BORDER_STYLE_GROOVE:
@@ -395,18 +399,16 @@ nsCSSBorderRenderer::IsCornerMergeable(m
 }
 
 BorderColorStyle
 nsCSSBorderRenderer::BorderColorStyleForSolidCorner(uint8_t aStyle, mozilla::css::Corner aCorner)
 {
   // note that this function assumes that the corner is already solid,
   // as per the earlier function
   switch (aStyle) {
-    case NS_STYLE_BORDER_STYLE_DOTTED:
-    case NS_STYLE_BORDER_STYLE_DASHED:
     case NS_STYLE_BORDER_STYLE_SOLID:
     case NS_STYLE_BORDER_STYLE_DOUBLE:
       return BorderColorStyleSolid;
 
     case NS_STYLE_BORDER_STYLE_INSET:
     case NS_STYLE_BORDER_STYLE_GROOVE:
       if (aCorner == NS_CORNER_TOP_LEFT)
         return BorderColorStyleDark;
@@ -1049,16 +1051,55 @@ nsCSSBorderRenderer::GetStraightBorderPo
     P.x += signs[0] * borderWidth / 2.0f;
     P.y += signs[1] * dim.height;
   }
 
   return P;
 }
 
 void
+nsCSSBorderRenderer::GetOuterAndInnerBezier(Bezier* aOuterBezier,
+                                            Bezier* aInnerBezier,
+                                            mozilla::css::Corner aCorner)
+{
+  // Return bezier control points for outer and inner curve for given corner.
+  //
+  //               ___---+ outer curve
+  //           __--      |
+  //         _-          |
+  //       /             |
+  //     /               |
+  //    |                |
+  //   |             __--+ inner curve
+  //  |            _-
+  //  |           /
+  // |           /
+  // |          |
+  // |          |
+  // |         |
+  // |         |
+  // |         |
+  // +---------+
+
+  mozilla::css::Side sideH(GetHorizontalSide(aCorner));
+  mozilla::css::Side sideV(GetVerticalSide(aCorner));
+
+  Size innerCornerSize(std::max(0.0f, mBorderRadii[aCorner].width -
+                                mBorderWidths[sideV]),
+                       std::max(0.0f, mBorderRadii[aCorner].height -
+                                mBorderWidths[sideH]));
+
+  GetBezierPointsForCorner(aOuterBezier, aCorner, mOuterRect.AtCorner(aCorner),
+                           mBorderRadii[aCorner]);
+
+  GetBezierPointsForCorner(aInnerBezier, aCorner, mInnerRect.AtCorner(aCorner),
+                           innerCornerSize);
+}
+
+void
 nsCSSBorderRenderer::FillSolidBorder(const Rect& aOuterRect,
                                      const Rect& aInnerRect,
                                      const RectCornerRadii& aBorderRadii,
                                      const Float* aBorderSizes,
                                      int aSides,
                                      const ColorPattern& aColor)
 {
   // Note that this function is allowed to draw more than just the
@@ -1287,16 +1328,45 @@ nsCSSBorderRenderer::DrawBorderSides(int
     compositeColors = mCompositeColors[i];
     break;
   }
 
   if (borderRenderStyle == NS_STYLE_BORDER_STYLE_NONE ||
       borderRenderStyle == NS_STYLE_BORDER_STYLE_HIDDEN)
     return;
 
+  if (borderRenderStyle == NS_STYLE_BORDER_STYLE_DASHED ||
+      borderRenderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
+    // Draw each corner separately, with the given side's color.
+    if (aSides & SIDE_BIT_TOP) {
+      DrawDashedOrDottedCorner(NS_SIDE_TOP, C_TL);
+    } else if (aSides & SIDE_BIT_LEFT) {
+      DrawDashedOrDottedCorner(NS_SIDE_LEFT, C_TL);
+    }
+
+    if (aSides & SIDE_BIT_TOP) {
+      DrawDashedOrDottedCorner(NS_SIDE_TOP, C_TR);
+    } else if (aSides & SIDE_BIT_RIGHT) {
+      DrawDashedOrDottedCorner(NS_SIDE_RIGHT, C_TR);
+    }
+
+    if (aSides & SIDE_BIT_BOTTOM) {
+      DrawDashedOrDottedCorner(NS_SIDE_BOTTOM, C_BL);
+    } else if (aSides & SIDE_BIT_LEFT) {
+      DrawDashedOrDottedCorner(NS_SIDE_LEFT, C_BL);
+    }
+
+    if (aSides & SIDE_BIT_BOTTOM) {
+      DrawDashedOrDottedCorner(NS_SIDE_BOTTOM, C_BR);
+    } else if (aSides & SIDE_BIT_RIGHT) {
+      DrawDashedOrDottedCorner(NS_SIDE_RIGHT, C_BR);
+    }
+    return;
+  }
+
   // -moz-border-colors is a hack; if we have it for a border, then
   // it's always drawn solid, and each color is given 1px.  The last
   // color is used for the remainder of the border's size.  Just
   // hand off to another function to do all that.
   if (compositeColors) {
     DrawBorderSidesCompositeColors(aSides, compositeColors);
     return;
   }
@@ -1311,18 +1381,16 @@ nsCSSBorderRenderer::DrawBorderSides(int
   if (mOneUnitBorder &&
       (borderRenderStyle == NS_STYLE_BORDER_STYLE_RIDGE ||
        borderRenderStyle == NS_STYLE_BORDER_STYLE_GROOVE ||
        borderRenderStyle == NS_STYLE_BORDER_STYLE_DOUBLE))
     borderRenderStyle = NS_STYLE_BORDER_STYLE_SOLID;
 
   switch (borderRenderStyle) {
     case NS_STYLE_BORDER_STYLE_SOLID:
-    case NS_STYLE_BORDER_STYLE_DASHED:
-    case NS_STYLE_BORDER_STYLE_DOTTED:
       borderColorStyleTopLeft[0] = BorderColorStyleSolid;
 
       borderColorStyleBottomRight[0] = BorderColorStyleSolid;
 
       borderColorStyleCount = 1;
       break;
 
     case NS_STYLE_BORDER_STYLE_GROOVE:
@@ -1534,17 +1602,17 @@ nsCSSBorderRenderer::DrawBorderSides(int
     soRect = siRect;
   }
 }
 
 void
 nsCSSBorderRenderer::SetupDashedOptions(StrokeOptions* aStrokeOptions,
                                         Float aDash[2],
                                         mozilla::css::Side aSide,
-                                        Float aBorderLength)
+                                        Float aBorderLength, bool isCorner)
 {
   uint8_t style = mBorderStyles[aSide];
   Float borderWidth = mBorderWidths[aSide];
 
   // Dashed line starts and ends with half segment in most case.
   //
   // __--+---+---+---+---+---+---+---+---+--__
   //     |###|   |   |###|###|   |   |###|
@@ -1682,16 +1750,33 @@ nsCSSBorderRenderer::SetupDashedOptions(
   aDash[0] = fullDash;
   aDash[1] = fullDash;
 
   if (style == NS_STYLE_BORDER_STYLE_DASHED && fullDash > 1.0f) {
     if (!fullStart) {
       // Draw half segments on both ends.
       aStrokeOptions->mDashOffset = halfDash;
     }
+  } else if (isCorner) {
+    // If side ends with filled full segment, corner should start with unfilled
+    // full segment.
+    //
+    //     corner            side
+    //   ------------>|<---------------------------
+    //                |
+    //          __+---+---+---+---+---+---+---+---+
+    //       _+-  |   |###|###|   |   |###|###|   |
+    //     /##|   |   |###|###|   |   |###|###|   |
+    //    +####|   |  |###|###|   |   |###|###|   |
+    //   /#\####| _+--+---+---+---+---+---+---+---+
+    //  |####\##+-
+    //  |#####+-
+    //  +--###/
+    //  |  --+
+    aStrokeOptions->mDashOffset = fullDash;
   }
 
   aStrokeOptions->mDashPattern = aDash;
   aStrokeOptions->mDashLength = 2;
 
   PrintAsFormatString("dash: %f %f\n", aDash[0], aDash[1]);
 }
 
@@ -1769,17 +1854,17 @@ nsCSSBorderRenderer::DrawDashedOrDottedS
 
   Float borderLength = GetBorderLength(aSide, start, end);
   if (borderLength < 0.0f) {
     return;
   }
 
   StrokeOptions strokeOptions(borderWidth);
   Float dash[2];
-  SetupDashedOptions(&strokeOptions, dash, aSide, borderLength);
+  SetupDashedOptions(&strokeOptions, dash, aSide, borderLength, false);
 
   mDrawTarget->StrokeLine(start, end,
                           ColorPattern(ToDeviceColor(borderColor)),
                           strokeOptions);
 }
 
 void
 nsCSSBorderRenderer::DrawDottedSideSlow(mozilla::css::Side aSide)
@@ -2026,16 +2111,251 @@ nsCSSBorderRenderer::DrawDottedSideSlow(
   RefPtr<Path> path = builder->Finish();
   mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
 
   if (mergeStart == MERGE_HALF || mergeEnd == MERGE_HALF) {
     mDrawTarget->PopClip();
   }
 }
 
+void
+nsCSSBorderRenderer::DrawDashedOrDottedCorner(mozilla::css::Side aSide,
+                                              mozilla::css::Corner aCorner)
+{
+  // Draw dashed/dotted corner with following approach.
+  //
+  // dashed corner
+  //   If both side has same border-width and border-width <= 2.0, draw dashed
+  //   line along the corner, with appropriate dash length and gap to make the
+  //   corner symmetric as far as possible.  Dash length equals to the gap, and
+  //   the ratio of the dash length to border-width is the maximum value in in
+  //   [1, 3] range.
+  //   Otherwise, draw dashed segments along the corner, keeping same dash
+  //   length ratio to border-width at that point.
+  //   (see DashedCornerFinder.h for more detail)
+  //   Line ends with half segments, to joint with both side easily.
+  //
+  // dotted corner
+  //   If both side has same border-width and border-width <= 2.0, draw 1:1
+  //   dashed line along the corner.
+  //   Otherwise Draw circles along the corner, with appropriate gap that makes
+  //   the corner symmetric as far as possible.  The size of the circle may
+  //   change along the corner, that is tangent to the outer curver and the
+  //   inner curve.  The ratio of the gap to circle diameter is the maximum
+  //   value in [0.5, 1] range.
+  //   (see DottedCornerFinder.h for more detail)
+  //   Corner ends with filled dots but those dots are drawn by
+  //   DrawDashedOrDottedSide.  So this may draw no circles if there's no space
+  //   between 2 dots at both ends.
+
+  NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED ||
+               mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
+               "Style should be dashed or dotted.");
+
+  if (IsCornerMergeable(aCorner)) {
+    // DrawDashedOrDottedSide will draw corner.
+     return;
+   }
+
+  mozilla::css::Side sideH(GetHorizontalSide(aCorner));
+  mozilla::css::Side sideV(GetVerticalSide(aCorner));
+  Float borderWidthH = mBorderWidths[sideH];
+  Float borderWidthV = mBorderWidths[sideV];
+  if (borderWidthH == 0.0f && borderWidthV == 0.0f) {
+    return;
+  }
+
+  Float styleH = mBorderStyles[sideH];
+  Float styleV = mBorderStyles[sideV];
+
+  // Corner between dotted and others with radius=0 is drawn by side.
+  if (IsZeroSize(mBorderRadii[aCorner]) &&
+      (styleV == NS_STYLE_BORDER_STYLE_DOTTED ||
+       styleH == NS_STYLE_BORDER_STYLE_DOTTED)) {
+    return;
+  }
+
+  if (borderWidthH != borderWidthV || borderWidthH > 2.0f) {
+    uint8_t style = mBorderStyles[aSide];
+    if (style == NS_STYLE_BORDER_STYLE_DOTTED) {
+      DrawDottedCornerSlow(aSide, aCorner);
+    } else {
+      DrawDashedCornerSlow(aSide, aCorner);
+    }
+    return;
+  }
+
+  nscolor borderColor = mBorderColors[aSide];
+  Point points[4];
+  bool ignored;
+  points[0] = GetStraightBorderPoint(sideH, aCorner, &ignored);
+  points[3] = GetStraightBorderPoint(sideV, aCorner, &ignored);
+  // Round points to draw dot on each pixel.
+  if (borderWidthH < 2.0f) {
+    points[0].x = round(points[0].x);
+  }
+  if (borderWidthV < 2.0f) {
+    points[3].y = round(points[3].y);
+  }
+  points[1] = points[0];
+  points[1].x += kKappaFactor * (points[3].x - points[0].x);
+  points[2] = points[3];
+  points[2].y += kKappaFactor * (points[0].y - points[3].y);
+
+  Float len = GetQuarterEllipticArcLength(fabs(points[0].x - points[3].x),
+                                          fabs(points[0].y - points[3].y));
+
+  Float dash[2];
+  StrokeOptions strokeOptions(borderWidthH);
+  SetupDashedOptions(&strokeOptions, dash, aSide, len, true);
+
+  RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
+  builder->MoveTo(points[0]);
+  builder->BezierTo(points[1], points[2], points[3]);
+  RefPtr<Path> path = builder->Finish();
+  mDrawTarget->Stroke(path, ColorPattern(ToDeviceColor(borderColor)),
+                      strokeOptions);
+}
+
+void
+nsCSSBorderRenderer::DrawDottedCornerSlow(mozilla::css::Side aSide,
+                                          mozilla::css::Corner aCorner)
+{
+  NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DOTTED,
+               "Style should be dotted.");
+
+  mozilla::css::Side sideH(GetHorizontalSide(aCorner));
+  mozilla::css::Side sideV(GetVerticalSide(aCorner));
+  Float R0 = mBorderWidths[sideH] / 2.0f;
+  Float Rn = mBorderWidths[sideV] / 2.0f;
+  if (R0 == 0.0f && Rn == 0.0f) {
+    return;
+  }
+
+  nscolor borderColor = mBorderColors[aSide];
+  Bezier outerBezier;
+  Bezier innerBezier;
+  GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
+
+  bool ignored;
+  Point C0 = GetStraightBorderPoint(sideH, aCorner, &ignored);
+  Point Cn = GetStraightBorderPoint(sideV, aCorner, &ignored);
+  DottedCornerFinder finder(outerBezier, innerBezier, aCorner,
+                            mBorderRadii[aCorner].width,
+                            mBorderRadii[aCorner].height,
+                            C0, R0, Cn, Rn, mBorderCornerDimensions[aCorner]);
+
+  RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
+  while (finder.HasMore()) {
+    DottedCornerFinder::Result result = finder.Next();
+
+    builder->MoveTo(Point(result.C.x + result.r, result.C.y));
+    builder->Arc(result.C, result.r, 0, Float(2.0 * M_PI));
+  }
+  RefPtr<Path> path = builder->Finish();
+  mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
+}
+
+void
+nsCSSBorderRenderer::DrawDashedCornerSlow(mozilla::css::Side aSide,
+                                          mozilla::css::Corner aCorner)
+{
+  NS_ASSERTION(mBorderStyles[aSide] == NS_STYLE_BORDER_STYLE_DASHED,
+               "Style should be dashed.");
+
+  mozilla::css::Side sideH(GetHorizontalSide(aCorner));
+  mozilla::css::Side sideV(GetVerticalSide(aCorner));
+  Float borderWidthH = mBorderWidths[sideH];
+  Float borderWidthV = mBorderWidths[sideV];
+  if (borderWidthH == 0.0f && borderWidthV == 0.0f) {
+    return;
+  }
+
+  nscolor borderColor = mBorderColors[aSide];
+  Bezier outerBezier;
+  Bezier innerBezier;
+  GetOuterAndInnerBezier(&outerBezier, &innerBezier, aCorner);
+
+  DashedCornerFinder finder(outerBezier, innerBezier,
+                            borderWidthH, borderWidthV,
+                            mBorderCornerDimensions[aCorner]);
+
+  RefPtr<PathBuilder> builder = mDrawTarget->CreatePathBuilder();
+  while (finder.HasMore()) {
+    DashedCornerFinder::Result result = finder.Next();
+
+    builder->MoveTo(result.outerSectionBezier.mPoints[0]);
+    builder->BezierTo(result.outerSectionBezier.mPoints[1],
+                      result.outerSectionBezier.mPoints[2],
+                      result.outerSectionBezier.mPoints[3]);
+    builder->LineTo(result.innerSectionBezier.mPoints[3]);
+    builder->BezierTo(result.innerSectionBezier.mPoints[2],
+                      result.innerSectionBezier.mPoints[1],
+                      result.innerSectionBezier.mPoints[0]);
+    builder->LineTo(result.outerSectionBezier.mPoints[0]);
+  }
+
+  if (outerBezier.mPoints[0].x != innerBezier.mPoints[0].x) {
+    // Fill gap before the first section.
+    //
+    //     outnerPoint[0]
+    //         |
+    //         v
+    //        _+-----------+--
+    //      /   \##########|
+    //    /      \#########|
+    //   +        \########|
+    //   |\         \######|
+    //   |  \        \#####|
+    //   |    \       \####|
+    //   |      \       \##|
+    //   |        \      \#|
+    //   |          \     \|
+    //   |            \  _-+--
+    //   +--------------+  ^
+    //   |              |  |
+    //   |              |  innerPoint[0]
+    //   |              |
+    builder->MoveTo(outerBezier.mPoints[0]);
+    builder->LineTo(innerBezier.mPoints[0]);
+    builder->LineTo(Point(innerBezier.mPoints[0].x, outerBezier.mPoints[0].y));
+    builder->LineTo(outerBezier.mPoints[0]);
+  }
+
+  if (outerBezier.mPoints[3].y != innerBezier.mPoints[3].y) {
+    // Fill gap after the last section.
+    //
+    // outnerPoint[3]
+    //   |
+    //   |
+    //   |    _+-----------+--
+    //   |  /   \          |
+    //   v/      \         |
+    //   +        \        |
+    //   |\         \      |
+    //   |##\        \     |
+    //   |####\       \    |
+    //   |######\       \  |
+    //   |########\      \ |
+    //   |##########\     \|
+    //   |############\  _-+--
+    //   +--------------+<-- innerPoint[3]
+    //   |              |
+    //   |              |
+    //   |              |
+    builder->MoveTo(outerBezier.mPoints[3]);
+    builder->LineTo(innerBezier.mPoints[3]);
+    builder->LineTo(Point(outerBezier.mPoints[3].x, innerBezier.mPoints[3].y));
+    builder->LineTo(outerBezier.mPoints[3]);
+  }
+
+  RefPtr<Path> path = builder->Finish();
+  mDrawTarget->Fill(path, ColorPattern(ToDeviceColor(borderColor)));
+}
+
 bool
 nsCSSBorderRenderer::AllBordersSameWidth()
 {
   if (mBorderWidths[0] == mBorderWidths[1] &&
       mBorderWidths[0] == mBorderWidths[2] &&
       mBorderWidths[0] == mBorderWidths[3])
   {
     return true;
--- a/layout/base/nsCSSRenderingBorders.h
+++ b/layout/base/nsCSSRenderingBorders.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef NS_CSS_RENDERING_BORDERS_H
 #define NS_CSS_RENDERING_BORDERS_H
 
 #include "gfxRect.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/gfx/2D.h"
+#include "mozilla/gfx/BezierUtils.h"
 #include "mozilla/gfx/PathHelpers.h"
 #include "mozilla/RefPtr.h"
 #include "nsColor.h"
 #include "nsCOMPtr.h"
 #include "nsStyleConsts.h"
 #include "nsPresContext.h"
 
 struct nsBorderColors;
@@ -56,16 +57,17 @@ typedef enum {
   BorderColorStyleNone,
   BorderColorStyleSolid,
   BorderColorStyleLight,
   BorderColorStyleDark
 } BorderColorStyle;
 
 class nsCSSBorderRenderer final
 {
+  typedef mozilla::gfx::Bezier Bezier;
   typedef mozilla::gfx::ColorPattern ColorPattern;
   typedef mozilla::gfx::DrawTarget DrawTarget;
   typedef mozilla::gfx::Float Float;
   typedef mozilla::gfx::Path Path;
   typedef mozilla::gfx::Point Point;
   typedef mozilla::gfx::Rect Rect;
   typedef mozilla::gfx::RectCornerRadii RectCornerRadii;
   typedef mozilla::gfx::StrokeOptions StrokeOptions;
@@ -161,16 +163,22 @@ private:
   // is taken care of by the ADD compositing.
   already_AddRefed<Path> GetSideClipSubPath(mozilla::css::Side aSide);
 
   // Return start or end point for dashed/dotted side
   Point GetStraightBorderPoint(mozilla::css::Side aSide,
                                mozilla::css::Corner aCorner,
                                bool* aIsUnfilled);
 
+  // Return bezier control points for the outer and the inner curve for given
+  // corner
+  void GetOuterAndInnerBezier(Bezier* aOuterBezier,
+                              Bezier* aInnerBezier,
+                              mozilla::css::Corner aCorner);
+
   // Given a set of sides to fill and a color, do so in the fastest way.
   //
   // Stroke tends to be faster for smaller borders because it doesn't go
   // through the tessellator, which has initialization overhead.  If
   // we're rendering all sides, we can use stroke at any thickness; we
   // also do TL/BR pairs at 1px thickness using stroke.
   //
   // If we can't stroke, then if it's a TL/BR pair, we use the specific
@@ -194,24 +202,36 @@ private:
   void DrawBorderSides (int aSides);
 
   // function used by the above to handle -moz-border-colors
   void DrawBorderSidesCompositeColors(int aSides, const nsBorderColors *compositeColors);
 
   // Setup the stroke options for the given dashed/dotted side
   void SetupDashedOptions(StrokeOptions* aStrokeOptions,
                           Float aDash[2], mozilla::css::Side aSide,
-                          Float aBorderLength);
+                          Float aBorderLength, bool isCorner);
 
   // Draw the given dashed/dotte side
   void DrawDashedOrDottedSide(mozilla::css::Side aSide);
 
   // Draw the given dotted side, each dot separately
   void DrawDottedSideSlow(mozilla::css::Side aSide);
 
+  // Draw the given dashed/dotted corner
+  void DrawDashedOrDottedCorner(mozilla::css::Side aSide,
+                                mozilla::css::Corner aCorner);
+
+  // Draw the given dotted corner, each segment separately
+  void DrawDottedCornerSlow(mozilla::css::Side aSide,
+                            mozilla::css::Corner aCorner);
+
+  // Draw the given dashed corner, each dot separately
+  void DrawDashedCornerSlow(mozilla::css::Side aSide,
+                            mozilla::css::Corner aCorner);
+
   // Analyze if all border sides have the same width.
   bool AllBordersSameWidth();
 
   // Analyze if all borders are 'solid' this also considers hidden or 'none'
   // borders because they can be considered 'solid' borders of 0 width and
   // with no color effect.
   bool AllBordersSolid(bool *aHasCompositeColors);