Bug 1363683 - Update webrender bindings for change to display list construction and finalization API. r?jrmuizel draft
authorKartikaya Gupta <kgupta@mozilla.com>
Mon, 15 May 2017 14:19:41 -0400
changeset 577966 9749814270b8d72445fff49362a8f2aa476c45a2
parent 577965 7fca3517c6c8a729b65abd401a885d8f8a46a7df
child 577967 b31d654f592e0ef8336c9c26c7650d38261ac63b
push id58844
push userkgupta@mozilla.com
push dateMon, 15 May 2017 18:23:13 +0000
reviewersjrmuizel
bugs1363683
milestone55.0a1
Bug 1363683 - Update webrender bindings for change to display list construction and finalization API. r?jrmuizel We now have to pass around a content size parameter to construct a display list, and we get back a content size from finalizing the display list. Since we pass the finalization results over IPC to WebRenderBridgeParent, we need to update the IPDL as well to pass this around. This changed in WR csets 425155a and 1eb84eb. MozReview-Commit-ID: 2VweYGBmVx3
gfx/layers/ipc/PWebRenderBridge.ipdl
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
gfx/layers/wr/WebRenderDisplayItemLayer.cpp
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -16,16 +16,17 @@ include protocol PTexture;
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::wr::ByteBuffer from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ExternalImageId from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ImageKey from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::FontKey from "mozilla/webrender/WebRenderTypes.h";
 using WrBuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
+using WrSize from "mozilla/webrender/webrender_ffi.h";
 using mozilla::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
 
 namespace mozilla {
 namespace layers {
 
 sync protocol PWebRenderBridge
 {
   manager PCompositorBridge;
@@ -47,20 +48,20 @@ parent:
   sync UpdateImage(ImageKey aImageKey, IntSize aSize,
                    SurfaceFormat aFormat, ByteBuffer aBytes);
   sync DeleteImage(ImageKey aImageKey);
   async DeleteCompositorAnimations(uint64_t[] aIds);
   async AddRawFont(FontKey aFontKey, ByteBuffer aBytes, uint32_t aFontIndex);
   async DeleteFont(FontKey aFontKey);
   async DPBegin(IntSize aSize);
   async DPEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
-              ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc,
+              WrSize aContentSize, ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc,
               WebRenderScrollData aScrollData);
   sync DPSyncEnd(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, uint64_t transactionId,
-                 ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc,
+                 WrSize aContentSize, ByteBuffer aDL, WrBuiltDisplayListDescriptor aDLDesc,
                  WebRenderScrollData aScrollData);
   sync DPGetSnapshot(PTexture texture);
   async AddExternalImageId(ExternalImageId aImageId, CompositableHandle aHandle);
   async AddExternalImageIdForCompositable(ExternalImageId aImageId, CompositableHandle aHandle);
   async RemoveExternalImageId(ExternalImageId aImageId);
   async SetLayerObserverEpoch(uint64_t layerObserverEpoch);
   async ClearCachedResources();
   // Schedule a composite if one isn't already scheduled.
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -97,25 +97,27 @@ WebRenderBridgeChild::DPEnd(wr::DisplayL
                             const gfx::IntSize& aSize,
                             bool aIsSync,
                             uint64_t aTransactionId,
                             const WebRenderScrollData& aScrollData)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mIsInTransaction);
 
-  wr::BuiltDisplayList dl = aBuilder.Finalize();
+  wr::BuiltDisplayList dl;
+  WrSize contentSize;
+  aBuilder.Finalize(contentSize, dl);
   ByteBuffer dlData(Move(dl.dl));
 
   if (aIsSync) {
     this->SendDPSyncEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
-                        dlData, dl.dl_desc, aScrollData);
+                        contentSize, dlData, dl.dl_desc, aScrollData);
   } else {
     this->SendDPEnd(aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(), aTransactionId,
-                    dlData, dl.dl_desc, aScrollData);
+                    contentSize, dlData, dl.dl_desc, aScrollData);
   }
 
   mParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 wr::ExternalImageId
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -299,16 +299,17 @@ WebRenderBridgeParent::RecvDPBegin(const
 }
 
 void
 WebRenderBridgeParent::HandleDPEnd(const gfx::IntSize& aSize,
                                  InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                  InfallibleTArray<OpDestroy>&& aToDestroy,
                                  const uint64_t& aFwdTransactionId,
                                  const uint64_t& aTransactionId,
+                                 const WrSize& aContentSize,
                                  const ByteBuffer& dl,
                                  const WrBuiltDisplayListDescriptor& dlDesc,
                                  const WebRenderScrollData& aScrollData)
 {
   UpdateFwdTransactionId(aFwdTransactionId);
   AutoClearReadLocks clearLocks(mReadLocks);
 
   if (mDestroyed) {
@@ -318,17 +319,17 @@ WebRenderBridgeParent::HandleDPEnd(const
     return;
   }
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
   ++mWrEpoch; // Update webrender epoch
   ProcessWebRenderCommands(aSize, aCommands, wr::NewEpoch(mWrEpoch),
-                           dl, dlDesc);
+                           aContentSize, dl, dlDesc);
   HoldPendingTransactionId(mWrEpoch, aTransactionId);
 
   mScrollData = aScrollData;
   UpdateAPZ();
 }
 
 CompositorBridgeParent*
 WebRenderBridgeParent::GetRootCompositorBridgeParent() const
@@ -400,44 +401,46 @@ WebRenderBridgeParent::GetScrollData() c
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDPEnd(const gfx::IntSize& aSize,
                                  InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                  InfallibleTArray<OpDestroy>&& aToDestroy,
                                  const uint64_t& aFwdTransactionId,
                                  const uint64_t& aTransactionId,
+                                 const WrSize& aContentSize,
                                  const ByteBuffer& dl,
                                  const WrBuiltDisplayListDescriptor& dlDesc,
                                  const WebRenderScrollData& aScrollData)
 {
   HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
-              dl, dlDesc, aScrollData);
+              aContentSize, dl, dlDesc, aScrollData);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDPSyncEnd(const gfx::IntSize &aSize,
                                      InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                      InfallibleTArray<OpDestroy>&& aToDestroy,
                                      const uint64_t& aFwdTransactionId,
                                      const uint64_t& aTransactionId,
+                                     const WrSize& aContentSize,
                                      const ByteBuffer& dl,
                                      const WrBuiltDisplayListDescriptor& dlDesc,
                                      const WebRenderScrollData& aScrollData)
 {
   HandleDPEnd(aSize, Move(aCommands), Move(aToDestroy), aFwdTransactionId, aTransactionId,
-              dl, dlDesc, aScrollData);
+              aContentSize, dl, dlDesc, aScrollData);
   return IPC_OK();
 }
 
 void
 WebRenderBridgeParent::ProcessWebRenderCommands(const gfx::IntSize &aSize,
                                                 InfallibleTArray<WebRenderParentCommand>& aCommands, const wr::Epoch& aEpoch,
-                                                const ByteBuffer& dl,
+                                                const WrSize& aContentSize, const ByteBuffer& dl,
                                                 const WrBuiltDisplayListDescriptor& dlDesc)
 {
   mCompositableHolder->SetCompositionTime(TimeStamp::Now());
 
   for (InfallibleTArray<WebRenderParentCommand>::index_type i = 0; i < aCommands.Length(); ++i) {
     const WebRenderParentCommand& cmd = aCommands[i];
     switch (cmd.type()) {
       case WebRenderParentCommand::TOpAddExternalImage: {
@@ -533,17 +536,17 @@ WebRenderBridgeParent::ProcessWebRenderC
       }
     }
   }
   if (mWidget) {
     LayoutDeviceIntSize size = mWidget->GetClientSize();
     mApi->SetWindowParameters(size);
   }
   mApi->SetRootDisplayList(gfx::Color(0.3f, 0.f, 0.f, 1.f), aEpoch, LayerSize(aSize.width, aSize.height),
-                           mPipelineId,
+                           mPipelineId, aContentSize,
                            dlDesc, dl.mData, dl.mLength);
 
   ScheduleComposition();
   DeleteOldImages();
 
   if (ShouldParentObserveEpoch()) {
     mCompositorBridge->ObserveLayerUpdate(wr::AsUint64(mPipelineId), GetChildLayerObserverEpoch(), true);
   }
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -85,24 +85,26 @@ public:
                                          const uint32_t& aFontIndex) override;
   mozilla::ipc::IPCResult RecvDeleteFont(const wr::FontKey& aFontKey) override;
   mozilla::ipc::IPCResult RecvDPBegin(const gfx::IntSize& aSize) override;
   mozilla::ipc::IPCResult RecvDPEnd(const gfx::IntSize& aSize,
                                     InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                     InfallibleTArray<OpDestroy>&& aToDestroy,
                                     const uint64_t& aFwdTransactionId,
                                     const uint64_t& aTransactionId,
+                                    const WrSize& aContentSize,
                                     const ByteBuffer& dl,
                                     const WrBuiltDisplayListDescriptor& dlDesc,
                                     const WebRenderScrollData& aScrollData) override;
   mozilla::ipc::IPCResult RecvDPSyncEnd(const gfx::IntSize& aSize,
                                         InfallibleTArray<WebRenderParentCommand>&& aCommands,
                                         InfallibleTArray<OpDestroy>&& aToDestroy,
                                         const uint64_t& aFwdTransactionId,
                                         const uint64_t& aTransactionId,
+                                        const WrSize& aContentSize,
                                         const ByteBuffer& dl,
                                         const WrBuiltDisplayListDescriptor& dlDesc,
                                         const WebRenderScrollData& aScrollData) override;
   mozilla::ipc::IPCResult RecvDPGetSnapshot(PTextureParent* aTexture) override;
 
   mozilla::ipc::IPCResult RecvAddExternalImageId(const ExternalImageId& aImageId,
                                                  const CompositableHandle& aHandle) override;
   mozilla::ipc::IPCResult RecvAddExternalImageIdForCompositable(const ExternalImageId& aImageId,
@@ -165,27 +167,28 @@ public:
     return ++sIdNameSpace;
   }
 
 private:
   virtual ~WebRenderBridgeParent();
 
   void DeleteOldImages();
   void ProcessWebRenderCommands(const gfx::IntSize &aSize, InfallibleTArray<WebRenderParentCommand>& commands, const wr::Epoch& aEpoch,
-                                    const ByteBuffer& dl,
+                                    const WrSize& aContentSize, const ByteBuffer& dl,
                                     const WrBuiltDisplayListDescriptor& dlDesc);
   void ScheduleComposition();
   void ClearResources();
   uint64_t GetChildLayerObserverEpoch() const { return mChildLayerObserverEpoch; }
   bool ShouldParentObserveEpoch();
   void HandleDPEnd(const gfx::IntSize& aSize,
                    InfallibleTArray<WebRenderParentCommand>&& aCommands,
                    InfallibleTArray<OpDestroy>&& aToDestroy,
                    const uint64_t& aFwdTransactionId,
                    const uint64_t& aTransactionId,
+                   const WrSize& aContentSize,
                    const ByteBuffer& dl,
                    const WrBuiltDisplayListDescriptor& dlDesc,
                    const WebRenderScrollData& aScrollData);
 
   void SampleAnimations(nsTArray<WrOpacityProperty>& aOpacityArray,
                         nsTArray<WrTransformProperty>& aTransformArray);
 
   CompositorBridgeParent* GetRootCompositorBridgeParent() const;
--- a/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
+++ b/gfx/layers/wr/WebRenderDisplayItemLayer.cpp
@@ -62,21 +62,22 @@ WebRenderDisplayItemLayer::RenderLayer(w
   // The conversion from ParentLayerPixel to LayerPixel below is a result of
   // changing the reference layer from "this layer" to the "the layer that
   // created aSc".
   LayerRect clipInParentLayerSpace = ViewAs<LayerPixel>(clip,
       PixelCastJustification::MovingDownToChildren);
   aBuilder.PushClip(aSc.ToRelativeWrRect(clipInParentLayerSpace), imageMask);
 
   if (mItem) {
-    wr::DisplayListBuilder builder(WrBridge()->GetPipeline());
+    WrSize contentSize; // this won't actually be used by anything
+    wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize);
     // We might have recycled this layer. Throw away the old commands.
     mParentCommands.Clear();
     mItem->CreateWebRenderCommands(builder, aSc, mParentCommands, this);
-    mBuiltDisplayList = builder.Finalize();
+    builder.Finalize(contentSize, mBuiltDisplayList);
   } else {
     // else we have an empty transaction and just use the
     // old commands.
     WebRenderLayerManager* manager = WrManager();
     MOZ_ASSERT(manager);
 
     // Since our recording relies on our parent layer's transform and stacking context
     // If this layer or our parent changed, this empty transaction won't work.
--- a/gfx/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -187,17 +187,18 @@ WebRenderLayerManager::EndTransactionInt
   LayoutDeviceIntSize size = mWidget->GetClientSize();
   if (!WrBridge()->DPBegin(size.ToUnknownSize())) {
     return false;
   }
   DiscardCompositorAnimations();
   mRoot->StartPendingAnimations(mAnimationReadyTime);
 
   StackingContextHelper sc;
-  wr::DisplayListBuilder builder(WrBridge()->GetPipeline());
+  WrSize contentSize { (float)size.width, (float)size.height };
+  wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize);
   WebRenderLayer::ToWebRenderLayer(mRoot)->RenderLayer(builder, sc);
   WrBridge()->ClearReadLocks();
 
   // We can't finish this transaction so return. This usually
   // happens in an empty transaction where we can't repaint a painted layer.
   // In this case, leave the transaction open and let a full transaction happen.
   if (mTransactionIncomplete) {
     DiscardLocalImages();
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -199,24 +199,26 @@ WebRenderAPI::GenerateFrame(const nsTArr
                                         aTransformArray.Length());
 }
 
 void
 WebRenderAPI::SetRootDisplayList(gfx::Color aBgColor,
                                  Epoch aEpoch,
                                  LayerSize aViewportSize,
                                  WrPipelineId pipeline_id,
+                                 const WrSize& content_size,
                                  WrBuiltDisplayListDescriptor dl_descriptor,
                                  uint8_t *dl_data,
                                  size_t dl_size)
 {
     wr_api_set_root_display_list(mWrApi,
                                  aEpoch,
                                  aViewportSize.width, aViewportSize.height,
                                  pipeline_id,
+                                 content_size,
                                  dl_descriptor,
                                  dl_data,
                                  dl_size);
 }
 
 void
 WebRenderAPI::ClearRootDisplayList(Epoch aEpoch,
                                    WrPipelineId pipeline_id)
@@ -498,20 +500,21 @@ WebRenderAPI::SetProfilerEnabled(bool aE
 
 void
 WebRenderAPI::RunOnRenderThread(UniquePtr<RendererEvent> aEvent)
 {
   auto event = reinterpret_cast<uintptr_t>(aEvent.release());
   wr_api_send_external_event(mWrApi, event);
 }
 
-DisplayListBuilder::DisplayListBuilder(PipelineId aId)
+DisplayListBuilder::DisplayListBuilder(PipelineId aId,
+                                       const WrSize& aContentSize)
 {
   MOZ_COUNT_CTOR(DisplayListBuilder);
-  mWrState = wr_state_new(aId);
+  mWrState = wr_state_new(aId, aContentSize);
 }
 
 DisplayListBuilder::~DisplayListBuilder()
 {
   MOZ_COUNT_DTOR(DisplayListBuilder);
   wr_state_delete(mWrState);
 }
 
@@ -522,24 +525,24 @@ DisplayListBuilder::Begin(const LayerInt
 }
 
 void
 DisplayListBuilder::End()
 {
   wr_dp_end(mWrState);
 }
 
-BuiltDisplayList
-DisplayListBuilder::Finalize()
+void
+DisplayListBuilder::Finalize(WrSize& aOutContentSize,
+                             BuiltDisplayList& aOutDisplayList)
 {
-  BuiltDisplayList dl;
   wr_api_finalize_builder(mWrState,
-                          &dl.dl_desc,
-                          &dl.dl.inner);
-  return dl;
+                          &aOutContentSize,
+                          &aOutDisplayList.dl_desc,
+                          &aOutDisplayList.dl.inner);
 }
 
 void
 DisplayListBuilder::PushStackingContext(const WrRect& aBounds,
                                         const uint64_t& aAnimationId,
                                         const float* aOpacity,
                                         const gfx::Matrix4x4* aTransform,
                                         const WrMixBlendMode& aMixBlendMode)
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -53,16 +53,17 @@ public:
   void GenerateFrame(const nsTArray<WrOpacityProperty>& aOpacityArray,
                      const nsTArray<WrTransformProperty>& aTransformArray);
 
   void SetWindowParameters(LayoutDeviceIntSize size);
   void SetRootDisplayList(gfx::Color aBgColor,
                           Epoch aEpoch,
                           LayerSize aViewportSize,
                           WrPipelineId pipeline_id,
+                          const WrSize& content_size,
                           WrBuiltDisplayListDescriptor dl_descriptor,
                           uint8_t *dl_data,
                           size_t dl_size);
 
   void ClearRootDisplayList(Epoch aEpoch,
                             WrPipelineId pipeline_id);
 
   void SetRootPipeline(wr::PipelineId aPipeline);
@@ -126,25 +127,27 @@ protected:
   friend class layers::WebRenderBridgeParent;
 };
 
 /// This is a simple C++ wrapper around WrState defined in the rust bindings.
 /// We may want to turn this into a direct wrapper on top of WebRenderFrameBuilder
 /// instead, so the interface may change a bit.
 class DisplayListBuilder {
 public:
-  explicit DisplayListBuilder(wr::PipelineId aId);
+  explicit DisplayListBuilder(wr::PipelineId aId,
+                              const WrSize& aContentSize);
   DisplayListBuilder(DisplayListBuilder&&) = default;
 
   ~DisplayListBuilder();
 
   void Begin(const LayerIntSize& aSize);
 
   void End();
-  wr::BuiltDisplayList Finalize();
+  void Finalize(WrSize& aOutContentSize,
+                wr::BuiltDisplayList& aOutDisplayList);
 
   void PushStackingContext(const WrRect& aBounds, // TODO: We should work with strongly typed rects
                            const float aOpacity,
                            const gfx::Matrix4x4& aTransform,
                            const WrMixBlendMode& aMixBlendMode);
 
   void PushStackingContext(const WrRect& aBounds, // TODO: We should work with strongly typed rects
                            const uint64_t& aAnimationId,
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -162,16 +162,26 @@ impl<U> Into<TypedPoint2D<f32, U>> for W
 
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
 pub struct WrSize {
     width: f32,
     height: f32,
 }
 
+impl WrSize {
+    fn new(width: f32, height: f32) -> WrSize {
+        WrSize { width: width, height: height }
+    }
+
+    fn zero() -> WrSize {
+        WrSize { width: 0.0, height: 0.0 }
+    }
+}
+
 impl<U> Into<TypedSize2D<f32, U>> for WrSize {
     fn into(self) -> TypedSize2D<f32, U> {
         TypedSize2D::new(self.width, self.height)
     }
 }
 
 #[repr(C)]
 #[derive(Debug, Clone, Copy)]
@@ -1008,16 +1018,17 @@ pub extern "C" fn wr_api_set_window_para
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_api_set_root_display_list(api: &mut WrAPI,
                                                       epoch: WrEpoch,
                                                       viewport_width: f32,
                                                       viewport_height: f32,
                                                       pipeline_id: WrPipelineId,
+                                                      content_size: WrSize,
                                                       dl_descriptor: WrBuiltDisplayListDescriptor,
                                                       dl_data: *mut u8,
                                                       dl_size: usize) {
     let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
     // See the documentation of set_display_list in api.rs. I don't think
     // it makes a difference in gecko at the moment(until APZ is figured out)
     // but I suppose it is a good default.
     let preserve_frame_state = true;
@@ -1026,27 +1037,27 @@ pub unsafe extern "C" fn wr_api_set_root
     let mut dl_vec = Vec::new();
     // XXX: see if we can get rid of the copy here
     dl_vec.extend_from_slice(dl_slice);
     let dl = BuiltDisplayList::from_data(dl_vec, dl_descriptor);
 
     api.set_display_list(Some(root_background_color),
                          epoch,
                          LayoutSize::new(viewport_width, viewport_height),
-                         (pipeline_id, dl),
+                         (pipeline_id, content_size.into(), dl),
                          preserve_frame_state);
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_api_clear_root_display_list(api: &mut WrAPI,
                                                         epoch: WrEpoch,
                                                         pipeline_id: WrPipelineId) {
     let root_background_color = ColorF::new(0.3, 0.0, 0.0, 1.0);
     let preserve_frame_state = true;
-    let frame_builder = WebRenderFrameBuilder::new(pipeline_id);
+    let frame_builder = WebRenderFrameBuilder::new(pipeline_id, WrSize::zero());
 
     api.set_display_list(Some(root_background_color),
                          epoch,
                          LayoutSize::new(0.0, 0.0),
                          frame_builder.dl_builder.finalize(),
                          preserve_frame_state);
 }
 
@@ -1143,37 +1154,40 @@ pub unsafe extern "C" fn wr_api_get_name
 
 pub struct WebRenderFrameBuilder {
     pub root_pipeline_id: WrPipelineId,
     pub dl_builder: webrender_traits::DisplayListBuilder,
     pub scroll_clips_defined: HashSet<ClipId>,
 }
 
 impl WebRenderFrameBuilder {
-    pub fn new(root_pipeline_id: WrPipelineId) -> WebRenderFrameBuilder {
+    pub fn new(root_pipeline_id: WrPipelineId,
+               content_size: WrSize) -> WebRenderFrameBuilder {
         WebRenderFrameBuilder {
             root_pipeline_id: root_pipeline_id,
-            dl_builder: webrender_traits::DisplayListBuilder::new(root_pipeline_id),
+            dl_builder: webrender_traits::DisplayListBuilder::new(root_pipeline_id, content_size.into()),
             scroll_clips_defined: HashSet::new(),
         }
     }
 }
 
 pub struct WrState {
     pipeline_id: WrPipelineId,
     frame_builder: WebRenderFrameBuilder,
 }
 
 #[no_mangle]
-pub extern "C" fn wr_state_new(pipeline_id: WrPipelineId) -> *mut WrState {
+pub extern "C" fn wr_state_new(pipeline_id: WrPipelineId,
+                               content_size: WrSize) -> *mut WrState {
     assert!(unsafe { is_in_main_thread() });
 
     let state = Box::new(WrState {
                              pipeline_id: pipeline_id,
-                             frame_builder: WebRenderFrameBuilder::new(pipeline_id),
+                             frame_builder: WebRenderFrameBuilder::new(pipeline_id,
+                                                                       content_size),
                          });
 
     Box::into_raw(state)
 }
 
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
 #[no_mangle]
 pub extern "C" fn wr_state_delete(state: *mut WrState) {
@@ -1626,21 +1640,24 @@ pub extern "C" fn wr_dp_push_box_shadow(
                           blur_radius,
                           spread_radius,
                           border_radius,
                           clip_mode);
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_api_finalize_builder(state: &mut WrState,
+                                                 content_size: &mut WrSize,
                                                  dl_descriptor: &mut WrBuiltDisplayListDescriptor,
                                                  dl_data: &mut WrVecU8) {
     let frame_builder = mem::replace(&mut state.frame_builder,
-                                     WebRenderFrameBuilder::new(state.pipeline_id));
-    let (_, dl) = frame_builder.dl_builder.finalize();
+                                     WebRenderFrameBuilder::new(state.pipeline_id,
+                                                                WrSize::zero()));
+    let (_, size, dl) = frame_builder.dl_builder.finalize();
+    *content_size = WrSize::new(size.width, size.height);
     let (data, descriptor) = dl.into_data();
     *dl_data = WrVecU8::from_vec(data);
     *dl_descriptor = descriptor;
 }
 
 #[no_mangle]
 pub unsafe extern "C" fn wr_dp_push_built_display_list(state: &mut WrState,
                                                        dl_descriptor: WrBuiltDisplayListDescriptor,
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -182,16 +182,26 @@ struct WrPipelineId {
   bool operator==(const WrPipelineId& aOther) const {
     return mNamespace == aOther.mNamespace &&
            mHandle == aOther.mHandle;
   }
 };
 
 struct WrState;
 
+struct WrSize {
+  float width;
+  float height;
+
+  bool operator==(const WrSize& aOther) const {
+    return width == aOther.width &&
+           height == aOther.height;
+  }
+};
+
 struct WrBuiltDisplayListDescriptor {
   size_t display_list_items_size;
   uint64_t builder_start_time;
   uint64_t builder_finish_time;
 
   bool operator==(const WrBuiltDisplayListDescriptor& aOther) const {
     return display_list_items_size == aOther.display_list_items_size &&
            builder_start_time == aOther.builder_start_time &&
@@ -299,26 +309,16 @@ struct WrBorderSide {
   WrBorderStyle style;
 
   bool operator==(const WrBorderSide& aOther) const {
     return color == aOther.color &&
            style == aOther.style;
   }
 };
 
-struct WrSize {
-  float width;
-  float height;
-
-  bool operator==(const WrSize& aOther) const {
-    return width == aOther.width &&
-           height == aOther.height;
-  }
-};
-
 struct WrBorderRadius {
   WrSize top_left;
   WrSize top_right;
   WrSize bottom_left;
   WrSize bottom_right;
 
   bool operator==(const WrBorderRadius& aOther) const {
     return top_left == aOther.top_left &&
@@ -543,16 +543,17 @@ WR_FUNC;
 
 WR_INLINE
 void wr_api_delete_image(WrAPI* aApi,
                          WrImageKey aKey)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_finalize_builder(WrState* aState,
+                             WrSize* aContentSize,
                              WrBuiltDisplayListDescriptor* aDlDescriptor,
                              WrVecU8* aDlData)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_generate_frame(WrAPI* aApi)
 WR_FUNC;
 
@@ -574,16 +575,17 @@ void wr_api_send_external_event(WrAPI* a
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
 void wr_api_set_root_display_list(WrAPI* aApi,
                                   WrEpoch aEpoch,
                                   float aViewportWidth,
                                   float aViewportHeight,
                                   WrPipelineId aPipelineId,
+                                  WrSize aContentSize,
                                   WrBuiltDisplayListDescriptor aDlDescriptor,
                                   uint8_t* aDlData,
                                   size_t aDlSize)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_set_root_pipeline(WrAPI* aApi,
                               WrPipelineId aPipelineId)
@@ -862,17 +864,18 @@ void wr_scroll_layer_with_id(WrAPI* aApi
                              WrPoint aNewScrollOrigin)
 WR_FUNC;
 
 WR_INLINE
 void wr_state_delete(WrState* aState)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
-WrState* wr_state_new(WrPipelineId aPipelineId)
+WrState* wr_state_new(WrPipelineId aPipelineId,
+                      WrSize aContentSize)
 WR_FUNC;
 
 WR_INLINE
 void wr_vec_u8_free(WrVecU8 aV)
 WR_FUNC;
 
 WR_INLINE
 bool wr_window_new(WrWindowId aWindowId,