Bug 1487407 - Properly support beveling bc borders in WR. r=Gankro
authorEmilio Cobos Álvarez <emilio@crisal.io>
Thu, 30 Aug 2018 00:35:03 +0200
changeset 438338 198999ea1b7962235bfc050b79e675a98cfd76a1
parent 438337 a7f1d2b04291a97a44fc08160b33cf3b0488127c
child 438339 95dfb8f992a6a376ce53480c15b6a6c78df625b0
push id34717
push usershindli@mozilla.com
push dateWed, 26 Sep 2018 21:52:33 +0000
treeherdermozilla-central@7ac88abc3c57 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersGankro
bugs1487407
milestone64.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 1487407 - Properly support beveling bc borders in WR. r=Gankro The previous border-collapse beveling implementation assumed that there would only be one beveled border per side in the whole table, which is... not true at all. So a bunch of borders ended up clobbering other values in mBevelBorders and never getting painted. I'm actually somewhat scarily surprised that only this reftest seems to fail without this patch... Here we reuse most of the existing one-off beveling / border rendering support in nsCSSRendering, and convert the Gecko bevels into a WebRender display list using rects and borders. This is only remotely possible thanks to Gecko not supporting dotted / dashed beveled borders :) This would slightly easier and presumably also more efficient with a triangle display item in WR instead of (ab)using the border display item to render the bevel, but this is probably relatively edge-casey so maybe not worth it... In any case I've left a TODO comment there, that can be a nice followup if we deem it worth it. Anyway, I'm _so_ sorry for the border trick, I was this (||) close to go and rewrite our border collapsing code, but after a few tries I realized it'd take me a whole lot of time (instead of the day that this has taken me). Differential Revision: https://phabricator.services.mozilla.com/D4793
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
layout/painting/nsCSSRendering.cpp
layout/painting/nsCSSRendering.h
layout/reftests/table-bordercollapse/reftest.list
layout/reftests/writing-mode/tables/reftest.list
layout/tables/nsTableFrame.cpp
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -1183,24 +1183,26 @@ DisplayListBuilder::PushIFrame(const wr:
 }
 
 void
 DisplayListBuilder::PushBorder(const wr::LayoutRect& aBounds,
                                const wr::LayoutRect& aClip,
                                bool aIsBackfaceVisible,
                                const wr::LayoutSideOffsets& aWidths,
                                const Range<const wr::BorderSide>& aSides,
-                               const wr::BorderRadius& aRadius)
+                               const wr::BorderRadius& aRadius,
+                               wr::AntialiasBorder aAntialias)
 {
   MOZ_ASSERT(aSides.length() == 4);
   if (aSides.length() != 4) {
     return;
   }
   wr_dp_push_border(mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
-                    aWidths, aSides[0], aSides[1], aSides[2], aSides[3], aRadius);
+                    aAntialias, aWidths, aSides[0], aSides[1], aSides[2],
+                    aSides[3], aRadius);
 }
 
 void
 DisplayListBuilder::PushBorderImage(const wr::LayoutRect& aBounds,
                                     const wr::LayoutRect& aClip,
                                     bool aIsBackfaceVisible,
                                     const wr::LayoutSideOffsets& aWidths,
                                     wr::ImageKey aImage,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -433,17 +433,18 @@ public:
 
   // XXX WrBorderSides are passed with Range.
   // It is just to bypass compiler bug. See Bug 1357734.
   void PushBorder(const wr::LayoutRect& aBounds,
                   const wr::LayoutRect& aClip,
                   bool aIsBackfaceVisible,
                   const wr::LayoutSideOffsets& aWidths,
                   const Range<const wr::BorderSide>& aSides,
-                  const wr::BorderRadius& aRadius);
+                  const wr::BorderRadius& aRadius,
+                  wr::AntialiasBorder = wr::AntialiasBorder::Yes);
 
   void PushBorderImage(const wr::LayoutRect& aBounds,
                        const wr::LayoutRect& aClip,
                        bool aIsBackfaceVisible,
                        const wr::LayoutSideOffsets& aWidths,
                        wr::ImageKey aImage,
                        const uint32_t aWidth,
                        const uint32_t aHeight,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -24,16 +24,24 @@ use nsstring::nsAString;
 #[cfg(target_os = "windows")]
 use dwrote::{FontDescriptor, FontWeight, FontStretch, FontStyle};
 
 #[cfg(target_os = "macos")]
 use core_foundation::string::CFString;
 #[cfg(target_os = "macos")]
 use core_graphics::font::CGFont;
 
+/// Whether a border should be antialiased.
+#[repr(C)]
+#[derive(Eq, PartialEq, Copy, Clone)]
+pub enum AntialiasBorder {
+    No = 0,
+    Yes,
+}
+
 #[repr(C)]
 pub enum WrExternalImageBufferType {
     TextureHandle = 0,
     TextureRectHandle = 1,
     TextureArrayHandle = 2,
     TextureExternalHandle = 3,
     ExternalBuffer = 4,
 }
@@ -2181,32 +2189,34 @@ pub extern "C" fn wr_dp_push_line(state:
 
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border(state: &mut WrState,
                                     rect: LayoutRect,
                                     clip: LayoutRect,
                                     is_backface_visible: bool,
+                                    do_aa: AntialiasBorder,
                                     widths: LayoutSideOffsets,
                                     top: BorderSide,
                                     right: BorderSide,
                                     bottom: BorderSide,
                                     left: BorderSide,
                                     radius: BorderRadius) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     let border_details = BorderDetails::Normal(NormalBorder {
-                                                   left: left.into(),
-                                                   right: right.into(),
-                                                   top: top.into(),
-                                                   bottom: bottom.into(),
-                                                   radius: radius.into(),
-                                                   do_aa: true,
-                                               });
+        left: left.into(),
+        right: right.into(),
+        top: top.into(),
+        bottom: bottom.into(),
+        radius: radius.into(),
+        do_aa: do_aa == AntialiasBorder::Yes,
+    });
+
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
     prim_info.tag = state.current_tag;
     state.frame_builder
          .dl_builder
          .push_border(&prim_info,
                       widths,
                       border_details);
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -16,16 +16,24 @@
 
 namespace mozilla {
 namespace wr {
 
 static const uint32_t MAX_CACHED_PROGRAM_COUNT = 15;
 
 static const uint64_t MAX_LOAD_TIME_MS = 400;
 
+// Whether a border should be antialiased.
+enum class AntialiasBorder {
+  No = 0,
+  Yes,
+
+  Sentinel /* this must be last for serialization purposes. */
+};
+
 enum class BorderStyle : uint32_t {
   None = 0,
   Solid = 1,
   Double = 2,
   Dotted = 3,
   Dashed = 4,
   Hidden = 5,
   Groove = 6,
@@ -1280,16 +1288,17 @@ void wr_dp_pop_stacking_context(WrState 
                                 bool aIsReferenceFrame)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_border(WrState *aState,
                        LayoutRect aRect,
                        LayoutRect aClip,
                        bool aIsBackfaceVisible,
+                       AntialiasBorder aDoAa,
                        LayoutSideOffsets aWidths,
                        BorderSide aTop,
                        BorderSide aRight,
                        BorderSide aBottom,
                        BorderSide aLeft,
                        BorderRadius aRadius)
 WR_FUNC;
 
--- a/layout/painting/nsCSSRendering.cpp
+++ b/layout/painting/nsCSSRendering.cpp
@@ -3793,17 +3793,16 @@ nsCSSRendering::DrawTableBorderSegment(D
                                        mozilla::Side aStartBevelSide,
                                        nscoord aStartBevelOffset,
                                        mozilla::Side aEndBevelSide,
                                        nscoord aEndBevelOffset)
 {
   bool horizontal =
     ((eSideTop == aStartBevelSide) || (eSideBottom == aStartBevelSide));
   nscoord oneDevPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerDevPixel);
-  uint8_t ridgeGroove = NS_STYLE_BORDER_STYLE_RIDGE;
 
   if ((oneDevPixel >= aBorder.width) || (oneDevPixel >= aBorder.height) ||
       (NS_STYLE_BORDER_STYLE_DASHED == aBorderStyle) ||
       (NS_STYLE_BORDER_STYLE_DOTTED == aBorderStyle)) {
     // no beveling for 1 pixel border, dash or dot
     aStartBevelOffset = 0;
     aEndBevelOffset = 0;
   }
@@ -3875,259 +3874,275 @@ nsCSSRendering::DrawTableBorderSegment(D
                           aAppUnitsPerDevPixel,
                           horizontal);
 
         rect.y += rect.height;
         rect.height = endDashLength;
         DrawSolidBorderSegment(
           aDrawTarget, rect, aBorderColor, aAppUnitsPerDevPixel);
       }
-    } break;
-    case NS_STYLE_BORDER_STYLE_GROOVE:
-      ridgeGroove = NS_STYLE_BORDER_STYLE_GROOVE; // and fall through to ridge
-      MOZ_FALLTHROUGH;
-    case NS_STYLE_BORDER_STYLE_RIDGE:
-      if ((horizontal && (oneDevPixel >= aBorder.height)) ||
-          (!horizontal && (oneDevPixel >= aBorder.width))) {
-        // a one pixel border
-        DrawSolidBorderSegment(aDrawTarget,
-                               aBorder,
-                               aBorderColor,
-                               aAppUnitsPerDevPixel,
-                               aStartBevelSide,
-                               aStartBevelOffset,
-                               aEndBevelSide,
-                               aEndBevelOffset);
-      } else {
-        nscoord startBevel =
-          (aStartBevelOffset > 0)
-            ? RoundFloatToPixel(
-                0.5f * (float)aStartBevelOffset, aAppUnitsPerDevPixel, true)
-            : 0;
-        nscoord endBevel = (aEndBevelOffset > 0)
-                             ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset,
-                                                 aAppUnitsPerDevPixel,
-                                                 true)
-                             : 0;
-        mozilla::Side ridgeGrooveSide = (horizontal) ? eSideTop : eSideLeft;
-        // FIXME: In theory, this should use the visited-dependent
-        // background color, but I don't care.
-        nscolor bevelColor =
-          MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor, aBorderColor);
-        nsRect rect(aBorder);
-        nscoord half;
-        if (horizontal) { // top, bottom
-          half = RoundFloatToPixel(0.5f * (float)aBorder.height,
-                                   aAppUnitsPerDevPixel);
-          rect.height = half;
-          if (eSideTop == aStartBevelSide) {
-            rect.x += startBevel;
-            rect.width -= startBevel;
-          }
-          if (eSideTop == aEndBevelSide) {
-            rect.width -= endBevel;
-          }
-          DrawSolidBorderSegment(aDrawTarget,
-                                 rect,
-                                 bevelColor,
-                                 aAppUnitsPerDevPixel,
-                                 aStartBevelSide,
-                                 startBevel,
-                                 aEndBevelSide,
-                                 endBevel);
-        } else { // left, right
-          half = RoundFloatToPixel(0.5f * (float)aBorder.width,
-                                   aAppUnitsPerDevPixel);
-          rect.width = half;
-          if (eSideLeft == aStartBevelSide) {
-            rect.y += startBevel;
-            rect.height -= startBevel;
-          }
-          if (eSideLeft == aEndBevelSide) {
-            rect.height -= endBevel;
-          }
-          DrawSolidBorderSegment(aDrawTarget,
-                                 rect,
-                                 bevelColor,
-                                 aAppUnitsPerDevPixel,
-                                 aStartBevelSide,
-                                 startBevel,
-                                 aEndBevelSide,
-                                 endBevel);
+    }
+    break;
+  default:
+    AutoTArray<SolidBeveledBorderSegment, 3> segments;
+    GetTableBorderSolidSegments(segments,
+                                aBorderStyle,
+                                aBorderColor,
+                                aBGColor,
+                                aBorder,
+                                aAppUnitsPerDevPixel,
+                                aStartBevelSide,
+                                aStartBevelOffset,
+                                aEndBevelSide,
+                                aEndBevelOffset);
+    for (const auto& segment : segments) {
+      DrawSolidBorderSegment(aDrawTarget,
+                             segment.mRect,
+                             segment.mColor,
+                             aAppUnitsPerDevPixel,
+                             segment.mStartBevel.mSide,
+                             segment.mStartBevel.mOffset,
+                             segment.mEndBevel.mSide,
+                             segment.mEndBevel.mOffset);
+    }
+    break;
+  }
+}
+
+void
+nsCSSRendering::GetTableBorderSolidSegments(
+    nsTArray<SolidBeveledBorderSegment>& aSegments,
+    uint8_t       aBorderStyle,
+    nscolor       aBorderColor,
+    nscolor       aBGColor,
+    const nsRect& aBorder,
+    int32_t       aAppUnitsPerDevPixel,
+    mozilla::Side aStartBevelSide,
+    nscoord       aStartBevelOffset,
+    mozilla::Side aEndBevelSide,
+    nscoord       aEndBevelOffset)
+{
+  const bool horizontal = eSideTop == aStartBevelSide || eSideBottom == aStartBevelSide;
+  const nscoord oneDevPixel = NSIntPixelsToAppUnits(1, aAppUnitsPerDevPixel);
+
+  switch (aBorderStyle) {
+  case NS_STYLE_BORDER_STYLE_NONE:
+  case NS_STYLE_BORDER_STYLE_HIDDEN:
+    return;
+  case NS_STYLE_BORDER_STYLE_DOTTED:
+  case NS_STYLE_BORDER_STYLE_DASHED:
+    MOZ_ASSERT_UNREACHABLE("Caller should have checked");
+    return;
+  case NS_STYLE_BORDER_STYLE_GROOVE:
+  case NS_STYLE_BORDER_STYLE_RIDGE:
+    if ((horizontal && (oneDevPixel >= aBorder.height)) ||
+        (!horizontal && (oneDevPixel >= aBorder.width))) {
+      aSegments.AppendElement(SolidBeveledBorderSegment {
+        aBorder,
+        aBorderColor,
+        { aStartBevelSide, aStartBevelOffset },
+        { aEndBevelSide, aEndBevelOffset }
+      });
+    } else {
+      nscoord startBevel = (aStartBevelOffset > 0)
+                            ? RoundFloatToPixel(0.5f * (float)aStartBevelOffset,
+                                                aAppUnitsPerDevPixel, true) : 0;
+      nscoord endBevel =   (aEndBevelOffset > 0)
+                            ? RoundFloatToPixel(0.5f * (float)aEndBevelOffset,
+                                                aAppUnitsPerDevPixel, true) : 0;
+      mozilla::Side ridgeGrooveSide = (horizontal) ? eSideTop : eSideLeft;
+      // FIXME: In theory, this should use the visited-dependent
+      // background color, but I don't care.
+      nscolor bevelColor = MakeBevelColor(ridgeGrooveSide, aBorderStyle,
+                                          aBGColor, aBorderColor);
+      nsRect rect(aBorder);
+      nscoord half;
+      if (horizontal) { // top, bottom
+        half = RoundFloatToPixel(0.5f * (float)aBorder.height,
+                                 aAppUnitsPerDevPixel);
+        rect.height = half;
+        if (eSideTop == aStartBevelSide) {
+          rect.x += startBevel;
+          rect.width -= startBevel;
         }
-
-        rect = aBorder;
-        ridgeGrooveSide =
-          (eSideTop == ridgeGrooveSide) ? eSideBottom : eSideRight;
-        // FIXME: In theory, this should use the visited-dependent
-        // background color, but I don't care.
-        bevelColor =
-          MakeBevelColor(ridgeGrooveSide, ridgeGroove, aBGColor, aBorderColor);
-        if (horizontal) {
-          rect.y = rect.y + half;
-          rect.height = aBorder.height - half;
-          if (eSideBottom == aStartBevelSide) {
-            rect.x += startBevel;
-            rect.width -= startBevel;
-          }
-          if (eSideBottom == aEndBevelSide) {
-            rect.width -= endBevel;
-          }
-          DrawSolidBorderSegment(aDrawTarget,
-                                 rect,
-                                 bevelColor,
-                                 aAppUnitsPerDevPixel,
-                                 aStartBevelSide,
-                                 startBevel,
-                                 aEndBevelSide,
-                                 endBevel);
-        } else {
-          rect.x = rect.x + half;
-          rect.width = aBorder.width - half;
-          if (eSideRight == aStartBevelSide) {
-            rect.y += aStartBevelOffset - startBevel;
-            rect.height -= startBevel;
-          }
-          if (eSideRight == aEndBevelSide) {
-            rect.height -= endBevel;
-          }
-          DrawSolidBorderSegment(aDrawTarget,
-                                 rect,
-                                 bevelColor,
-                                 aAppUnitsPerDevPixel,
-                                 aStartBevelSide,
-                                 startBevel,
-                                 aEndBevelSide,
-                                 endBevel);
+        if (eSideTop == aEndBevelSide) {
+          rect.width -= endBevel;
         }
+        aSegments.AppendElement(SolidBeveledBorderSegment {
+          rect,
+          bevelColor,
+          { aStartBevelSide, startBevel },
+          { aEndBevelSide, endBevel }
+        });
+      } else { // left, right
+        half = RoundFloatToPixel(0.5f * (float)aBorder.width,
+                                 aAppUnitsPerDevPixel);
+        rect.width = half;
+        if (eSideLeft == aStartBevelSide) {
+          rect.y += startBevel;
+          rect.height -= startBevel;
+        }
+        if (eSideLeft == aEndBevelSide) {
+          rect.height -= endBevel;
+        }
+        aSegments.AppendElement(SolidBeveledBorderSegment {
+          rect,
+          bevelColor,
+          { aStartBevelSide, startBevel },
+          { aEndBevelSide, endBevel }
+        });
       }
-      break;
-    case NS_STYLE_BORDER_STYLE_DOUBLE:
-      // We can only do "double" borders if the thickness of the border
-      // is more than 2px.  Otherwise, we fall through to painting a
-      // solid border.
-      if ((aBorder.width > 2 * oneDevPixel || horizontal) &&
-          (aBorder.height > 2 * oneDevPixel || !horizontal)) {
-        nscoord startBevel =
-          (aStartBevelOffset > 0)
-            ? RoundFloatToPixel(0.333333f * (float)aStartBevelOffset,
-                                aAppUnitsPerDevPixel)
-            : 0;
-        nscoord endBevel =
-          (aEndBevelOffset > 0)
-            ? RoundFloatToPixel(0.333333f * (float)aEndBevelOffset,
-                                aAppUnitsPerDevPixel)
-            : 0;
-        if (horizontal) { // top, bottom
-          nscoord thirdHeight = RoundFloatToPixel(
-            0.333333f * (float)aBorder.height, aAppUnitsPerDevPixel);
-
-          // draw the top line or rect
-          nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight);
-          if (eSideTop == aStartBevelSide) {
-            topRect.x += aStartBevelOffset - startBevel;
-            topRect.width -= aStartBevelOffset - startBevel;
-          }
-          if (eSideTop == aEndBevelSide) {
-            topRect.width -= aEndBevelOffset - endBevel;
-          }
-          DrawSolidBorderSegment(aDrawTarget,
-                                 topRect,
-                                 aBorderColor,
-                                 aAppUnitsPerDevPixel,
-                                 aStartBevelSide,
-                                 startBevel,
-                                 aEndBevelSide,
-                                 endBevel);
-
-          // draw the botom line or rect
-          nscoord heightOffset = aBorder.height - thirdHeight;
-          nsRect bottomRect(aBorder.x,
-                            aBorder.y + heightOffset,
-                            aBorder.width,
-                            aBorder.height - heightOffset);
-          if (eSideBottom == aStartBevelSide) {
-            bottomRect.x += aStartBevelOffset - startBevel;
-            bottomRect.width -= aStartBevelOffset - startBevel;
-          }
-          if (eSideBottom == aEndBevelSide) {
-            bottomRect.width -= aEndBevelOffset - endBevel;
-          }
-          DrawSolidBorderSegment(aDrawTarget,
-                                 bottomRect,
-                                 aBorderColor,
-                                 aAppUnitsPerDevPixel,
-                                 aStartBevelSide,
-                                 startBevel,
-                                 aEndBevelSide,
-                                 endBevel);
-        } else { // left, right
-          nscoord thirdWidth = RoundFloatToPixel(
-            0.333333f * (float)aBorder.width, aAppUnitsPerDevPixel);
-
-          nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height);
-          if (eSideLeft == aStartBevelSide) {
-            leftRect.y += aStartBevelOffset - startBevel;
-            leftRect.height -= aStartBevelOffset - startBevel;
-          }
-          if (eSideLeft == aEndBevelSide) {
-            leftRect.height -= aEndBevelOffset - endBevel;
-          }
-          DrawSolidBorderSegment(aDrawTarget,
-                                 leftRect,
-                                 aBorderColor,
-                                 aAppUnitsPerDevPixel,
-                                 aStartBevelSide,
-                                 startBevel,
-                                 aEndBevelSide,
-                                 endBevel);
-
-          nscoord widthOffset = aBorder.width - thirdWidth;
-          nsRect rightRect(aBorder.x + widthOffset,
-                           aBorder.y,
-                           aBorder.width - widthOffset,
-                           aBorder.height);
-          if (eSideRight == aStartBevelSide) {
-            rightRect.y += aStartBevelOffset - startBevel;
-            rightRect.height -= aStartBevelOffset - startBevel;
-          }
-          if (eSideRight == aEndBevelSide) {
-            rightRect.height -= aEndBevelOffset - endBevel;
-          }
-          DrawSolidBorderSegment(aDrawTarget,
-                                 rightRect,
-                                 aBorderColor,
-                                 aAppUnitsPerDevPixel,
-                                 aStartBevelSide,
-                                 startBevel,
-                                 aEndBevelSide,
-                                 endBevel);
+
+      rect = aBorder;
+      ridgeGrooveSide = (eSideTop == ridgeGrooveSide) ? eSideBottom : eSideRight;
+      // FIXME: In theory, this should use the visited-dependent
+      // background color, but I don't care.
+      bevelColor = MakeBevelColor(ridgeGrooveSide, aBorderStyle,
+                                  aBGColor, aBorderColor);
+      if (horizontal) {
+        rect.y = rect.y + half;
+        rect.height = aBorder.height - half;
+        if (eSideBottom == aStartBevelSide) {
+          rect.x += startBevel;
+          rect.width -= startBevel;
+        }
+        if (eSideBottom == aEndBevelSide) {
+          rect.width -= endBevel;
+        }
+        aSegments.AppendElement(SolidBeveledBorderSegment {
+          rect,
+          bevelColor,
+          { aStartBevelSide, startBevel },
+          { aEndBevelSide, endBevel }
+        });
+      } else {
+        rect.x = rect.x + half;
+        rect.width = aBorder.width - half;
+        if (eSideRight == aStartBevelSide) {
+          rect.y += aStartBevelOffset - startBevel;
+          rect.height -= startBevel;
+        }
+        if (eSideRight == aEndBevelSide) {
+          rect.height -= endBevel;
+        }
+        aSegments.AppendElement(SolidBeveledBorderSegment {
+          rect,
+          bevelColor,
+          { aStartBevelSide, startBevel },
+          { aEndBevelSide, endBevel }
+        });
+      }
+    }
+    break;
+  case NS_STYLE_BORDER_STYLE_DOUBLE:
+    // We can only do "double" borders if the thickness of the border
+    // is more than 2px.  Otherwise, we fall through to painting a
+    // solid border.
+    if ((aBorder.width > 2 * oneDevPixel || horizontal) &&
+        (aBorder.height > 2 * oneDevPixel || !horizontal)) {
+      nscoord startBevel = (aStartBevelOffset > 0)
+                            ? RoundFloatToPixel(0.333333f *
+                                                (float)aStartBevelOffset,
+                                                 aAppUnitsPerDevPixel) : 0;
+      nscoord endBevel =   (aEndBevelOffset > 0)
+                            ? RoundFloatToPixel(0.333333f *
+                                                (float)aEndBevelOffset,
+                                                aAppUnitsPerDevPixel) : 0;
+      if (horizontal) { // top, bottom
+        nscoord thirdHeight = RoundFloatToPixel(0.333333f *
+                                                (float)aBorder.height,
+                                                aAppUnitsPerDevPixel);
+
+        // draw the top line or rect
+        nsRect topRect(aBorder.x, aBorder.y, aBorder.width, thirdHeight);
+        if (eSideTop == aStartBevelSide) {
+          topRect.x += aStartBevelOffset - startBevel;
+          topRect.width -= aStartBevelOffset - startBevel;
+        }
+        if (eSideTop == aEndBevelSide) {
+          topRect.width -= aEndBevelOffset - endBevel;
         }
-        break;
+
+        aSegments.AppendElement(SolidBeveledBorderSegment {
+          topRect,
+          aBorderColor,
+          { aStartBevelSide, startBevel },
+          { aEndBevelSide, endBevel }
+        });
+
+        // draw the botom line or rect
+        nscoord heightOffset = aBorder.height - thirdHeight;
+        nsRect bottomRect(aBorder.x, aBorder.y + heightOffset, aBorder.width, aBorder.height - heightOffset);
+        if (eSideBottom == aStartBevelSide) {
+          bottomRect.x += aStartBevelOffset - startBevel;
+          bottomRect.width -= aStartBevelOffset - startBevel;
+        }
+        if (eSideBottom == aEndBevelSide) {
+          bottomRect.width -= aEndBevelOffset - endBevel;
+        }
+        aSegments.AppendElement(SolidBeveledBorderSegment {
+          bottomRect,
+          aBorderColor,
+          { aStartBevelSide, startBevel },
+          { aEndBevelSide, endBevel }
+        });
+      } else { // left, right
+        nscoord thirdWidth = RoundFloatToPixel(0.333333f * (float)aBorder.width,
+                                               aAppUnitsPerDevPixel);
+
+        nsRect leftRect(aBorder.x, aBorder.y, thirdWidth, aBorder.height);
+        if (eSideLeft == aStartBevelSide) {
+          leftRect.y += aStartBevelOffset - startBevel;
+          leftRect.height -= aStartBevelOffset - startBevel;
+        }
+        if (eSideLeft == aEndBevelSide) {
+          leftRect.height -= aEndBevelOffset - endBevel;
+        }
+
+        aSegments.AppendElement(SolidBeveledBorderSegment {
+          leftRect,
+          aBorderColor,
+          { aStartBevelSide, startBevel },
+          { aEndBevelSide, endBevel }
+        });
+
+        nscoord widthOffset = aBorder.width - thirdWidth;
+        nsRect rightRect(aBorder.x + widthOffset, aBorder.y, aBorder.width - widthOffset, aBorder.height);
+        if (eSideRight == aStartBevelSide) {
+          rightRect.y += aStartBevelOffset - startBevel;
+          rightRect.height -= aStartBevelOffset - startBevel;
+        }
+        if (eSideRight == aEndBevelSide) {
+          rightRect.height -= aEndBevelOffset - endBevel;
+        }
+        aSegments.AppendElement(SolidBeveledBorderSegment {
+          rightRect,
+          aBorderColor,
+          { aStartBevelSide, startBevel },
+          { aEndBevelSide, endBevel }
+        });
       }
-      // else fall through to solid
-      MOZ_FALLTHROUGH;
-    case NS_STYLE_BORDER_STYLE_SOLID:
-      DrawSolidBorderSegment(aDrawTarget,
-                             aBorder,
-                             aBorderColor,
-                             aAppUnitsPerDevPixel,
-                             aStartBevelSide,
-                             aStartBevelOffset,
-                             aEndBevelSide,
-                             aEndBevelOffset);
-      break;
-    case NS_STYLE_BORDER_STYLE_OUTSET:
-    case NS_STYLE_BORDER_STYLE_INSET:
-      NS_ASSERTION(false,
-                   "inset, outset should have been converted to groove, ridge");
-      break;
-    case NS_STYLE_BORDER_STYLE_AUTO:
-      NS_ASSERTION(false, "Unexpected 'auto' table border");
-      break;
+    }
+    // else fall through to solid
+    MOZ_FALLTHROUGH;
+  case NS_STYLE_BORDER_STYLE_SOLID:
+    aSegments.AppendElement(SolidBeveledBorderSegment {
+      aBorder,
+      aBorderColor,
+      { aStartBevelSide, aStartBevelOffset },
+      { aEndBevelSide, aEndBevelOffset }
+    });
+    break;
+  case NS_STYLE_BORDER_STYLE_OUTSET:
+  case NS_STYLE_BORDER_STYLE_INSET:
+    MOZ_ASSERT_UNREACHABLE("inset, outset should have been converted to groove, ridge");
+    break;
+  case NS_STYLE_BORDER_STYLE_AUTO:
+    MOZ_ASSERT_UNREACHABLE("Unexpected 'auto' table border");
+    break;
   }
 }
 
 // End table border-collapsing section
 
 Rect
 nsCSSRendering::ExpandPaintingRectForDecorationLine(nsIFrame* aFrame,
                                                     const uint8_t aStyle,
--- a/layout/painting/nsCSSRendering.h
+++ b/layout/painting/nsCSSRendering.h
@@ -586,29 +586,59 @@ struct nsCSSRendering
   static void BeginFrameTreesLocked();
   /**
    * Called when we've finished using a display list. When all
    * BeginFrameTreeLocked calls have been balanced by an EndFrameTreeLocked,
    * the frame tree may start changing again.
    */
   static void EndFrameTreesLocked();
 
-  // Draw a border segment in the table collapsing border model without
-  // beveling corners
+  // Draw a border segment in the table collapsing border model with beveling
+  // corners.
   static void DrawTableBorderSegment(
     DrawTarget& aDrawTarget,
     uint8_t aBorderStyle,
     nscolor aBorderColor,
     nscolor aBGColor,
     const nsRect& aBorderRect,
     int32_t aAppUnitsPerDevPixel,
-    mozilla::Side aStartBevelSide = mozilla::eSideTop,
-    nscoord aStartBevelOffset = 0,
-    mozilla::Side aEndBevelSide = mozilla::eSideTop,
-    nscoord aEndBevelOffset = 0);
+    mozilla::Side aStartBevelSide,
+    nscoord aStartBevelOffset,
+    mozilla::Side aEndBevelSide,
+    nscoord aEndBevelOffset);
+
+  // A single border bevel.
+  struct Bevel
+  {
+    mozilla::Side mSide;
+    nscoord mOffset;
+  };
+
+  // A single solid beveled border segment.
+  struct SolidBeveledBorderSegment
+  {
+    nsRect mRect;
+    nscolor mColor;
+    Bevel mStartBevel;
+    Bevel mEndBevel;
+  };
+
+  // Collect the table border segments with beveling. Can't be called with
+  // dashed / dotted borders, since we don't support beveling those.
+  static void GetTableBorderSolidSegments(
+      nsTArray<SolidBeveledBorderSegment>& aSegments,
+      uint8_t aBorderStyle,
+      nscolor aBorderColor,
+      nscolor aBGColor,
+      const nsRect& aBorderRect,
+      int32_t aAppUnitsPerDevPixel,
+      mozilla::Side aStartBevelSide,
+      nscoord aStartBevelOffset,
+      mozilla::Side aEndBevelSide,
+      nscoord aEndBevelOffset);
 
   // NOTE: pt, dirtyRect, lineSize, ascent, offset in the following
   //       structs are non-rounded device pixels, not app units.
   struct DecorationRectParams
   {
     // The width [length] and the height [thickness] of the decoration
     // line. This is a "logical" size in textRun orientation, so that
     // for a vertical textrun, width will actually be a physical height;
--- a/layout/reftests/table-bordercollapse/reftest.list
+++ b/layout/reftests/table-bordercollapse/reftest.list
@@ -102,19 +102,21 @@ fails-if(webrender) == frame_vsides_rule
 == borderhandling-rules-border-all-strict.html borderhandling-rules-border-all-strict-ref.html
 == bordercolor-1.html bordercolor-ref.html
 != bordercolor-2.html bordercolor-ref.html
 == bordercolor-3.html bordercolor-3-ref.html
 == bordercolor-4.html bordercolor-4-ref.html
 == empty-toprow.html empty-toprow-ref.html
 == double_borders.html double_borders_ref.html
 == border-collapse-rtl.html border-collapse-rtl-ref.html
-# Fuzzy because for some reason the corner beveling is antialiased differently.
+# Fuzzy because border-collapsed borders are not antialiased, since each segment is painted separately.
 # So get 40 pixels of fuzz, 20 at each beveled corner (because the border width
 # is 20px).
-fuzzy(0-255,0-40) == border-style-outset-becomes-groove.html border-style-outset-becomes-groove-ref.html
-# Fuzzy because for some reason the corner beveling is antialiased differently.
+# Bug 1382896 for webrender (since tables use Gecko border colors).
+fails-if(webrender) fuzzy(0-255,0-40) == border-style-outset-becomes-groove.html border-style-outset-becomes-groove-ref.html
+# Fuzzy because border-collapsed borders are not antialiased, since each segment is painted separately.
 # So get 40 pixels of fuzz, 20 at each beveled corner (because the border width
 # is 20px).
-fuzzy(0-255,0-40) == border-style-inset-becomes-ridge.html border-style-inset-becomes-ridge-ref.html
+# Bug 1382896 for webrender (since tables use Gecko border colors).
+fails-if(webrender) fuzzy(0-255,0-40) == border-style-inset-becomes-ridge.html border-style-inset-becomes-ridge-ref.html
 fuzzy(0-2,0-11000) == 1324524.html 1324524-ref.html
 == 1384602-1a.html 1384602-1-ref.html
 == 1384602-1b.html 1384602-1-ref.html
--- a/layout/reftests/writing-mode/tables/reftest.list
+++ b/layout/reftests/writing-mode/tables/reftest.list
@@ -4,17 +4,17 @@
 fuzzy-if(skiaContent,0-3,0-750) == vertical-table-2b.html vertical-table-2-ref.html
 == vertical-table-rowspan-1.html vertical-table-rowspan-1-ref.html
 == vertical-table-rowspan-2.html vertical-table-rowspan-2-ref.html
 == vertical-table-colspan-1.html vertical-table-colspan-1-ref.html
 == vertical-table-colspan-2.html vertical-table-colspan-2-ref.html
 == vertical-table-specified-width-1.html vertical-table-specified-width-1-ref.html
 asserts(1) == vertical-table-specified-width-2.html vertical-table-specified-width-2-ref.html # bug 1179741
 fuzzy-if(cocoaWidget,0-141,0-24) == vertical-border-collapse-1.html vertical-border-collapse-1-ref.html
-fuzzy-if(cocoaWidget,0-141,0-24) fails-if(webrender) == vertical-border-collapse-2.html vertical-border-collapse-2-ref.html # bug 1393907 for webrender
+fuzzy-if(cocoaWidget,0-141,0-24) == vertical-border-collapse-2.html vertical-border-collapse-2-ref.html
 
 == fixed-table-layout-002-vlr.html fixed-table-layout-002-ref.html
 == fixed-table-layout-003-vlr.html fixed-table-layout-002-ref.html
 == fixed-table-layout-004-vlr.html fixed-table-layout-004-ref.html
 == fixed-table-layout-005-vlr.html fixed-table-layout-005-ref.html
 == fixed-table-layout-006-vlr.html fixed-table-layout-006-ref.html
 == fixed-table-layout-007-vlr.html fixed-table-layout-007-ref.html
 == fixed-table-layout-009-vlr.html fixed-table-layout-009-ref.html
--- a/layout/tables/nsTableFrame.cpp
+++ b/layout/tables/nsTableFrame.cpp
@@ -1238,36 +1238,35 @@ nsDisplayTableItem::ComputeInvalidationR
   }
 
   nsDisplayItem::ComputeInvalidationRegion(aBuilder, aGeometry, aInvalidRegion);
 }
 
 // A display item that draws all collapsed borders for a table.
 // At some point, we may want to find a nicer partitioning for dividing
 // border-collapse segments into their own display items.
-class nsDisplayTableBorderCollapse : public nsDisplayTableItem {
+class nsDisplayTableBorderCollapse final : public nsDisplayTableItem {
 public:
   nsDisplayTableBorderCollapse(nsDisplayListBuilder* aBuilder,
                                nsTableFrame* aFrame)
     : nsDisplayTableItem(aBuilder, aFrame) {
     MOZ_COUNT_CTOR(nsDisplayTableBorderCollapse);
     }
 #ifdef NS_BUILD_REFCNT_LOGGING
   virtual ~nsDisplayTableBorderCollapse() {
     MOZ_COUNT_DTOR(nsDisplayTableBorderCollapse);
   }
 #endif
 
-  virtual void Paint(nsDisplayListBuilder* aBuilder,
-                     gfxContext* aCtx) override;
-  virtual bool CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
-                                       wr::IpcResourceUpdateQueue& aResources,
-                                       const StackingContextHelper& aSc,
-                                       mozilla::layers::WebRenderLayerManager* aManager,
-                                       nsDisplayListBuilder* aDisplayListBuilder) override;
+  void Paint(nsDisplayListBuilder* aBuilder, gfxContext* aCtx) override;
+  bool CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
+                               wr::IpcResourceUpdateQueue& aResources,
+                               const StackingContextHelper& aSc,
+                               layers::WebRenderLayerManager* aManager,
+                               nsDisplayListBuilder* aDisplayListBuilder) override;
   NS_DISPLAY_DECL_NAME("TableBorderCollapse", TYPE_TABLE_BORDER_COLLAPSE)
 };
 
 void
 nsDisplayTableBorderCollapse::Paint(nsDisplayListBuilder* aBuilder,
                                     gfxContext* aCtx)
 {
   nsPoint pt = ToReferenceFrame();
@@ -1281,26 +1280,27 @@ nsDisplayTableBorderCollapse::Paint(nsDi
   AutoRestoreTransform autoRestoreTransform(drawTarget);
   drawTarget->SetTransform(
       drawTarget->GetTransform().PreTranslate(ToPoint(devPixelOffset)));
 
   static_cast<nsTableFrame*>(mFrame)->PaintBCBorders(*drawTarget, GetPaintRect() - pt);
 }
 
 bool
-nsDisplayTableBorderCollapse::CreateWebRenderCommands(mozilla::wr::DisplayListBuilder& aBuilder,
+nsDisplayTableBorderCollapse::CreateWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                                                       wr::IpcResourceUpdateQueue& aResources,
                                                       const StackingContextHelper& aSc,
                                                       mozilla::layers::WebRenderLayerManager* aManager,
                                                       nsDisplayListBuilder* aDisplayListBuilder)
 {
-  static_cast<nsTableFrame *>(mFrame)->CreateWebRenderCommandsForBCBorders(aBuilder,
-                                                                          aSc,
-                                                                          GetPaintRect(),
-                                                                          ToReferenceFrame());
+  static_cast<nsTableFrame*>(mFrame)->
+    CreateWebRenderCommandsForBCBorders(aBuilder,
+                                        aSc,
+                                        GetPaintRect(),
+                                        ToReferenceFrame());
   return true;
 }
 
 /* static */ void
 nsTableFrame::GenericTraversal(nsDisplayListBuilder* aBuilder, nsFrame* aFrame,
                                const nsDisplayListSet& aLists)
 {
   // This is similar to what nsContainerFrame::BuildDisplayListForNonBlockChildren
@@ -6500,16 +6500,30 @@ struct BCBorderParameters
   nscolor mBGColor;
   nsRect mBorderRect;
   int32_t mAppUnitsPerDevPixel;
   mozilla::Side mStartBevelSide;
   nscoord mStartBevelOffset;
   mozilla::Side mEndBevelSide;
   nscoord mEndBevelOffset;
   bool mBackfaceIsVisible;
+
+  bool NeedToBevel() const
+  {
+    if (!mStartBevelOffset && !mEndBevelOffset) {
+      return false;
+    }
+
+    if (mBorderStyle == NS_STYLE_BORDER_STYLE_DASHED ||
+        mBorderStyle == NS_STYLE_BORDER_STYLE_DOTTED) {
+      return false;
+    }
+
+    return true;
+  }
 };
 
 struct BCBlockDirSeg
 {
   BCBlockDirSeg();
 
   void Start(BCPaintBorderIterator& aIter,
              BCBorderOwner          aBorderOwner,
@@ -6524,18 +6538,17 @@ struct BCBlockDirSeg
                                                   BCPixelSize aInlineSegBSize);
   void Paint(BCPaintBorderIterator& aIter,
              DrawTarget&            aDrawTarget,
              BCPixelSize            aInlineSegBSize);
   void CreateWebRenderCommands(BCPaintBorderIterator& aIter,
                                BCPixelSize aInlineSegBSize,
                                wr::DisplayListBuilder& aBuilder,
                                const layers::StackingContextHelper& aSc,
-                               const nsPoint& aPt,
-                               Maybe<BCBorderParameters>* aBevelBorders);
+                               const nsPoint& aPt);
   void AdvanceOffsetB();
   void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
 
 
   union {
     nsTableColFrame*  mCol;
     int32_t           mColWidth;
   };
@@ -6579,18 +6592,17 @@ struct BCInlineDirSeg
                      BCPixelSize            aIStartSegISize);
   void AdvanceOffsetI();
   void IncludeCurrentBorder(BCPaintBorderIterator& aIter);
   Maybe<BCBorderParameters> BuildBorderParameters(BCPaintBorderIterator& aIter);
   void Paint(BCPaintBorderIterator& aIter, DrawTarget& aDrawTarget);
   void CreateWebRenderCommands(BCPaintBorderIterator& aIter,
                                wr::DisplayListBuilder& aBuilder,
                                const layers::StackingContextHelper& aSc,
-                               const nsPoint& aPt,
-                               Maybe<BCBorderParameters>* aBevelBorders);
+                               const nsPoint& aPt);
 
   nscoord            mOffsetI;       // i-offset with respect to the table edge
   nscoord            mOffsetB;       // b-offset with respect to the table edge
   nscoord            mLength;        // inline-dir length including corners
   BCPixelSize        mWidth;         // border thickness in pixels
   nscoord            mIStartBevelOffset; // how much to bevel at the iStart
   LogicalSide        mIStartBevelSide;   // direction to bevel at the iStart
   bool               mIsIEndBevel;       // should we bevel at the iEnd end
@@ -6627,34 +6639,32 @@ struct BCCreateWebRenderCommandsData
     , mSc(aSc)
     , mOffsetToReferenceFrame(aOffsetToReferenceFrame)
   {
   }
 
   wr::DisplayListBuilder& mBuilder;
   const layers::StackingContextHelper& mSc;
   const nsPoint& mOffsetToReferenceFrame;
-  Maybe<BCBorderParameters> mBevelBorders[4];
 };
 
 struct BCPaintBorderAction
 {
   explicit BCPaintBorderAction(DrawTarget& aDrawTarget)
     : mMode(Mode::PAINT)
     , mPaintData(aDrawTarget)
   {
   }
 
   BCPaintBorderAction(wr::DisplayListBuilder& aBuilder,
                       const layers::StackingContextHelper& aSc,
                       const nsPoint& aOffsetToReferenceFrame)
     : mMode(Mode::CREATE_WEBRENDER_COMMANDS)
     , mCreateWebRenderCommandsData(aBuilder, aSc, aOffsetToReferenceFrame)
   {
-    mMode = Mode::CREATE_WEBRENDER_COMMANDS;
   }
 
   ~BCPaintBorderAction()
   {
     // mCreateWebRenderCommandsData is in a union which means the destructor
     // wouldn't be called when BCPaintBorderAction get destroyed. So call the
     // destructor here explicitly.
     if (mMode == Mode::CREATE_WEBRENDER_COMMANDS) {
@@ -7494,82 +7504,219 @@ BCBlockDirSeg::Paint(BCPaintBorderIterat
 
   nsCSSRendering::DrawTableBorderSegment(aDrawTarget, param->mBorderStyle, param->mBorderColor,
                                          param->mBGColor, param->mBorderRect,
                                          param->mAppUnitsPerDevPixel,
                                          param->mStartBevelSide, param->mStartBevelOffset,
                                          param->mEndBevelSide, param->mEndBevelOffset);
 }
 
+// Pushes a border bevel triangle and substracts the relevant rectangle from
+// aRect, which, after all the bevels, will end up being a solid segment rect.
+static void
+AdjustAndPushBevel(wr::DisplayListBuilder& aBuilder,
+                   wr::LayoutRect& aRect,
+                   nscolor aColor,
+                   const nsCSSRendering::Bevel& aBevel,
+                   int32_t aAppUnitsPerDevPixel,
+                   bool aBackfaceIsVisible,
+                   bool aIsStart)
+{
+  if (!aBevel.mOffset) {
+    return;
+  }
+
+  const auto kTransparent = wr::ToColorF(gfx::Color(0., 0., 0., 0.));
+  const bool horizontal =
+    aBevel.mSide == eSideTop || aBevel.mSide == eSideBottom;
+
+  // Crappy CSS triangle as known by every web developer ever :)
+  Float offset = NSAppUnitsToFloatPixels(aBevel.mOffset, aAppUnitsPerDevPixel);
+  wr::LayoutRect bevelRect = aRect;
+  wr::BorderSide bevelBorder[4];
+  NS_FOR_CSS_SIDES(i) {
+    bevelBorder[i] = wr::ToBorderSide(ToDeviceColor(aColor), NS_STYLE_BORDER_STYLE_SOLID);
+  }
+
+  // We're creating a half-transparent triangle using the border primitive.
+  //
+  // Classic web-dev trick, with a gotcha: we use a single corner to avoid
+  // seams and rounding errors.
+  //
+  // Classic web-dev trick :P
+  auto borderWidths = wr::ToBorderWidths(0, 0, 0, 0);
+  bevelBorder[aBevel.mSide].color = kTransparent;
+  if (aIsStart) {
+    if (horizontal) {
+      bevelBorder[eSideLeft].color = kTransparent;
+      borderWidths.left = offset;
+    } else {
+      bevelBorder[eSideTop].color = kTransparent;
+      borderWidths.top = offset;
+    }
+  } else {
+    if (horizontal) {
+      bevelBorder[eSideRight].color = kTransparent;
+      borderWidths.right = offset;
+    } else {
+      bevelBorder[eSideBottom].color = kTransparent;
+      borderWidths.bottom = offset;
+    }
+  }
+
+  if (horizontal) {
+    if (aIsStart) {
+      aRect.origin.x += offset;
+    } else {
+      bevelRect.origin.x += aRect.size.width - offset;
+    }
+    aRect.size.width -= offset;
+    bevelRect.size.height = aRect.size.height;
+    bevelRect.size.width = offset;
+    if (aBevel.mSide == eSideTop) {
+      borderWidths.bottom = aRect.size.height;
+    } else {
+      borderWidths.top = aRect.size.height;
+    }
+  } else {
+    if (aIsStart) {
+      aRect.origin.y += offset;
+    } else {
+      bevelRect.origin.y += aRect.size.height - offset;
+    }
+    aRect.size.height -= offset;
+    bevelRect.size.width = aRect.size.width;
+    bevelRect.size.height = offset;
+    if (aBevel.mSide == eSideLeft) {
+      borderWidths.right = aRect.size.width;
+    } else {
+      borderWidths.left = aRect.size.width;
+    }
+  }
+
+  Range<const wr::BorderSide> wrsides(bevelBorder, 4);
+  // It's important to _not_ anti-alias the bevel, because otherwise we wouldn't
+  // be able bevel to sides of the same color without bleeding in the middle.
+  aBuilder.PushBorder(bevelRect,
+                      bevelRect,
+                      aBackfaceIsVisible,
+                      borderWidths,
+                      wrsides,
+                      wr::EmptyBorderRadius(),
+                      wr::AntialiasBorder::No);
+}
+
+static void
+CreateWRCommandsForBeveledBorder(const BCBorderParameters& aBorderParams,
+                                 wr::DisplayListBuilder& aBuilder,
+                                 const layers::StackingContextHelper& aSc,
+                                 const nsPoint& aOffset)
+{
+  MOZ_ASSERT(aBorderParams.NeedToBevel());
+
+  AutoTArray<nsCSSRendering::SolidBeveledBorderSegment, 3> segments;
+  nsCSSRendering::GetTableBorderSolidSegments(segments,
+                                              aBorderParams.mBorderStyle,
+                                              aBorderParams.mBorderColor,
+                                              aBorderParams.mBGColor,
+                                              aBorderParams.mBorderRect,
+                                              aBorderParams.mAppUnitsPerDevPixel,
+                                              aBorderParams.mStartBevelSide,
+                                              aBorderParams.mStartBevelOffset,
+                                              aBorderParams.mEndBevelSide,
+                                              aBorderParams.mEndBevelOffset);
+
+  for (const auto& segment : segments) {
+    auto rect = LayoutDeviceRect::FromUnknownRect(
+      NSRectToRect(segment.mRect + aOffset, aBorderParams.mAppUnitsPerDevPixel));
+    auto roundedRect = wr::ToRoundedLayoutRect(rect);
+    auto color = wr::ToColorF(ToDeviceColor(segment.mColor));
+
+    // Adjust for the start bevel if needed.
+    AdjustAndPushBevel(aBuilder,
+                       roundedRect,
+                       segment.mColor,
+                       segment.mStartBevel,
+                       aBorderParams.mAppUnitsPerDevPixel,
+                       aBorderParams.mBackfaceIsVisible,
+                       true);
+
+    AdjustAndPushBevel(aBuilder,
+                       roundedRect,
+                       segment.mColor,
+                       segment.mEndBevel,
+                       aBorderParams.mAppUnitsPerDevPixel,
+                       aBorderParams.mBackfaceIsVisible,
+                       false);
+
+    aBuilder.PushRect(roundedRect, roundedRect, aBorderParams.mBackfaceIsVisible, color);
+  }
+}
+
+static void
+CreateWRCommandsForBorderSegment(const BCBorderParameters& aBorderParams,
+                                 wr::DisplayListBuilder& aBuilder,
+                                 const layers::StackingContextHelper& aSc,
+                                 const nsPoint& aOffset)
+{
+  if (aBorderParams.NeedToBevel()) {
+    CreateWRCommandsForBeveledBorder(aBorderParams, aBuilder, aSc, aOffset);
+    return;
+  }
+
+  auto borderRect = LayoutDeviceRect::FromUnknownRect(
+      NSRectToRect(aBorderParams.mBorderRect + aOffset,
+                   aBorderParams.mAppUnitsPerDevPixel));
+
+  wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(borderRect);
+  wr::BorderSide wrSide[4];
+  NS_FOR_CSS_SIDES(i) {
+    wrSide[i] = wr::ToBorderSide(ToDeviceColor(aBorderParams.mBorderColor), NS_STYLE_BORDER_STYLE_NONE);
+  }
+  const bool horizontal = aBorderParams.mStartBevelSide == eSideTop ||
+                          aBorderParams.mStartBevelSide == eSideBottom;
+  auto borderWidth = horizontal ? roundedRect.size.height : roundedRect.size.width;
+
+  // All border style is set to none except left side. So setting the widths of
+  // each side to width of rect is fine.
+  auto borderWidths =
+    wr::ToBorderWidths(0, 0, 0, 0);
+
+  wrSide[horizontal ? eSideTop : eSideLeft] =
+    wr::ToBorderSide(ToDeviceColor(aBorderParams.mBorderColor),
+                     aBorderParams.mBorderStyle);
+
+  if (horizontal) {
+    borderWidths.top = borderWidth;
+  } else {
+    borderWidths.left = borderWidth;
+  }
+
+  Range<const wr::BorderSide> wrsides(wrSide, 4);
+  aBuilder.PushBorder(roundedRect,
+                      roundedRect,
+                      aBorderParams.mBackfaceIsVisible,
+                      borderWidths,
+                      wrsides,
+                      wr::EmptyBorderRadius());
+}
+
 void
 BCBlockDirSeg::CreateWebRenderCommands(BCPaintBorderIterator& aIter,
                                        BCPixelSize aInlineSegBSize,
                                        wr::DisplayListBuilder& aBuilder,
                                        const layers::StackingContextHelper& aSc,
-                                       const nsPoint& aOffset,
-                                       Maybe<BCBorderParameters>* aBevelBorders)
+                                       const nsPoint& aOffset)
 {
   Maybe<BCBorderParameters> param = BuildBorderParameters(aIter, aInlineSegBSize);
   if (param.isNothing()) {
     return;
   }
 
-  if (param->mStartBevelOffset != 0 || param->mEndBevelOffset != 0) {
-    // If both bevel offsets are non zero, the parameters of two bevels should
-    // be the same. So we choose start bevel side here.
-    mozilla::Side bevelSide = param->mStartBevelOffset != 0 ? param->mStartBevelSide : param->mEndBevelSide;
-
-    // The left border is going to be beveled on its right edge because that's
-    // the edge that intersects other borders (in this case the top and bottom borders).
-    // Correspondingly, if the bevel side is "right" that means we are operating on
-    // the left border, and so store the parameters for that entry in aBevelBorders.
-    // Same goes for the other directions.
-    switch (bevelSide) {
-      case eSideTop:
-        aBevelBorders[eSideBottom] = param;
-        break;
-      case eSideBottom:
-        aBevelBorders[eSideTop] = param;
-        break;
-      case eSideLeft:
-        aBevelBorders[eSideRight] = param;
-        break;
-      case eSideRight:
-        aBevelBorders[eSideLeft] = param;
-        break;
-    }
-
-    return;
-  }
-
-  LayoutDeviceRect borderRect = LayoutDeviceRect::FromUnknownRect(NSRectToRect(param->mBorderRect + aOffset,
-                                                                               param->mAppUnitsPerDevPixel));
-
-  wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(borderRect);
-  wr::BorderSide wrSide[4];
-  NS_FOR_CSS_SIDES(i) {
-    wrSide[i] = wr::ToBorderSide(ToDeviceColor(param->mBorderColor), NS_STYLE_BORDER_STYLE_NONE);
-  }
-  wrSide[eSideLeft] = wr::ToBorderSide(ToDeviceColor(param->mBorderColor), param->mBorderStyle);
-
-  wr::BorderRadius borderRadii = wr::EmptyBorderRadius();
-
-  // All border style is set to none except left side. So setting the widths of
-  // each side to width of rect is fine.
-  wr::LayoutSideOffsets borderWidths = wr::ToBorderWidths(roundedRect.size.width,
-                                                     roundedRect.size.width,
-                                                     roundedRect.size.width,
-                                                     roundedRect.size.width);
-  Range<const wr::BorderSide> wrsides(wrSide, 4);
-  aBuilder.PushBorder(roundedRect,
-                      roundedRect,
-                      param->mBackfaceIsVisible,
-                      borderWidths,
-                      wrsides,
-                      borderRadii);
+  CreateWRCommandsForBorderSegment(*param, aBuilder, aSc, aOffset);
 }
 
 /**
  * Advance the start point of a segment
  */
 void
 BCBlockDirSeg::AdvanceOffsetB()
 {
@@ -7793,70 +7940,24 @@ BCInlineDirSeg::Paint(BCPaintBorderItera
                                          param->mStartBevelSide, param->mStartBevelOffset,
                                          param->mEndBevelSide, param->mEndBevelOffset);
 }
 
 void
 BCInlineDirSeg::CreateWebRenderCommands(BCPaintBorderIterator& aIter,
                                         wr::DisplayListBuilder& aBuilder,
                                         const layers::StackingContextHelper& aSc,
-                                        const nsPoint& aPt,
-                                        Maybe<BCBorderParameters>* aBevelBorders)
+                                        const nsPoint& aPt)
 {
   Maybe<BCBorderParameters> param = BuildBorderParameters(aIter);
   if (param.isNothing()) {
     return;
   }
 
-  if (param->mStartBevelOffset != 0 || param->mEndBevelOffset != 0) {
-    mozilla::Side bevelSide = param->mStartBevelOffset != 0 ? param->mStartBevelSide : param->mEndBevelSide;
-
-    // See detailed comment on equivalent code in BCBlockDirSeg::CreateWebRenderCommands.
-    switch (bevelSide) {
-      case eSideTop:
-        aBevelBorders[eSideBottom] = param;
-        break;
-      case eSideBottom:
-        aBevelBorders[eSideTop] = param;
-        break;
-      case eSideLeft:
-        aBevelBorders[eSideRight] = param;
-        break;
-      case eSideRight:
-        aBevelBorders[eSideLeft] = param;
-        break;
-    }
-
-    return;
-  }
-
-  LayoutDeviceRect borderRect = LayoutDeviceRect::FromUnknownRect(NSRectToRect(param->mBorderRect + aPt,
-                                                                               param->mAppUnitsPerDevPixel));
-  wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(borderRect);
-  wr::BorderSide wrSide[4];
-  NS_FOR_CSS_SIDES(i) {
-    wrSide[i] = wr::ToBorderSide(ToDeviceColor(param->mBorderColor), NS_STYLE_BORDER_STYLE_NONE);
-  }
-  wrSide[eSideTop] = wr::ToBorderSide(ToDeviceColor(param->mBorderColor), param->mBorderStyle);
-
-  wr::BorderRadius borderRadii = wr::EmptyBorderRadius();
-
-  // All border style is set to none except top side. So setting the widths of
-  // each side to height of rect is fine.
-  wr::LayoutSideOffsets borderWidths = wr::ToBorderWidths(roundedRect.size.height,
-                                                     roundedRect.size.height,
-                                                     roundedRect.size.height,
-                                                     roundedRect.size.height);
-  Range<const wr::BorderSide> wrsides(wrSide, 4);
-  aBuilder.PushBorder(roundedRect,
-                      roundedRect,
-                      param->mBackfaceIsVisible,
-                      borderWidths,
-                      wrsides,
-                      borderRadii);
+  CreateWRCommandsForBorderSegment(*param, aBuilder, aSc, aPt);
 }
 
 /**
  * Advance the start point of a segment
  */
 void
 BCInlineDirSeg::AdvanceOffsetI()
 {
@@ -7943,18 +8044,17 @@ BCPaintBorderIterator::AccumulateOrDoAct
       if (mInlineSeg.mWidth > 0) {
         if (aAction.mMode == BCPaintBorderAction::Mode::PAINT) {
           mInlineSeg.Paint(*this, aAction.mPaintData.mDrawTarget);
         } else {
           MOZ_ASSERT(aAction.mMode == BCPaintBorderAction::Mode::CREATE_WEBRENDER_COMMANDS);
           mInlineSeg.CreateWebRenderCommands(*this,
                                              aAction.mCreateWebRenderCommandsData.mBuilder,
                                              aAction.mCreateWebRenderCommandsData.mSc,
-                                             aAction.mCreateWebRenderCommandsData.mOffsetToReferenceFrame,
-                                             aAction.mCreateWebRenderCommandsData.mBevelBorders);
+                                             aAction.mCreateWebRenderCommandsData.mOffsetToReferenceFrame);
         }
       }
       mInlineSeg.AdvanceOffsetI();
     }
     mInlineSeg.Start(*this, borderOwner, iStartSegISize, bStartSegBSize);
   }
   mInlineSeg.IncludeCurrentBorder(*this);
   mBlockDirInfo[relColIndex].mWidth = iStartSegISize;
@@ -7996,18 +8096,17 @@ BCPaintBorderIterator::AccumulateOrDoAct
         if (aAction.mMode == BCPaintBorderAction::Mode::PAINT) {
           blockDirSeg.Paint(*this, aAction.mPaintData.mDrawTarget, inlineSegBSize);
         } else {
           MOZ_ASSERT(aAction.mMode == BCPaintBorderAction::Mode::CREATE_WEBRENDER_COMMANDS);
           blockDirSeg.CreateWebRenderCommands(*this,
                                               inlineSegBSize,
                                               aAction.mCreateWebRenderCommandsData.mBuilder,
                                               aAction.mCreateWebRenderCommandsData.mSc,
-                                              aAction.mCreateWebRenderCommandsData.mOffsetToReferenceFrame,
-                                              aAction.mCreateWebRenderCommandsData.mBevelBorders);
+                                              aAction.mCreateWebRenderCommandsData.mOffsetToReferenceFrame);
         }
       }
       blockDirSeg.AdvanceOffsetB();
     }
     blockDirSeg.Start(*this, borderOwner, blockSegISize, inlineSegBSize);
   }
   blockDirSeg.IncludeCurrentBorder(*this);
   mPrevInlineSegBSize = inlineSegBSize;
@@ -8078,64 +8177,16 @@ nsTableFrame::CreateWebRenderCommandsFor
                                                   const mozilla::layers::StackingContextHelper& aSc,
                                                   const nsRect& aVisibleRect,
                                                   const nsPoint& aOffsetToReferenceFrame)
 {
   BCPaintBorderAction action(aBuilder, aSc, aOffsetToReferenceFrame);
   // We always draw whole table border for webrender. Passing the visible rect
   // dirty rect.
   IterateBCBorders(action, aVisibleRect - aOffsetToReferenceFrame);
-
-  LayoutDeviceRect allBorderRect;
-  wr::BorderSide wrSide[4];
-  wr::LayoutSideOffsets wrWidths;
-  wr::BorderRadius borderRadii = wr::EmptyBorderRadius();
-  bool backfaceIsVisible = false;
-  NS_FOR_CSS_SIDES(side) {
-    auto param = action.mCreateWebRenderCommandsData.mBevelBorders[side];
-    LayoutDeviceRect borderRect;
-    nscolor borderColor = NS_RGBA(0, 0, 0, 255);
-    uint8_t borderStyle = NS_STYLE_BORDER_STYLE_NONE;
-    if (param.isSome()) {
-      borderRect = LayoutDeviceRect::FromUnknownRect(NSRectToRect(param->mBorderRect + aOffsetToReferenceFrame,
-                                                                  param->mAppUnitsPerDevPixel));
-      borderColor = param->mBorderColor;
-      borderStyle = param->mBorderStyle;
-      backfaceIsVisible |= param->mBackfaceIsVisible;
-    }
-
-    wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(borderRect);
-    allBorderRect = allBorderRect.Union(borderRect);
-    wrSide[side] = wr::ToBorderSide(ToDeviceColor(borderColor), borderStyle);
-    switch (side) {
-      case eSideTop:
-        wrWidths.top = roundedRect.size.height;
-        break;
-      case eSideBottom:
-        wrWidths.bottom = roundedRect.size.height;
-        break;
-      case eSideLeft:
-        wrWidths.left = roundedRect.size.width;
-        break;
-      case eSideRight:
-        wrWidths.right = roundedRect.size.width;
-        break;
-    }
-  }
-
-  if (!allBorderRect.IsEmpty()) {
-    Range<const wr::BorderSide> wrsides(wrSide, 4);
-    wr::LayoutRect allRoundedRect = wr::ToRoundedLayoutRect(allBorderRect);
-    aBuilder.PushBorder(allRoundedRect,
-                        allRoundedRect,
-                        backfaceIsVisible,
-                        wrWidths,
-                        wrsides,
-                        borderRadii);
-  }
 }
 
 bool
 nsTableFrame::RowHasSpanningCells(int32_t aRowIndex, int32_t aNumEffCols)
 {
   bool result = false;
   nsTableCellMap* cellMap = GetCellMap();
   MOZ_ASSERT (cellMap, "bad call, cellMap not yet allocated.");