Bug 1341156 - Modify webrender_ffi and add OpDPPushBorderImage. r=kats r=ethlin draft
authorMorris Tseng <mtseng@mozilla.com>
Mon, 06 Mar 2017 10:38:12 +0800
changeset 493796 2215c5d5d144cb6466b22440c3ad72a51054dd69
parent 493795 86f3abba99b53d4db6c1e4c50b460c8dc44e6629
child 493797 862b6fa20fda270c542a30179185b8980e75611b
push id47843
push usermtseng@mozilla.com
push dateMon, 06 Mar 2017 02:45:16 +0000
reviewerskats, ethlin
bugs1341156
milestone54.0a1
Bug 1341156 - Modify webrender_ffi and add OpDPPushBorderImage. r=kats r=ethlin MozReview-Commit-ID: Ct63gy6nk3q * * * commit 28f9cea112a2e246cb39302907fa9a7ab4cd5a51 Author: Morris Tseng <mtseng@mozilla.com> p1 MozReview-Commit-ID: 3AfRjAO9k3L
gfx/layers/ipc/WebRenderMessages.ipdlh
gfx/layers/wr/WebRenderBorderLayer.cpp
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderMessageUtils.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/WebRenderTypes.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi.h
layout/painting/nsDisplayList.cpp
--- a/gfx/layers/ipc/WebRenderMessages.ipdlh
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -6,16 +6,20 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include LayersMessages;
 include protocol PTexture;
 
 using WrBorderRadius from "mozilla/webrender/webrender_ffi.h";
 using WrBorderSide from "mozilla/webrender/webrender_ffi.h";
+using WrBorderWidths from "mozilla/webrender/webrender_ffi.h";
+using WrNinePatchDescriptor from "mozilla/webrender/webrender_ffi.h";
+using WrSideOffsets2Df32 from "mozilla/webrender/webrender_ffi.h";
+using WrRepeatMode from "mozilla/webrender/webrender_ffi.h";
 using WrColor from "mozilla/webrender/webrender_ffi.h";
 using WrLayoutSize from "mozilla/webrender/webrender_ffi.h";
 using WrRect from "mozilla/webrender/webrender_ffi.h";
 using WrPoint from "mozilla/webrender/webrender_ffi.h";
 using WrGradientStop from "mozilla/webrender/webrender_ffi.h";
 using WrGradientExtendMode from "mozilla/webrender/webrender_ffi.h";
 using WrGlyphArray from "mozilla/webrender/webrender_ffi.h";
 using WrMixBlendMode from "mozilla/webrender/webrender_ffi.h";
@@ -58,23 +62,35 @@ struct OpDPPushRect {
   WrRect bounds;
   WrRect clip;
   WrColor color;
 };
 
 struct OpDPPushBorder {
   WrRect bounds;
   WrRect clip;
+  WrBorderWidths widths;
   WrBorderSide top;
   WrBorderSide right;
   WrBorderSide bottom;
   WrBorderSide left;
   WrBorderRadius radius;
 };
 
+struct OpDPPushBorderImage {
+  WrRect bounds;
+  WrRect clip;
+  WrBorderWidths widths;
+  ImageKey key;
+  WrNinePatchDescriptor patch;
+  WrSideOffsets2Df32 outset;
+  WrRepeatMode repeat_horizontal;
+  WrRepeatMode repeat_vertical;
+};
+
 struct OpDPPushLinearGradient {
   WrRect bounds;
   WrRect clip;
   WrPoint startPoint;
   WrPoint endPoint;
   WrGradientExtendMode extendMode;
   WrGradientStop[] stops;
 };
@@ -131,16 +147,17 @@ struct OpDPPushBoxShadow {
 
 union WebRenderCommand {
   OpDPPushStackingContext;
   OpDPPopStackingContext;
   OpDPPushScrollLayer;
   OpDPPopScrollLayer;
   OpDPPushRect;
   OpDPPushBorder;
+  OpDPPushBorderImage;
   OpDPPushLinearGradient;
   OpDPPushRadialGradient;
   OpDPPushImage;
   OpDPPushIframe;
   OpDPPushText;
   OpDPPushBoxShadow;
 };
 
--- a/gfx/layers/wr/WebRenderBorderLayer.cpp
+++ b/gfx/layers/wr/WebRenderBorderLayer.cpp
@@ -57,18 +57,20 @@ WebRenderBorderLayer::RenderLayer(wr::Di
   aBuilder.PushStackingContext(wr::ToWrRect(relBounds),
                                wr::ToWrRect(overflow),
                                nullptr,
                                1.0f,
                                //GetAnimations(),
                                transform,
                                WrMixBlendMode::Normal);
   aBuilder.PushBorder(wr::ToWrRect(rect), wr::ToWrRect(clip),
-                      wr::ToWrBorderSide(mWidths[0], mColors[0], mBorderStyles[0]),
-                      wr::ToWrBorderSide(mWidths[1], mColors[1], mBorderStyles[1]),
-                      wr::ToWrBorderSide(mWidths[2], mColors[2], mBorderStyles[2]),
-                      wr::ToWrBorderSide(mWidths[3], mColors[3], mBorderStyles[3]),
-                      wr::ToWrBorderRadius(mCorners[0], mCorners[1], mCorners[3], mCorners[2]));
+                      wr::ToWrBorderWidths(mWidths[0], mWidths[1], mWidths[2], mWidths[3]),
+                      wr::ToWrBorderSide(mColors[0], mBorderStyles[0]),
+                      wr::ToWrBorderSide(mColors[1], mBorderStyles[1]),
+                      wr::ToWrBorderSide(mColors[2], mBorderStyles[2]),
+                      wr::ToWrBorderSide(mColors[3], mBorderStyles[3]),
+                      wr::ToWrBorderRadius(mCorners[eCornerTopLeft], mCorners[eCornerTopRight],
+                                           mCorners[eCornerBottomLeft], mCorners[eCornerBottomRight]));
   aBuilder.PopStackingContext();
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -117,20 +117,27 @@ WebRenderBridgeChild::ProcessWebrenderCo
       case WebRenderCommand::TOpDPPushRect: {
         const OpDPPushRect& op = cmd.get_OpDPPushRect();
         builder.PushRect(op.bounds(), op.clip(), op.color());
         break;
       }
       case WebRenderCommand::TOpDPPushBorder: {
         const OpDPPushBorder& op = cmd.get_OpDPPushBorder();
         builder.PushBorder(op.bounds(), op.clip(),
-                           op.top(), op.right(), op.bottom(), op.left(),
+                           op.widths(), op.top(), op.right(), op.bottom(), op.left(),
                            op.radius());
         break;
       }
+      case WebRenderCommand::TOpDPPushBorderImage: {
+        const OpDPPushBorderImage& op = cmd.get_OpDPPushBorderImage();
+        builder.PushBorderImage(op.bounds(), op.clip(),
+                                op.widths(), op.key(), op.patch(), op.outset(),
+                                op.repeat_horizontal(), op.repeat_vertical());
+        break;
+      }
       case WebRenderCommand::TOpDPPushLinearGradient: {
         const OpDPPushLinearGradient& op = cmd.get_OpDPPushLinearGradient();
         builder.PushLinearGradient(op.bounds(), op.clip(),
                                    op.startPoint(), op.endPoint(),
                                    op.stops(), op.extendMode());
         break;
       }
       case WebRenderCommand::TOpDPPushRadialGradient: {
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -208,27 +208,111 @@ struct ParamTraits<WrGradientStop>
 };
 
 template<>
 struct ParamTraits<WrBorderSide>
 {
   static void
   Write(Message* aMsg, const WrBorderSide& aParam)
   {
-    WriteParam(aMsg, aParam.width);
     WriteParam(aMsg, aParam.color);
     WriteParam(aMsg, aParam.style);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, WrBorderSide* aResult)
   {
+    return ReadParam(aMsg, aIter, &aResult->color)
+        && ReadParam(aMsg, aIter, &aResult->style);
+  }
+};
+
+template<>
+struct ParamTraits<WrBorderWidths>
+{
+  static void
+  Write(Message* aMsg, const WrBorderWidths& aParam)
+  {
+    WriteParam(aMsg, aParam.left);
+    WriteParam(aMsg, aParam.top);
+    WriteParam(aMsg, aParam.right);
+    WriteParam(aMsg, aParam.bottom);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, WrBorderWidths* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->left)
+        && ReadParam(aMsg, aIter, &aResult->top)
+        && ReadParam(aMsg, aIter, &aResult->right)
+        && ReadParam(aMsg, aIter, &aResult->bottom);
+  }
+};
+
+template<>
+struct ParamTraits<WrSideOffsets2Du32>
+{
+  static void
+  Write(Message* aMsg, const WrSideOffsets2Du32& aParam)
+  {
+    WriteParam(aMsg, aParam.top);
+    WriteParam(aMsg, aParam.right);
+    WriteParam(aMsg, aParam.bottom);
+    WriteParam(aMsg, aParam.left);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, WrSideOffsets2Du32* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->top)
+        && ReadParam(aMsg, aIter, &aResult->right)
+        && ReadParam(aMsg, aIter, &aResult->bottom)
+        && ReadParam(aMsg, aIter, &aResult->left);
+  }
+};
+
+template<>
+struct ParamTraits<WrSideOffsets2Df32>
+{
+  static void
+  Write(Message* aMsg, const WrSideOffsets2Df32& aParam)
+  {
+    WriteParam(aMsg, aParam.top);
+    WriteParam(aMsg, aParam.right);
+    WriteParam(aMsg, aParam.bottom);
+    WriteParam(aMsg, aParam.left);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, WrSideOffsets2Df32* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->top)
+        && ReadParam(aMsg, aIter, &aResult->right)
+        && ReadParam(aMsg, aIter, &aResult->bottom)
+        && ReadParam(aMsg, aIter, &aResult->left);
+  }
+};
+
+template<>
+struct ParamTraits<WrNinePatchDescriptor>
+{
+  static void
+  Write(Message* aMsg, const WrNinePatchDescriptor& aParam)
+  {
+    WriteParam(aMsg, aParam.width);
+    WriteParam(aMsg, aParam.height);
+    WriteParam(aMsg, aParam.slice);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, WrNinePatchDescriptor* aResult)
+  {
     return ReadParam(aMsg, aIter, &aResult->width)
-        && ReadParam(aMsg, aIter, &aResult->color)
-        && ReadParam(aMsg, aIter, &aResult->style);
+        && ReadParam(aMsg, aIter, &aResult->height)
+        && ReadParam(aMsg, aIter, &aResult->slice);
   }
 };
 
 template<>
 struct ParamTraits<WrLayoutSize>
 {
   static void
   Write(Message* aMsg, const WrLayoutSize& aParam)
@@ -397,11 +481,20 @@ template<>
 struct ParamTraits<WrGradientExtendMode>
   : public ContiguousEnumSerializer<
         WrGradientExtendMode,
         WrGradientExtendMode::Clamp,
         WrGradientExtendMode::Sentinel>
 {
 };
 
+template<>
+struct ParamTraits<WrRepeatMode>
+  : public ContiguousEnumSerializer<
+        WrRepeatMode,
+        WrRepeatMode::Stretch,
+        WrRepeatMode::Sentinel>
+{
+};
+
 } // namespace IPC
 
 #endif // GFX_WEBRENDERMESSAGEUTILS_H
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -477,25 +477,40 @@ DisplayListBuilder::PushIFrame(const WrR
                                PipelineId aPipeline)
 {
   wr_dp_push_iframe(mWrState, aBounds, aClip, aPipeline);
 }
 
 void
 DisplayListBuilder::PushBorder(const WrRect& aBounds,
                                const WrRect& aClip,
+                               const WrBorderWidths& aWidths,
                                const WrBorderSide& aTop,
                                const WrBorderSide& aRight,
                                const WrBorderSide& aBottom,
                                const WrBorderSide& aLeft,
                                const WrBorderRadius& aRadius)
 {
   wr_dp_push_border(mWrState, aBounds, aClip,
-                    aTop, aRight, aBottom, aLeft,
-                    aRadius);
+                    aWidths, aTop, aRight, aBottom, aLeft, aRadius);
+}
+
+void
+DisplayListBuilder::PushBorderImage(const WrRect& aBounds,
+                                    const WrRect& aClip,
+                                    const WrBorderWidths& aWidths,
+                                    wr::ImageKey aImage,
+                                    const WrNinePatchDescriptor& aPatch,
+                                    const WrSideOffsets2Df32& aOutset,
+                                    const WrRepeatMode& aRepeatHorizontal,
+                                    const WrRepeatMode& aRepeatVertical)
+{
+  wr_dp_push_border_image(mWrState, aBounds, aClip,
+                          aWidths, aImage, aPatch, aOutset,
+                          aRepeatHorizontal, aRepeatVertical);
 }
 
 void
 DisplayListBuilder::PushText(const WrRect& aBounds,
                              const WrRect& aClip,
                              const gfx::Color& aColor,
                              wr::FontKey aFontKey,
                              Range<const WrGlyphInstance> aGlyphBuffer,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -170,22 +170,32 @@ public:
                  wr::ImageKey aImage);
 
   void PushIFrame(const WrRect& aBounds,
                   const WrRect& aClip,
                   wr::PipelineId aPipeline);
 
   void PushBorder(const WrRect& aBounds,
                   const WrRect& aClip,
+                  const WrBorderWidths& aWidths,
                   const WrBorderSide& aTop,
                   const WrBorderSide& aRight,
                   const WrBorderSide& aBbottom,
                   const WrBorderSide& aLeft,
                   const WrBorderRadius& aRadius);
 
+  void PushBorderImage(const WrRect& aBounds,
+                       const WrRect& aClip,
+                       const WrBorderWidths& aWidths,
+                       wr::ImageKey aImage,
+                       const WrNinePatchDescriptor& aPatch,
+                       const WrSideOffsets2Df32& aOutset,
+                       const WrRepeatMode& aRepeatHorizontal,
+                       const WrRepeatMode& aRepeatVertical);
+
   void PushText(const WrRect& aBounds,
                 const WrRect& aClip,
                 const gfx::Color& aColor,
                 wr::FontKey aFontKey,
                 Range<const WrGlyphInstance> aGlyphBuffer,
                 float aGlyphSize);
 
   void PushBoxShadow(const WrRect& aRect,
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -172,20 +172,19 @@ static inline WrBorderStyle ToWrBorderSt
   case NS_STYLE_BORDER_STYLE_OUTSET:
     return WrBorderStyle::Outset;
   default:
     MOZ_ASSERT(false);
   }
   return WrBorderStyle::None;
 }
 
-static inline WrBorderSide ToWrBorderSide(const LayerCoord width, const gfx::Color& color, const uint8_t& style)
+static inline WrBorderSide ToWrBorderSide(const gfx::Color& color, const uint8_t& style)
 {
   WrBorderSide bs;
-  bs.width = width;
   bs.color = ToWrColor(color);
   bs.style = ToWrBorderStyle(style);
   return bs;
 }
 
 static inline WrPoint ToWrPoint(const LayerPoint point)
 {
   WrPoint lp;
@@ -208,16 +207,74 @@ static inline WrBorderRadius ToWrBorderR
   WrBorderRadius br;
   br.top_left = ToWrLayoutSize(topLeft);
   br.top_right = ToWrLayoutSize(topRight);
   br.bottom_left = ToWrLayoutSize(bottomLeft);
   br.bottom_right = ToWrLayoutSize(bottomRight);
   return br;
 }
 
+static inline WrBorderWidths ToWrBorderWidths(float top, float right, float bottom, float left)
+{
+  WrBorderWidths bw;
+  bw.top = top;
+  bw.right = right;
+  bw.bottom = bottom;
+  bw.left = left;
+  return bw;
+}
+
+static inline WrNinePatchDescriptor ToWrNinePatchDescriptor(uint32_t width, uint32_t height,
+                                                            const WrSideOffsets2Du32& slice)
+{
+  WrNinePatchDescriptor patch;
+  patch.width = width;
+  patch.height = height;
+  patch.slice = slice;
+  return patch;
+}
+
+static inline WrSideOffsets2Du32 ToWrSideOffsets2Du32(uint32_t top, uint32_t right, uint32_t bottom, uint32_t left)
+{
+  WrSideOffsets2Du32 offset;
+  offset.top = top;
+  offset.right = right;
+  offset.bottom = bottom;
+  offset.left = left;
+  return offset;
+}
+
+static inline WrSideOffsets2Df32 ToWrSideOffsets2Df32(float top, float right, float bottom, float left)
+{
+  WrSideOffsets2Df32 offset;
+  offset.top = top;
+  offset.right = right;
+  offset.bottom = bottom;
+  offset.left = left;
+  return offset;
+}
+
+static inline WrRepeatMode ToWrRepeatMode(uint8_t repeatMode)
+{
+  switch (repeatMode) {
+  case NS_STYLE_BORDER_IMAGE_REPEAT_STRETCH:
+    return WrRepeatMode::Stretch;
+  case NS_STYLE_BORDER_IMAGE_REPEAT_REPEAT:
+    return WrRepeatMode::Repeat;
+  case NS_STYLE_BORDER_IMAGE_REPEAT_ROUND:
+    return WrRepeatMode::Round;
+  case NS_STYLE_BORDER_IMAGE_REPEAT_SPACE:
+    return WrRepeatMode::Space;
+  default:
+    MOZ_ASSERT(false);
+  }
+
+  return WrRepeatMode::Stretch;
+}
+
 template<class T>
 static inline WrRect ToWrRect(const gfx::RectTyped<T>& rect)
 {
   WrRect r;
   r.x = rect.x;
   r.y = rect.y;
   r.width = rect.width;
   r.height = rect.height;
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1,28 +1,29 @@
 use std::ffi::CString;
 use std::{mem, slice};
 use std::path::PathBuf;
 use std::os::raw::{c_void, c_char};
 use gleam::gl;
-use webrender_traits::{BorderSide, BorderStyle, BorderRadius, BorderWidths, BorderDetails, NormalBorder};
+use webrender_traits::{BorderSide, BorderStyle, BorderRadius, BorderWidths, NormalBorder};
+use webrender_traits::{BorderDetails, ImageBorder, NinePatchDescriptor, RepeatMode};
 use webrender_traits::{PipelineId, ClipRegion, PropertyBinding};
 use webrender_traits::{Epoch, ExtendMode, ColorF, GlyphInstance, GradientStop, ImageDescriptor};
 use webrender_traits::{FilterOp, ImageData, ImageFormat, ImageKey, ImageMask, ImageRendering, MixBlendMode};
 use webrender_traits::{ExternalImageId, RenderApi, FontKey};
 use webrender_traits::{DeviceUintSize, ExternalEvent};
 use webrender_traits::{LayoutPoint, LayoutRect, LayoutSize, LayoutTransform};
 use webrender_traits::{BoxShadowClipMode, LayerPixel, ServoScrollRootId, IdNamespace};
 use webrender_traits::{BuiltDisplayListDescriptor, AuxiliaryListsDescriptor};
 use webrender_traits::{BuiltDisplayList, AuxiliaryLists};
 use webrender::renderer::{Renderer, RendererOptions};
 use webrender::renderer::{ExternalImage, ExternalImageHandler, ExternalImageSource};
 use webrender::{ApiRecordingReceiver, BinaryRecorder};
 use app_units::Au;
-use euclid::TypedPoint2D;
+use euclid::{TypedPoint2D, SideOffsets2D};
 
 extern crate webrender_traits;
 
 static ENABLE_RECORDING: bool = false;
 
 // This macro adds some checks to make sure we notice when the memory representation of
 // types change.
 macro_rules! check_ffi_type {
@@ -766,37 +767,56 @@ pub extern fn wr_dp_push_box_shadow(stat
                                                    blur_radius,
                                                    spread_radius,
                                                    border_radius,
                                                    clip_mode.to_box_shadow_clip_mode());
 }
 
 #[no_mangle]
 pub extern fn wr_dp_push_border(state: &mut WrState, rect: WrRect, clip: WrRect,
-                                top: WrBorderSide, right: WrBorderSide, bottom: WrBorderSide, left: WrBorderSide,
+                                widths: WrBorderWidths,
+                                top: WrBorderSide, right: WrBorderSide,
+                                bottom: WrBorderSide, left: WrBorderSide,
                                 radius: WrBorderRadius) {
     assert!( unsafe { is_in_main_thread() });
     let clip_region = state.frame_builder.dl_builder.new_clip_region(&clip.to_rect(), Vec::new(), None);
-    let border_widths = BorderWidths {
-        left: left.width,
-        top: top.width,
-        right: right.width,
-        bottom: bottom.width
-    };
     let border_details = BorderDetails::Normal(NormalBorder {
         left: left.to_border_side(),
         right: right.to_border_side(),
         top: top.to_border_side(),
         bottom: bottom.to_border_side(),
         radius: radius.to_border_radius(),
     });
     state.frame_builder.dl_builder.push_border(
                                     rect.to_rect(),
                                     clip_region,
-                                    border_widths,
+                                    widths.to_border_widths(),
+                                    border_details);
+}
+
+#[no_mangle]
+pub extern fn wr_dp_push_border_image(state: &mut WrState, rect: WrRect, clip: WrRect,
+                                      widths: WrBorderWidths,
+                                      image: ImageKey, patch: WrNinePatchDescriptor,
+                                      outset: WrSideOffsets2D<f32>,
+                                      repeat_horizontal: WrRepeatMode,
+                                      repeat_vertical: WrRepeatMode) {
+    assert!( unsafe { is_in_main_thread() });
+    let clip_region = state.frame_builder.dl_builder.new_clip_region(&clip.to_rect(), Vec::new(), None);
+    let border_details = BorderDetails::Image(ImageBorder {
+        image_key: image,
+        patch: patch.to_nine_patch_descriptor(),
+        outset: outset.to_side_offsets_2d(),
+        repeat_horizontal: repeat_horizontal.to_repeat_mode(),
+        repeat_vertical: repeat_vertical.to_repeat_mode(),
+    });
+    state.frame_builder.dl_builder.push_border(
+                                    rect.to_rect(),
+                                    clip_region,
+                                    widths.to_border_widths(),
                                     border_details);
 }
 
 #[no_mangle]
 pub extern fn wr_dp_push_linear_gradient(state: &mut WrState, rect: WrRect, clip: WrRect,
                                          start_point: WrPoint, end_point: WrPoint,
                                          stops: * const WrGradientStop, stops_count: usize,
                                          extend_mode: WrGradientExtendMode) {
@@ -884,30 +904,106 @@ impl WrBorderRadius
                        bottom_left: self.bottom_left.to_layout_size(),
                        bottom_right: self.bottom_right.to_layout_size() }
     }
 }
 
 #[repr(C)]
 pub struct WrBorderSide
 {
-    width: f32,
     color: WrColor,
     style: BorderStyle
 }
 
 impl WrBorderSide
 {
     pub fn to_border_side(&self) -> BorderSide
     {
         BorderSide { color: self.color.to_color(), style: self.style }
     }
 }
 
 #[repr(C)]
+pub struct WrBorderWidths
+{
+    left: f32,
+    top: f32,
+    right: f32,
+    bottom: f32,
+}
+
+impl WrBorderWidths
+{
+    pub fn to_border_widths(&self) -> BorderWidths
+    {
+        BorderWidths { left: self.left, top: self.top, right: self.right, bottom: self.bottom }
+    }
+}
+
+#[repr(C)]
+pub struct WrNinePatchDescriptor
+{
+    width: u32,
+    height: u32,
+    slice: WrSideOffsets2D<u32>,
+}
+
+impl WrNinePatchDescriptor
+{
+    pub fn to_nine_patch_descriptor(&self) -> NinePatchDescriptor
+    {
+        NinePatchDescriptor {
+            width: self.width,
+            height: self.height,
+            slice: self.slice.to_side_offsets_2d(),
+        }
+    }
+}
+
+#[repr(C)]
+pub struct WrSideOffsets2D<T>
+{
+    top: T,
+    right: T,
+    bottom: T,
+    left: T,
+}
+
+impl<T: Copy> WrSideOffsets2D<T>
+{
+    pub fn to_side_offsets_2d(&self) -> SideOffsets2D<T>
+    {
+        SideOffsets2D::new(self.top, self.right, self.bottom, self.left)
+    }
+}
+
+#[repr(C)]
+pub enum WrRepeatMode
+{
+    Stretch,
+    Repeat,
+    Round,
+    Space,
+}
+
+impl WrRepeatMode
+{
+   pub fn to_repeat_mode(self) -> RepeatMode
+   {
+       match self
+       {
+           WrRepeatMode::Stretch => RepeatMode::Stretch,
+           WrRepeatMode::Repeat => RepeatMode::Repeat,
+           WrRepeatMode::Round => RepeatMode::Round,
+           WrRepeatMode::Space => RepeatMode::Space,
+       }
+   }
+}
+
+#[repr(C)]
 pub struct WrGradientStop
 {
     offset: f32,
     color: WrColor,
 }
 
 impl WrGradientStop
 {
--- a/gfx/webrender_bindings/webrender_ffi.h
+++ b/gfx/webrender_bindings/webrender_ffi.h
@@ -149,16 +149,26 @@ enum class WrMixBlendMode: uint32_t
 enum class WrGradientExtendMode : uint32_t
 {
   Clamp      = 0,
   Repeat     = 1,
 
   Sentinel /* this must be last, for IPC serialization purposes */
 };
 
+enum class WrRepeatMode : uint32_t
+{
+  Stretch      = 0,
+  Repeat       = 1,
+  Round        = 2,
+  Space        = 3,
+
+  Sentinel /* this must be last, for IPC serialization purposes */
+};
+
 // -----
 // Typedefs for struct fields and function signatures below.
 // -----
 
 typedef uint64_t WrImageIdType;
 
 // -----
 // Structs used in C++ code with corresponding types in Rust code
@@ -224,24 +234,22 @@ struct WrGradientStop {
 
   bool operator==(const WrGradientStop& aRhs) const
   {
     return offset == aRhs.offset && color == aRhs.color;
   }
 };
 
 struct WrBorderSide {
-  float width;
   WrColor color;
   WrBorderStyle style;
 
   bool operator==(const WrBorderSide& aRhs) const
   {
-    return width == aRhs.width && color == aRhs.color &&
-           style == aRhs.style;
+    return color == aRhs.color && style == aRhs.style;
   }
 };
 
 struct WrLayoutSize
 {
   float width;
   float height;
 
@@ -259,16 +267,67 @@ struct WrBorderRadius {
 
   bool operator==(const WrBorderRadius& aRhs) const
   {
     return top_left == aRhs.top_left && top_right == aRhs.top_right &&
            bottom_left == aRhs.bottom_left && bottom_right == aRhs.bottom_right;
   }
 };
 
+struct WrBorderWidths {
+  float left;
+  float top;
+  float right;
+  float bottom;
+
+  bool operator==(const WrBorderWidths& aRhs) const
+  {
+    return left == aRhs.left && top == aRhs.top &&
+           right == aRhs.right && bottom == aRhs.bottom;
+  }
+};
+
+struct WrSideOffsets2Du32 {
+  uint32_t top;
+  uint32_t right;
+  uint32_t bottom;
+  uint32_t left;
+
+  bool operator==(const WrSideOffsets2Du32& aRhs) const
+  {
+    return top == aRhs.top && right == aRhs.right &&
+           bottom == aRhs.bottom && left == aRhs.left;
+  }
+};
+
+struct WrSideOffsets2Df32 {
+  float top;
+  float right;
+  float bottom;
+  float left;
+
+  bool operator==(const WrSideOffsets2Df32& aRhs) const
+  {
+    return top == aRhs.top && right == aRhs.right &&
+           bottom == aRhs.bottom && left == aRhs.left;
+  }
+};
+
+struct WrNinePatchDescriptor {
+  uint32_t width;
+  uint32_t height;
+  WrSideOffsets2Du32 slice;
+
+  bool operator==(const WrNinePatchDescriptor& aRhs) const
+  {
+    return width == aRhs.width && height == aRhs.height &&
+           slice == aRhs.slice;
+  }
+};
+
 struct WrRect
 {
   float x;
   float y;
   float width;
   float height;
 
   bool operator==(const WrRect& aRhs) const
@@ -534,21 +593,30 @@ WR_FUNC;
 WR_INLINE void
 wr_dp_push_text(WrState* wrState, WrRect bounds, WrRect clip, WrColor color,
                 WrFontKey font_Key, const WrGlyphInstance* glyphs,
                 uint32_t glyph_count, float glyph_size)
 WR_FUNC;
 
 WR_INLINE void
 wr_dp_push_border(WrState* wrState, WrRect bounds, WrRect clip,
+                  WrBorderWidths widths,
                   WrBorderSide top, WrBorderSide right, WrBorderSide bottom, WrBorderSide left,
                   WrBorderRadius radius)
 WR_FUNC;
 
 WR_INLINE void
+wr_dp_push_border_image(WrState* wrState, WrRect bounds, WrRect clip,
+                        WrBorderWidths widths,
+                        WrImageKey image, WrNinePatchDescriptor patch, WrSideOffsets2Df32 outset,
+                        WrRepeatMode repeat_horizontal,
+                        WrRepeatMode repeat_vertical)
+WR_FUNC;
+
+WR_INLINE void
 wr_dp_push_linear_gradient(WrState* wrState, WrRect bounds, WrRect clip,
                            WrPoint startPoint, WrPoint endPoint,
                            const WrGradientStop* stops, size_t stopsCount,
                            WrGradientExtendMode extendMode)
 WR_FUNC;
 
 WR_INLINE void
 wr_dp_push_radial_gradient(WrState* wrState, WrRect bounds, WrRect clip,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -4153,24 +4153,30 @@ nsDisplayOutline::CreateWebRenderCommand
 {
   MOZ_ASSERT(mBorderRenderer.isSome());
 
   Rect outlineTransformedRect = aLayer->RelativeToParent(mBorderRenderer->mOuterRect);
 
   nsCSSBorderRenderer* br = mBorderRenderer.ptr();
   WrBorderSide side[4];
   NS_FOR_CSS_SIDES(i) {
-    side[i] = wr::ToWrBorderSide(br->mBorderWidths[i], ToDeviceColor(br->mBorderColors[i]), br->mBorderStyles[i]);
-  }
-  WrBorderRadius borderRadius = wr::ToWrBorderRadius(LayerSize(br->mBorderRadii[0].width, br->mBorderRadii[0].height),
-                                                     LayerSize(br->mBorderRadii[1].width, br->mBorderRadii[1].height),
-                                                     LayerSize(br->mBorderRadii[3].width, br->mBorderRadii[3].height),
-                                                     LayerSize(br->mBorderRadii[2].width, br->mBorderRadii[2].height));
+    side[i] = wr::ToWrBorderSide(ToDeviceColor(br->mBorderColors[i]), br->mBorderStyles[i]);
+  }
+
+  WrBorderRadius borderRadius =
+    wr::ToWrBorderRadius(
+      LayerSize(br->mBorderRadii[eCornerTopLeft].width, br->mBorderRadii[eCornerTopLeft].height),
+      LayerSize(br->mBorderRadii[eCornerTopRight].width, br->mBorderRadii[eCornerTopRight].height),
+      LayerSize(br->mBorderRadii[eCornerBottomLeft].width, br->mBorderRadii[eCornerBottomLeft].height),
+      LayerSize(br->mBorderRadii[eCornerBottomRight].width, br->mBorderRadii[eCornerBottomRight].height));
+
   aBuilder.PushBorder(wr::ToWrRect(outlineTransformedRect),
                       wr::ToWrRect(outlineTransformedRect),
+                      wr::ToWrBorderWidths(br->mBorderWidths[0], br->mBorderWidths[1],
+                                           br->mBorderWidths[2], br->mBorderWidths[3]),
                       side[0], side[1], side[2], side[3],
                       borderRadius);
 }
 
 bool
 nsDisplayOutline::IsInvisibleInRect(const nsRect& aRect)
 {
   const nsStyleOutline* outline = mFrame->StyleOutline();