Bug 382721 - Part 4: Support dotted/dashed border-radiused corners. r=jrmuizel
authorTooru Fujisawa <arai_a@mac.com>
Thu, 06 Aug 2015 11:42:09 +0900
changeset 301551 22e1df7564ca1e04a6e1410e4348336a14d3a62f
parent 301550 5306a743dbdfbb4cd223fdc0498bcf91d5942abe
child 301552 f681dfc661e79c5ea57650effda2d6eb217a36d2
push id30336
push usercbook@mozilla.com
push dateSun, 12 Jun 2016 09:26:58 +0000
treeherdermozilla-central@016e0f47e8ad [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjrmuizel
bugs382721
milestone50.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 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.001f;
+    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.001f;
+  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 "nsAutoPtr.h"
 #include "nsStyleChangeList.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);