Bug 1341156 - Modify webrender_ffi and add OpDPPushBorderImage. r=jrmuizel draft
authorMorris Tseng <mtseng@mozilla.com>
Thu, 23 Feb 2017 16:51:10 +0800
changeset 490711 bd9daf63fe5f8704fc52410eadf94c2995af1c79
parent 490710 95480a61674b3bb87b358d09c81e1f2f06d57545
child 490712 2f84c893866133e4d4214b04242ee5ae6dccb2fe
push id47204
push userbmo:mtseng@mozilla.com
push dateWed, 01 Mar 2017 07:32:07 +0000
reviewersjrmuizel
bugs1341156
milestone54.0a1
Bug 1341156 - Modify webrender_ffi and add OpDPPushBorderImage. r=jrmuizel MozReview-Commit-ID: Ct63gy6nk3q
gfx/layers/ipc/WebRenderMessages.ipdlh
gfx/layers/wr/WebRenderBorderLayer.cpp
gfx/layers/wr/WebRenderBridgeParent.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
--- a/gfx/layers/ipc/WebRenderMessages.ipdlh
+++ b/gfx/layers/ipc/WebRenderMessages.ipdlh
@@ -4,18 +4,22 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 include LayersSurfaces;
 include LayersMessages;
 include protocol PTexture;
 
-using WrBorderRadius from "mozilla/webrender/webrender_ffi.h";
 using WrBorderSide from "mozilla/webrender/webrender_ffi.h";
+using WrBorderWidth 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 WrNormalBorder 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,21 +62,29 @@ struct OpDPPushRect {
   WrRect bounds;
   WrRect clip;
   WrColor color;
 };
 
 struct OpDPPushBorder {
   WrRect bounds;
   WrRect clip;
-  WrBorderSide top;
-  WrBorderSide right;
-  WrBorderSide bottom;
-  WrBorderSide left;
-  WrBorderRadius radius;
+  WrBorderWidth widths;
+  WrNormalBorder normalBorder;
+};
+
+struct OpDPPushBorderImage {
+  WrRect bounds;
+  WrRect clip;
+  WrBorderWidth 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;
@@ -141,16 +153,17 @@ struct OpDPPushBoxShadow {
 
 union WebRenderCommand {
   OpDPPushStackingContext;
   OpDPPopStackingContext;
   OpDPPushScrollLayer;
   OpDPPopScrollLayer;
   OpDPPushRect;
   OpDPPushBorder;
+  OpDPPushBorderImage;
   OpDPPushLinearGradient;
   OpDPPushRadialGradient;
   OpDPPushImage;
   OpAddExternalImage;
   OpDPPushIframe;
   OpDPPushText;
   OpDPPushBoxShadow;
   CompositableOperation;
--- a/gfx/layers/wr/WebRenderBorderLayer.cpp
+++ b/gfx/layers/wr/WebRenderBorderLayer.cpp
@@ -60,18 +60,20 @@ WebRenderBorderLayer::RenderLayer()
                               Nothing(),
                               1.0f,
                               GetAnimations(),
                               transform,
                               WrMixBlendMode::Normal,
                               FrameMetrics::NULL_SCROLL_ID));
   WrBridge()->AddWebRenderCommand(
     OpDPPushBorder(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::ToWrBorderWidth(mWidths[0], mWidths[1], mWidths[2], mWidths[3]),
+                   wr::ToWrNormalBorder(
+                     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[0], mCorners[1], mCorners[3], mCorners[2]))));
   WrBridge()->AddWebRenderCommand(OpDPPopStackingContext());
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -297,18 +297,25 @@ WebRenderBridgeParent::ProcessWebrenderC
       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.radius());
+                           op.widths(), op.normalBorder());
+        break;
+      }
+      case WebRenderCommand::TOpDPPushBorderImage: {
+        const OpDPPushBorderImage& op = cmd.get_OpDPPushBorderImage();
+        builder.PushBorderImage(op.bounds(), op.clip(),
+                                op.widths(),
+                                wr::ToWrImageBorder(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;
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -208,27 +208,159 @@ 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<WrBorderWidth>
+{
+  static void
+  Write(Message* aMsg, const WrBorderWidth& 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, WrBorderWidth* 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<WrNormalBorder>
+{
+  static void
+  Write(Message* aMsg, const WrNormalBorder& aParam)
+  {
+    WriteParam(aMsg, aParam.left);
+    WriteParam(aMsg, aParam.top);
+    WriteParam(aMsg, aParam.right);
+    WriteParam(aMsg, aParam.bottom);
+    WriteParam(aMsg, aParam.radius);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, WrNormalBorder* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->left)
+        && ReadParam(aMsg, aIter, &aResult->top)
+        && ReadParam(aMsg, aIter, &aResult->right)
+        && ReadParam(aMsg, aIter, &aResult->bottom)
+        && ReadParam(aMsg, aIter, &aResult->radius);
+  }
+};
+
+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<WrImageBorder>
+{
+  static void
+  Write(Message* aMsg, const WrImageBorder& aParam)
+  {
+    WriteParam(aMsg, aParam.image);
+    WriteParam(aMsg, aParam.patch);
+    WriteParam(aMsg, aParam.outset);
+    WriteParam(aMsg, aParam.repeat_horizontal);
+    WriteParam(aMsg, aParam.repeat_vertical);
+  }
+
+  static bool
+  Read(const Message* aMsg, PickleIterator* aIter, WrImageBorder* aResult)
+  {
+    return ReadParam(aMsg, aIter, &aResult->image)
+        && ReadParam(aMsg, aIter, &aResult->patch)
+        && ReadParam(aMsg, aIter, &aResult->outset)
+        && ReadParam(aMsg, aIter, &aResult->repeat_horizontal)
+        && ReadParam(aMsg, aIter, &aResult->repeat_vertical);
   }
 };
 
 template<>
 struct ParamTraits<WrLayoutSize>
 {
   static void
   Write(Message* aMsg, const WrLayoutSize& aParam)
@@ -358,11 +490,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
@@ -423,25 +423,31 @@ DisplayListBuilder::PushIFrame(const WrR
                                PipelineId aPipeline)
 {
   wr_dp_push_iframe(mWrState, aBounds, aClip, aPipeline);
 }
 
 void
 DisplayListBuilder::PushBorder(const WrRect& aBounds,
                                const WrRect& aClip,
-                               const WrBorderSide& aTop,
-                               const WrBorderSide& aRight,
-                               const WrBorderSide& aBottom,
-                               const WrBorderSide& aLeft,
-                               const WrBorderRadius& aRadius)
+                               const WrBorderWidth& aWidths,
+                               const WrNormalBorder& aNormalBorder)
 {
   wr_dp_push_border(mWrState, aBounds, aClip,
-                    aTop, aRight, aBottom, aLeft,
-                    aRadius);
+                    aWidths, aNormalBorder);
+}
+
+void
+DisplayListBuilder::PushBorderImage(const WrRect& aBounds,
+                                    const WrRect& aClip,
+                                    const WrBorderWidth& aWidths,
+                                    const WrImageBorder& aImageBorder)
+{
+  wr_dp_push_border_image(mWrState, aBounds, aClip,
+                          aWidths, aImageBorder);
 }
 
 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
@@ -152,21 +152,23 @@ public:
                  wr::ImageKey aImage);
 
   void PushIFrame(const WrRect& aBounds,
                   const WrRect& aClip,
                   wr::PipelineId aPipeline);
 
   void PushBorder(const WrRect& aBounds,
                   const WrRect& aClip,
-                  const WrBorderSide& aTop,
-                  const WrBorderSide& aRight,
-                  const WrBorderSide& aBbottom,
-                  const WrBorderSide& aLeft,
-                  const WrBorderRadius& aRadius);
+                  const WrBorderWidth& aWidths,
+                  const WrNormalBorder& aNormalBorder);
+
+  void PushBorderImage(const WrRect& aBounds,
+                       const WrRect& aClip,
+                       const WrBorderWidth& aWidths,
+                       const WrImageBorder& aImageBorder);
 
   void PushText(const WrRect& aBounds,
                 const WrRect& aClip,
                 const gfx::Color& aColor,
                 wr::FontKey aFontKey,
                 Range<const WrGlyphInstance> aGlyphBuffer,
                 float aGlyphSize);
 
--- 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,101 @@ 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 WrBorderWidth ToWrBorderWidth(float top, float right, float bottom, float left)
+{
+  WrBorderWidth bw;
+  bw.top = top;
+  bw.right = right;
+  bw.bottom = bottom;
+  bw.left = left;
+  return bw;
+}
+
+static inline WrNormalBorder ToWrNormalBorder(const WrBorderSide& top, const WrBorderSide& right,
+                                              const WrBorderSide& bottom, const WrBorderSide& left,
+                                              const WrBorderRadius& radius)
+{
+  WrNormalBorder nb;
+  nb.top = top;
+  nb.right = right;
+  nb.bottom = bottom;
+  nb.left = left;
+  nb.radius = radius;
+  return nb;
+}
+
+static inline WrImageBorder ToWrImageBorder(const WrImageKey& image,
+                                            const WrNinePatchDescriptor& patch,
+                                            const WrSideOffsets2Df32& outset,
+                                            const WrRepeatMode& repeat_horizontal,
+                                            const WrRepeatMode& repeat_vertical)
+{
+  WrImageBorder border;
+  border.image = image;
+  border.patch = patch;
+  border.outset = outset;
+  border.repeat_horizontal = repeat_horizontal;
+  border.repeat_vertical = repeat_vertical;
+  return border;
+}
+
+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,14 +1,15 @@
 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};
 use webrender_traits::{PipelineId, ClipRegion, PropertyBinding};
 use webrender_traits::{Epoch, ExtendMode, ColorF, GlyphInstance, GradientStop, ImageDescriptor};
 use webrender_traits::{FilterOp, ImageData, ImageFormat, ImageKey, ImageMask, ImageRendering, RendererKind, 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::renderer::{Renderer, RendererOptions};
@@ -674,38 +675,36 @@ 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,
-                                radius: WrBorderRadius) {
+                                widths: WrBorderWidths, normal_border: NormalBorder) {
     assert!( unsafe { is_in_compositor_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,
-                                    border_details);
+                                    widths.to_border_widths(),
+                                    BorderDetails::Normal(normal_border));
+}
+
+#[no_mangle]
+pub extern fn wr_dp_push_border_image(state: &mut WrState, rect: WrRect, clip: WrRect,
+                                      widths: WrBorderWidths, image_border: ImageBorder) {
+    assert!( unsafe { is_in_compositor_thread() });
+    let clip_region = state.frame_builder.dl_builder.new_clip_region(&clip.to_rect(), Vec::new(), None);
+    state.frame_builder.dl_builder.push_border(
+                                    rect.to_rect(),
+                                    clip_region,
+                                    widths.to_border_widths(),
+                                    BorderDetails::Image(image_border));
 }
 
 #[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) {
     assert!( unsafe { is_in_compositor_thread() });
@@ -792,30 +791,46 @@ 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 WrGradientStop
 {
     offset: f32,
     color: WrColor,
 }
 
 impl WrGradientStop
 {
--- a/gfx/webrender_bindings/webrender_ffi.h
+++ b/gfx/webrender_bindings/webrender_ffi.h
@@ -146,16 +146,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
@@ -221,24 +231,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;
 
@@ -256,16 +264,98 @@ 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 WrBorderWidth {
+  float left;
+  float top;
+  float right;
+  float bottom;
+
+  bool operator==(const WrBorderWidth& aRhs) const
+  {
+    return left == aRhs.left && top == aRhs.top &&
+           right == aRhs.right && bottom == aRhs.bottom;
+  }
+};
+
+struct WrNormalBorder {
+  WrBorderSide left;
+  WrBorderSide right;
+  WrBorderSide top;
+  WrBorderSide bottom;
+  WrBorderRadius radius;
+
+  bool operator==(const WrNormalBorder& aRhs) const
+  {
+    return left == aRhs.left && right == aRhs.right &&
+           top == aRhs.top && bottom == aRhs.bottom &&
+           radius == aRhs.radius;
+  }
+};
+
+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 WrImageBorder {
+  WrImageKey image;
+  WrNinePatchDescriptor patch;
+  WrSideOffsets2Df32 outset;
+  WrRepeatMode repeat_horizontal;
+  WrRepeatMode repeat_vertical;
+
+  bool operator==(const WrImageBorder& aRhs) const
+  {
+    return image == aRhs.image &&
+           patch == aRhs.patch && outset == aRhs.outset &&
+           repeat_horizontal == aRhs.repeat_horizontal &&
+           repeat_vertical == aRhs.repeat_vertical;
+  }
+};
+
 struct WrRect
 {
   float x;
   float y;
   float width;
   float height;
 
   bool operator==(const WrRect& aRhs) const
@@ -504,18 +594,22 @@ 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,
-                  WrBorderSide top, WrBorderSide right, WrBorderSide bottom, WrBorderSide left,
-                  WrBorderRadius radius)
+                  WrBorderWidth widths, WrNormalBorder normal_border)
+WR_FUNC;
+
+WR_INLINE void
+wr_dp_push_border_image(WrState* wrState, WrRect bounds, WrRect clip,
+                        WrBorderWidth widths, WrImageBorder image_border)
 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;