Bug 1518605 - New WebRender ClipId/SpatialId API. r=kats
authorDzmitry Malyshau <dmalyshau@mozilla.com>
Wed, 09 Jan 2019 03:27:07 +0000
changeset 452998 9dcf694ba4b32bbebc7ae4e88675ddee1c40996a
parent 452997 9e0410ca1106ca09b30d29e48387d4447094eb79
child 452999 bd59070a24d72da867cf1531e89e75ffed2848ae
push id35338
push userebalazs@mozilla.com
push dateWed, 09 Jan 2019 09:25:13 +0000
treeherdermozilla-central@1f5e1707e04e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1518605, 1503447
milestone66.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 1518605 - New WebRender ClipId/SpatialId API. r=kats Port to separate SpatialId from ClipId in Webrender API (WR PR #3251). Patch was originally written and reviewed on bug 1503447. Depends on D16005 Differential Revision: https://phabricator.services.mozilla.com/D16006
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/ClipManager.cpp
gfx/layers/wr/ClipManager.h
gfx/layers/wr/StackingContextHelper.cpp
gfx/layers/wr/StackingContextHelper.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderCommandBuilder.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/WebRenderTypes.cpp
gfx/webrender_bindings/WebRenderTypes.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
layout/generic/nsBulletFrame.cpp
layout/generic/nsImageFrame.cpp
layout/painting/nsCSSRenderingBorders.cpp
layout/painting/nsDisplayList.cpp
widget/windows/nsWindow.cpp
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -394,24 +394,29 @@ void AsyncImagePipelineManager::ApplyAsy
 
   aPipeline->mIsChanged = false;
 
   wr::LayoutSize contentSize{aPipeline->mScBounds.Width(),
                              aPipeline->mScBounds.Height()};
   wr::DisplayListBuilder builder(aPipelineId, contentSize);
 
   float opacity = 1.0f;
-  Maybe<wr::WrClipId> referenceFrameId = builder.PushStackingContext(
+  Maybe<wr::WrSpatialId> referenceFrameId = builder.PushStackingContext(
       wr::ToRoundedLayoutRect(aPipeline->mScBounds), nullptr, nullptr, &opacity,
       aPipeline->mScTransform.IsIdentity() ? nullptr : &aPipeline->mScTransform,
       wr::TransformStyle::Flat, nullptr, aPipeline->mMixBlendMode,
       nsTArray<wr::WrFilterOp>(), true,
       // This is fine to do unconditionally because we only push images here.
       wr::RasterSpace::Screen());
 
+  Maybe<wr::SpaceAndClipChainHelper> spaceAndClipChainHelper;
+  if (referenceFrameId) {
+    spaceAndClipChainHelper.emplace(builder, referenceFrameId.ref());
+  }
+
   if (aPipeline->mCurrentTexture && !keys.IsEmpty()) {
     LayoutDeviceRect rect(0, 0, aPipeline->mCurrentTexture->GetSize().width,
                           aPipeline->mCurrentTexture->GetSize().height);
     if (aPipeline->mScaleToSize.isSome()) {
       rect = LayoutDeviceRect(0, 0, aPipeline->mScaleToSize.value().width,
                               aPipeline->mScaleToSize.value().height);
     }
 
@@ -426,16 +431,17 @@ void AsyncImagePipelineManager::ApplyAsy
     } else {
       MOZ_ASSERT(keys.Length() == 1);
       builder.PushImage(wr::ToRoundedLayoutRect(rect),
                         wr::ToRoundedLayoutRect(rect), true, aPipeline->mFilter,
                         keys[0]);
     }
   }
 
+  spaceAndClipChainHelper.reset();
   builder.PopStackingContext(referenceFrameId.isSome());
 
   wr::BuiltDisplayList dl;
   wr::LayoutSize builderContentSize;
   builder.Finalize(builderContentSize, dl);
   aSceneBuilderTxn.SetDisplayList(
       gfx::Color(0.f, 0.f, 0.f, 0.f), aEpoch,
       LayerSize(aPipeline->mScBounds.Width(), aPipeline->mScBounds.Height()),
--- a/gfx/layers/wr/ClipManager.cpp
+++ b/gfx/layers/wr/ClipManager.cpp
@@ -12,22 +12,20 @@
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/webrender/WebRenderAPI.h"
 #include "nsDisplayList.h"
 #include "nsStyleStructInlines.h"
 #include "UnitTransforms.h"
 
 #define CLIP_LOG(...)
-/*
-#define CLIP_LOG(...) printf_stderr("CLIP: " __VA_ARGS__)
-#define CLIP_LOG(...) if (XRE_IsContentProcess()) {         \
-                        printf_stderr("CLIP: " __VA_ARGS__) \
-                      }
-*/
+//#define CLIP_LOG(...) printf_stderr("CLIP: " __VA_ARGS__)
+//#define CLIP_LOG(...) if (XRE_IsContentProcess()) {         \
+//                        printf_stderr("CLIP: " __VA_ARGS__);\
+//                      }
 
 namespace mozilla {
 namespace layers {
 
 ClipManager::ClipManager() : mManager(nullptr), mBuilder(nullptr) {}
 
 void ClipManager::BeginBuild(WebRenderLayerManager* aManager,
                              wr::DisplayListBuilder& aBuilder) {
@@ -61,88 +59,93 @@ void ClipManager::BeginList(const Stacki
       mCacheStack.emplace();
     }
   }
 
   ItemClips clips(nullptr, nullptr, false);
   if (!mItemClipStack.empty()) {
     clips.CopyOutputsFrom(mItemClipStack.top());
   }
+
+  if (aStackingContext.ReferenceFrameId()) {
+    clips.mScrollId = aStackingContext.ReferenceFrameId().ref();
+  }
+
   mItemClipStack.push(clips);
 }
 
 void ClipManager::EndList(const StackingContextHelper& aStackingContext) {
   MOZ_ASSERT(!mItemClipStack.empty());
-  mItemClipStack.top().Unapply(mBuilder);
+  mBuilder->SetClipChainLeaf(Nothing());
   mItemClipStack.pop();
 
   if (aStackingContext.AffectsClipPositioning()) {
     if (aStackingContext.ReferenceFrameId()) {
       PopOverrideForASR(mItemClipStack.empty() ? nullptr
                                                : mItemClipStack.top().mASR);
     } else {
       MOZ_ASSERT(!mCacheStack.empty());
       mCacheStack.pop();
     }
   }
 }
 
 void ClipManager::PushOverrideForASR(const ActiveScrolledRoot* aASR,
-                                     const wr::WrClipId& aClipId) {
-  Maybe<wr::WrClipId> scrollId = GetScrollLayer(aASR);
-  MOZ_ASSERT(scrollId.isSome());
+                                     const wr::WrSpatialId& aSpatialId) {
+  Maybe<wr::WrSpaceAndClip> spaceAndClip = GetScrollLayer(aASR);
+  MOZ_ASSERT(spaceAndClip.isSome());
 
-  CLIP_LOG("Pushing override %zu -> %s\n", scrollId->id,
-           Stringify(aClipId.id).c_str());
-  auto it = mASROverride.insert({*scrollId, std::stack<wr::WrClipId>()});
-  it.first->second.push(aClipId);
+  CLIP_LOG("Pushing %p override %zu -> %s\n", aASR, spaceAndClip->space.id,
+           Stringify(aSpatialId.id).c_str());
+
+  auto it =
+      mASROverride.insert({spaceAndClip->space, std::stack<wr::WrSpatialId>()});
+  it.first->second.push(aSpatialId);
 
   // Start a new cache
   mCacheStack.emplace();
 }
 
 void ClipManager::PopOverrideForASR(const ActiveScrolledRoot* aASR) {
   MOZ_ASSERT(!mCacheStack.empty());
   mCacheStack.pop();
 
-  Maybe<wr::WrClipId> scrollId = GetScrollLayer(aASR);
-  MOZ_ASSERT(scrollId.isSome());
+  Maybe<wr::WrSpaceAndClip> spaceAndClip = GetScrollLayer(aASR);
+  MOZ_ASSERT(spaceAndClip.isSome());
 
-  auto it = mASROverride.find(*scrollId);
-  MOZ_ASSERT(it != mASROverride.end());
-  MOZ_ASSERT(!(it->second.empty()));
-  CLIP_LOG("Popping override %zu -> %s\n", scrollId->id,
+  auto it = mASROverride.find(spaceAndClip->space);
+  CLIP_LOG("Popping %p override %zu -> %s\n", aASR, spaceAndClip->space.id,
            Stringify(it->second.top().id).c_str());
+
   it->second.pop();
   if (it->second.empty()) {
     mASROverride.erase(it);
   }
 }
 
-Maybe<wr::WrClipId> ClipManager::ClipIdAfterOverride(
-    const Maybe<wr::WrClipId>& aClipId) {
-  if (!aClipId) {
-    return Nothing();
-  }
-  auto it = mASROverride.find(*aClipId);
+wr::WrSpatialId ClipManager::SpatialIdAfterOverride(
+    const wr::WrSpatialId& aSpatialId) {
+  auto it = mASROverride.find(aSpatialId);
   if (it == mASROverride.end()) {
-    return aClipId;
+    return aSpatialId;
   }
   MOZ_ASSERT(!it->second.empty());
-  CLIP_LOG("Overriding %zu with %s\n", aClipId->id,
+  CLIP_LOG("Overriding %zu with %s\n", aSpatialId.id,
            Stringify(it->second.top().id).c_str());
-  return Some(it->second.top());
+
+  return it->second.top();
 }
 
-void ClipManager::BeginItem(nsDisplayItem* aItem,
-                            const StackingContextHelper& aStackingContext) {
-  CLIP_LOG("processing item %p\n", aItem);
-
+wr::WrSpaceAndClipChain ClipManager::SwitchItem(
+    nsDisplayItem* aItem, const StackingContextHelper& aStackingContext) {
   const DisplayItemClipChain* clip = aItem->GetClipChain();
   const ActiveScrolledRoot* asr = aItem->GetActiveScrolledRoot();
+  CLIP_LOG("processing item %p (%s) asr %p\n", aItem,
+           DisplayItemTypeName(aItem->GetType()), asr);
+
   DisplayItemType type = aItem->GetType();
   if (type == DisplayItemType::TYPE_STICKY_POSITION) {
     // For sticky position items, the ASR is computed differently depending
     // on whether the item has a fixed descendant or not. But for WebRender
     // purposes we always want to use the ASR that would have been used if it
     // didn't have fixed descendants, which is stored as the "container ASR" on
     // the sticky item.
     asr = static_cast<nsDisplayStickyPosition*>(aItem)->GetContainerASR();
@@ -169,134 +172,127 @@ void ClipManager::BeginItem(nsDisplayIte
   MOZ_ASSERT(!mItemClipStack.empty());
   if (clips.HasSameInputs(mItemClipStack.top())) {
     // Early-exit because if the clips are the same as aItem's previous sibling,
     // then we don't need to do do the work of popping the old stuff and then
     // pushing it right back on for the new item. Note that if aItem doesn't
     // have a previous sibling, that means BeginList would have been called
     // just before this, which will have pushed a ItemClips(nullptr, nullptr)
     // onto mItemClipStack, so the HasSameInputs check should return false.
-    CLIP_LOG("early-exit for %p\n", aItem);
-    return;
+    CLIP_LOG("\tearly-exit for %p\n", aItem);
+    return mItemClipStack.top().GetSpaceAndClipChain();
   }
 
   // Pop aItem's previous sibling's stuff from mBuilder in preparation for
   // pushing aItem's stuff.
-  mItemClipStack.top().Unapply(mBuilder);
   mItemClipStack.pop();
 
   // Zoom display items report their bounds etc using the parent document's
   // APD because zoom items act as a conversion layer between the two different
   // APDs.
   int32_t auPerDevPixel;
   if (type == DisplayItemType::TYPE_ZOOM) {
     auPerDevPixel =
         static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
   } else {
     auPerDevPixel = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   }
 
   // If the leaf of the clip chain is going to be merged with the display item's
   // clip rect, then we should create a clip chain id from the leaf's parent.
   if (separateLeaf) {
+    CLIP_LOG("\tseparate leaf detected, ignoring the last clip\n");
     clip = clip->mParent;
   }
 
   // There are two ASR chains here that we need to be fully defined. One is the
   // ASR chain pointed to by |asr|. The other is the
   // ASR chain pointed to by clip->mASR. We pick the leafmost
   // of these two chains because that one will include the other. Calling
   // DefineScrollLayers with this leafmost ASR will recursively define all the
   // ASRs that we care about for this item, but will not actually push
   // anything onto the WR stack.
   const ActiveScrolledRoot* leafmostASR = asr;
   if (clip) {
     leafmostASR = ActiveScrolledRoot::PickDescendant(leafmostASR, clip->mASR);
   }
-  Maybe<wr::WrClipId> leafmostId =
+  Maybe<wr::WrSpaceAndClip> leafmostId =
       DefineScrollLayers(leafmostASR, aItem, aStackingContext);
 
   // Define all the clips in the item's clip chain, and obtain a clip chain id
   // for it.
   clips.mClipChainId = DefineClipChain(clip, auPerDevPixel, aStackingContext);
 
-  if (clip) {
-    // If the clip's ASR is different, then we need to set the scroll id
-    // explicitly to match the desired ASR.
-    Maybe<wr::WrClipId> scrollId = GetScrollLayer(asr);
-    MOZ_ASSERT(scrollId.isSome());
-    clips.mScrollId = ClipIdAfterOverride(scrollId);
-  } else {
-    // If we don't have a clip at all, then we don't want to explicitly push
-    // the ASR either, because as with the first clause of this if condition,
-    // the item might get hoisted out of a stacking context that was pushed
-    // between the |asr| and this |aItem|. Instead we just leave clips.mScrollId
-    // empty and things seem to work out.
-    // XXX: there might be cases where things don't just "work out", in which
-    // case we might need to do something smarter here.
-  }
+  Maybe<wr::WrSpaceAndClip> spaceAndClip = GetScrollLayer(asr);
+  MOZ_ASSERT(spaceAndClip.isSome());
+  clips.mScrollId = SpatialIdAfterOverride(spaceAndClip->space);
+  CLIP_LOG("\tassigning %d -> %d\n", (int)spaceAndClip->space.id,
+           (int)clips.mScrollId.id);
 
   // Now that we have the scroll id and a clip id for the item, push it onto
   // the WR stack.
-  clips.Apply(mBuilder, auPerDevPixel);
+  clips.UpdateSeparateLeaf(*mBuilder, auPerDevPixel);
+  auto spaceAndClipChain = clips.GetSpaceAndClipChain();
   mItemClipStack.push(clips);
 
   CLIP_LOG("done setup for %p\n", aItem);
+  return spaceAndClipChain;
 }
 
-Maybe<wr::WrClipId> ClipManager::GetScrollLayer(
+Maybe<wr::WrSpaceAndClip> ClipManager::GetScrollLayer(
     const ActiveScrolledRoot* aASR) {
   for (const ActiveScrolledRoot* asr = aASR; asr; asr = asr->mParent) {
-    Maybe<wr::WrClipId> scrollId =
+    Maybe<wr::WrSpaceAndClip> spaceAndClip =
         mBuilder->GetScrollIdForDefinedScrollLayer(asr->GetViewId());
-    if (scrollId) {
-      return scrollId;
+    if (spaceAndClip) {
+      return spaceAndClip;
     }
 
     // If this ASR doesn't have a scroll ID, then we should check its ancestor.
     // There may not be one defined because the ASR may not be scrollable or we
     // failed to get the scroll metadata.
   }
 
-  Maybe<wr::WrClipId> scrollId = mBuilder->GetScrollIdForDefinedScrollLayer(
-      ScrollableLayerGuid::NULL_SCROLL_ID);
-  MOZ_ASSERT(scrollId.isSome());
-  return scrollId;
+  Maybe<wr::WrSpaceAndClip> spaceAndClip =
+      mBuilder->GetScrollIdForDefinedScrollLayer(
+          ScrollableLayerGuid::NULL_SCROLL_ID);
+  MOZ_ASSERT(spaceAndClip.isSome());
+  return spaceAndClip;
 }
 
-Maybe<wr::WrClipId> ClipManager::DefineScrollLayers(
+Maybe<wr::WrSpaceAndClip> ClipManager::DefineScrollLayers(
     const ActiveScrolledRoot* aASR, nsDisplayItem* aItem,
     const StackingContextHelper& aSc) {
   if (!aASR) {
     // Recursion base case
     return Nothing();
   }
   ScrollableLayerGuid::ViewID viewId = aASR->GetViewId();
-  Maybe<wr::WrClipId> scrollId =
+  Maybe<wr::WrSpaceAndClip> spaceAndClip =
       mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
-  if (scrollId) {
+  if (spaceAndClip) {
     // If we've already defined this scroll layer before, we can early-exit
-    return scrollId;
+    return spaceAndClip;
   }
   // Recurse to define the ancestors
-  Maybe<wr::WrClipId> ancestorScrollId =
+  Maybe<wr::WrSpaceAndClip> ancestorSpaceAndClip =
       DefineScrollLayers(aASR->mParent, aItem, aSc);
 
   Maybe<ScrollMetadata> metadata =
       aASR->mScrollableFrame->ComputeScrollMetadata(
           mManager, aItem->ReferenceFrame(), Nothing(), nullptr);
   if (!metadata) {
     MOZ_ASSERT_UNREACHABLE("Expected scroll metadata to be available!");
-    return ancestorScrollId;
+    return ancestorSpaceAndClip;
   }
 
   FrameMetrics& metrics = metadata->GetMetrics();
   if (!metrics.IsScrollable()) {
     // This item is a scrolling no-op, skip over it in the ASR chain.
-    return ancestorScrollId;
+    return ancestorSpaceAndClip;
   }
 
   LayoutDeviceRect contentRect =
       metrics.GetExpandedScrollableRect() * metrics.GetDevPixelsPerCSSPixel();
   LayoutDeviceRect clipBounds = LayoutDeviceRect::FromUnknownRect(
       metrics.GetCompositionBounds().ToUnknownRect());
   // The content rect that we hand to PushScrollLayer should be relative to
   // the same origin as the clipBounds that we hand to PushScrollLayer - that
@@ -304,22 +300,23 @@ Maybe<wr::WrClipId> ClipManager::DefineS
   // However, when we get the scrollable rect from the FrameMetrics, the origin
   // has nothing to do with the position of the frame but instead represents
   // the minimum allowed scroll offset of the scrollable content. While APZ
   // uses this to clamp the scroll position, we don't need to send this to
   // WebRender at all. Instead, we take the position from the composition
   // bounds.
   contentRect.MoveTo(clipBounds.TopLeft());
 
-  Maybe<wr::WrClipId> parent = ClipIdAfterOverride(ancestorScrollId);
-  scrollId = Some(mBuilder->DefineScrollLayer(
-      viewId, parent, wr::ToRoundedLayoutRect(contentRect),
-      wr::ToRoundedLayoutRect(clipBounds)));
-
-  return scrollId;
+  Maybe<wr::WrSpaceAndClip> parent = ancestorSpaceAndClip;
+  if (parent) {
+    parent->space = SpatialIdAfterOverride(parent->space);
+  }
+  return Some(mBuilder->DefineScrollLayer(viewId, parent,
+                                          wr::ToRoundedLayoutRect(contentRect),
+                                          wr::ToRoundedLayoutRect(clipBounds)));
 }
 
 Maybe<wr::WrClipChainId> ClipManager::DefineClipChain(
     const DisplayItemClipChain* aChain, int32_t aAppUnitsPerDevPixel,
     const StackingContextHelper& aSc) {
   AutoTArray<wr::WrClipId, 6> clipIds;
   // Iterate through the clips in the current item's clip chain, define them
   // in WR, and put their IDs into |clipIds|.
@@ -340,25 +337,25 @@ Maybe<wr::WrClipChainId> ClipManager::De
     }
 
     LayoutDeviceRect clip = LayoutDeviceRect::FromAppUnits(
         chain->mClip.GetClipRect(), aAppUnitsPerDevPixel);
     nsTArray<wr::ComplexClipRegion> wrRoundedRects;
     chain->mClip.ToComplexClipRegions(aAppUnitsPerDevPixel, aSc,
                                       wrRoundedRects);
 
-    Maybe<wr::WrClipId> scrollId = GetScrollLayer(chain->mASR);
+    Maybe<wr::WrSpaceAndClip> spaceAndClip = GetScrollLayer(chain->mASR);
     // Before calling DefineClipChain we defined the ASRs by calling
     // DefineScrollLayers, so we must have a scrollId here.
-    MOZ_ASSERT(scrollId.isSome());
+    MOZ_ASSERT(spaceAndClip.isSome());
 
     // Define the clip
-    Maybe<wr::WrClipId> parent = ClipIdAfterOverride(scrollId);
+    spaceAndClip->space = SpatialIdAfterOverride(spaceAndClip->space);
     wr::WrClipId clipId = mBuilder->DefineClip(
-        parent, wr::ToRoundedLayoutRect(clip), &wrRoundedRects);
+        spaceAndClip, wr::ToRoundedLayoutRect(clip), &wrRoundedRects);
     clipIds.AppendElement(clipId);
     cache[chain] = clipId;
     CLIP_LOG("cache[%p] <= %zu\n", chain, clipId.id);
   }
 
   // Now find the parent display item's clipchain id
   Maybe<wr::WrClipChainId> parentChainId;
   if (!mItemClipStack.empty()) {
@@ -381,48 +378,45 @@ ClipManager::~ClipManager() {
   MOZ_ASSERT(!mBuilder);
   MOZ_ASSERT(mCacheStack.empty());
   MOZ_ASSERT(mItemClipStack.empty());
 }
 
 ClipManager::ItemClips::ItemClips(const ActiveScrolledRoot* aASR,
                                   const DisplayItemClipChain* aChain,
                                   bool aSeparateLeaf)
-    : mASR(aASR),
-      mChain(aChain),
-      mSeparateLeaf(aSeparateLeaf),
-      mApplied(false) {}
+    : mASR(aASR), mChain(aChain), mSeparateLeaf(aSeparateLeaf) {
+  mScrollId.id = 0;
+}
 
-void ClipManager::ItemClips::Apply(wr::DisplayListBuilder* aBuilder,
-                                   int32_t aAppUnitsPerDevPixel) {
-  MOZ_ASSERT(!mApplied);
-  mApplied = true;
-
+void ClipManager::ItemClips::UpdateSeparateLeaf(
+    wr::DisplayListBuilder& aBuilder, int32_t aAppUnitsPerDevPixel) {
   Maybe<wr::LayoutRect> clipLeaf;
   if (mSeparateLeaf) {
     MOZ_ASSERT(mChain);
     clipLeaf.emplace(wr::ToRoundedLayoutRect(LayoutDeviceRect::FromAppUnits(
         mChain->mClip.GetClipRect(), aAppUnitsPerDevPixel)));
   }
 
-  aBuilder->PushClipAndScrollInfo(mScrollId.ptrOr(nullptr),
-                                  mClipChainId.ptrOr(nullptr), clipLeaf);
-}
-
-void ClipManager::ItemClips::Unapply(wr::DisplayListBuilder* aBuilder) {
-  if (mApplied) {
-    mApplied = false;
-    aBuilder->PopClipAndScrollInfo(mScrollId.ptrOr(nullptr));
-  }
+  aBuilder.SetClipChainLeaf(clipLeaf);
 }
 
 bool ClipManager::ItemClips::HasSameInputs(const ItemClips& aOther) {
   return mASR == aOther.mASR && mChain == aOther.mChain &&
          mSeparateLeaf == aOther.mSeparateLeaf;
 }
 
 void ClipManager::ItemClips::CopyOutputsFrom(const ItemClips& aOther) {
   mScrollId = aOther.mScrollId;
   mClipChainId = aOther.mClipChainId;
 }
 
+wr::WrSpaceAndClipChain ClipManager::ItemClips::GetSpaceAndClipChain() const {
+  auto spaceAndClipChain = wr::RootScrollNodeWithChain();
+  spaceAndClipChain.space = mScrollId;
+  if (mClipChainId) {
+    spaceAndClipChain.clip_chain = mClipChainId->id;
+  }
+  return spaceAndClipChain;
+}
+
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/wr/ClipManager.h
+++ b/gfx/layers/wr/ClipManager.h
@@ -55,32 +55,32 @@ class ClipManager {
 
   void BeginBuild(WebRenderLayerManager* aManager,
                   wr::DisplayListBuilder& aBuilder);
   void EndBuild();
 
   void BeginList(const StackingContextHelper& aStackingContext);
   void EndList(const StackingContextHelper& aStackingContext);
 
-  void BeginItem(nsDisplayItem* aItem,
-                 const StackingContextHelper& aStackingContext);
+  wr::WrSpaceAndClipChain SwitchItem(
+      nsDisplayItem* aItem, const StackingContextHelper& aStackingContext);
   ~ClipManager();
 
   void PushOverrideForASR(const ActiveScrolledRoot* aASR,
-                          const wr::WrClipId& aClipId);
+                          const wr::WrSpatialId& aSpatialId);
   void PopOverrideForASR(const ActiveScrolledRoot* aASR);
 
  private:
-  Maybe<wr::WrClipId> ClipIdAfterOverride(const Maybe<wr::WrClipId>& aClipId);
+  wr::WrSpatialId SpatialIdAfterOverride(const wr::WrSpatialId& aSpatialId);
 
-  Maybe<wr::WrClipId> GetScrollLayer(const ActiveScrolledRoot* aASR);
+  Maybe<wr::WrSpaceAndClip> GetScrollLayer(const ActiveScrolledRoot* aASR);
 
-  Maybe<wr::WrClipId> DefineScrollLayers(const ActiveScrolledRoot* aASR,
-                                         nsDisplayItem* aItem,
-                                         const StackingContextHelper& aSc);
+  Maybe<wr::WrSpaceAndClip> DefineScrollLayers(
+      const ActiveScrolledRoot* aASR, nsDisplayItem* aItem,
+      const StackingContextHelper& aSc);
 
   Maybe<wr::WrClipChainId> DefineClipChain(const DisplayItemClipChain* aChain,
                                            int32_t aAppUnitsPerDevPixel,
                                            const StackingContextHelper& aSc);
 
   WebRenderLayerManager* MOZ_NON_OWNING_REF mManager;
   wr::DisplayListBuilder* mBuilder;
 
@@ -111,39 +111,37 @@ class ClipManager {
   // differently.
   // Any time ClipManager wants to define a new clip as a child of ASR X, it
   // should first check the cache overrides to see if there is a cache override
   // item ((a) or (b) above) that is already a child of X, and then define that
   // clip as a child of Y instead. This map stores X -> Y, which allows
   // ClipManager to do the necessary lookup. Note that there theoretically might
   // be multiple different "Y" clips (in case of nested cache overrides), which
   // is why we need a stack.
-  std::unordered_map<wr::WrClipId, std::stack<wr::WrClipId>> mASROverride;
+  std::unordered_map<wr::WrSpatialId, std::stack<wr::WrSpatialId>> mASROverride;
 
   // This holds some clip state for a single nsDisplayItem
   struct ItemClips {
     ItemClips(const ActiveScrolledRoot* aASR,
               const DisplayItemClipChain* aChain, bool aSeparateLeaf);
 
     // These are the "inputs" - they come from the nsDisplayItem
     const ActiveScrolledRoot* mASR;
     const DisplayItemClipChain* mChain;
     bool mSeparateLeaf;
 
     // These are the "outputs" - they are pushed to WR as needed
-    Maybe<wr::WrClipId> mScrollId;
+    wr::WrSpatialId mScrollId;
     Maybe<wr::WrClipChainId> mClipChainId;
 
-    // State tracking
-    bool mApplied;
-
-    void Apply(wr::DisplayListBuilder* aBuilder, int32_t aAppUnitsPerDevPixel);
-    void Unapply(wr::DisplayListBuilder* aBuilder);
+    void UpdateSeparateLeaf(wr::DisplayListBuilder& aBuilder,
+                            int32_t aAppUnitsPerDevPixel);
     bool HasSameInputs(const ItemClips& aOther);
     void CopyOutputsFrom(const ItemClips& aOther);
+    wr::WrSpaceAndClipChain GetSpaceAndClipChain() const;
   };
 
   // A stack of ItemClips corresponding to the nsDisplayItem ancestry. Each
   // time we recurse into a nsDisplayItem's child list, this stack size
   // increases by one. The topmost item on the stack is for the display item
   // we are currently processing and items deeper on the stack are for that
   // display item's ancestors.
   std::stack<ItemClips> mItemClipStack;
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -69,16 +69,20 @@ StackingContextHelper::StackingContextHe
 
   mReferenceFrameId = mBuilder->PushStackingContext(
       wr::ToLayoutRect(aBounds), aClipNodeId, aAnimation, aOpacityPtr,
       aTransformPtr,
       aIsPreserve3D ? wr::TransformStyle::Preserve3D : wr::TransformStyle::Flat,
       aPerspectivePtr, wr::ToMixBlendMode(aMixBlendMode), aFilters,
       aBackfaceVisible, rasterSpace);
 
+  if (mReferenceFrameId) {
+    mSpaceAndClipChainHelper.emplace(aBuilder, mReferenceFrameId.ref());
+  }
+
   mAffectsClipPositioning =
       mReferenceFrameId.isSome() || (aBounds.TopLeft() != LayoutDevicePoint());
 
   // If the parent stacking context has a deferred transform item, inherit it
   // into this stacking context, as long as the ASR hasn't changed. Refer to
   // the comments on StackingContextHelper::mDeferredTransformItem for an
   // explanation of what goes in these fields.
   if (aParentSC.mDeferredTransformItem &&
@@ -93,16 +97,17 @@ StackingContextHelper::StackingContextHe
       mDeferredTransformItem = aParentSC.mDeferredTransformItem;
       mDeferredAncestorTransform = aParentSC.mDeferredAncestorTransform;
     }
   }
 }
 
 StackingContextHelper::~StackingContextHelper() {
   if (mBuilder) {
+    mSpaceAndClipChainHelper.reset();
     mBuilder->PopStackingContext(mReferenceFrameId.isSome());
   }
 }
 
 const Maybe<nsDisplayTransform*>&
 StackingContextHelper::GetDeferredTransformItem() const {
   return mDeferredTransformItem;
 }
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -61,32 +61,33 @@ class MOZ_RAII StackingContextHelper {
   const gfx::Matrix& GetSnappingSurfaceTransform() const {
     return mSnappingSurfaceTransform;
   }
 
   const Maybe<nsDisplayTransform*>& GetDeferredTransformItem() const;
   Maybe<gfx::Matrix4x4> GetDeferredTransformMatrix() const;
 
   bool AffectsClipPositioning() const { return mAffectsClipPositioning; }
-  Maybe<wr::WrClipId> ReferenceFrameId() const { return mReferenceFrameId; }
+  Maybe<wr::WrSpatialId> ReferenceFrameId() const { return mReferenceFrameId; }
 
  private:
   wr::DisplayListBuilder* mBuilder;
   gfx::Size mScale;
   gfx::Matrix mInheritedTransform;
 
   // The "snapping surface" defines the space that we want to snap in.
   // You can think of it as the nearest physical surface.
   // Animated transforms create a new snapping surface, so that changes to their
   // transform don't affect the snapping of their contents. Non-animated
   // transforms do *not* create a new snapping surface, so that for example the
   // existence of a non-animated identity transform does not affect snapping.
   gfx::Matrix mSnappingSurfaceTransform;
   bool mAffectsClipPositioning;
-  Maybe<wr::WrClipId> mReferenceFrameId;
+  Maybe<wr::WrSpatialId> mReferenceFrameId;
+  Maybe<wr::SpaceAndClipChainHelper> mSpaceAndClipChainHelper;
 
   // The deferred transform item is used when building the WebRenderScrollData
   // structure. The backstory is that APZ needs to know about transforms that
   // apply to the different APZC instances. Prior to bug 1423370, we would do
   // this by creating a new WebRenderLayerScrollData for each nsDisplayTransform
   // item we encountered. However, this was unnecessarily expensive because it
   // turned out a lot of nsDisplayTransform items didn't have new ASRs defined
   // as descendants, so we'd create the WebRenderLayerScrollData and send it
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -1121,28 +1121,34 @@ void Grouper::ConstructGroups(nsDisplayL
 
   nsDisplayItem* item = aList->GetBottom();
   nsDisplayItem* startOfCurrentGroup = item;
   while (item) {
     if (IsItemProbablyActive(item, mDisplayListBuilder)) {
       currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder,
                              aBuilder, aResources, this, startOfCurrentGroup,
                              item);
-      mClipManager.BeginItem(item, aSc);
-      sIndent++;
-      // Note: this call to CreateWebRenderCommands can recurse back into
-      // this function.
-      RenderRootStateManager* manager =
-          aCommandBuilder->mManager->GetRenderRootStateManager();
-      bool createdWRCommands = item->CreateWebRenderCommands(
-          aBuilder, aResources, aSc, manager, mDisplayListBuilder);
-      sIndent--;
-      MOZ_RELEASE_ASSERT(createdWRCommands,
-                         "active transforms should always succeed at creating "
-                         "WebRender commands");
+
+      {
+        auto spaceAndClipChain = mClipManager.SwitchItem(item, aSc);
+        wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain);
+
+        sIndent++;
+        // Note: this call to CreateWebRenderCommands can recurse back into
+        // this function.
+        RenderRootStateManager* manager =
+            aCommandBuilder->mManager->GetRenderRootStateManager();
+        bool createdWRCommands = item->CreateWebRenderCommands(
+            aBuilder, aResources, aSc, manager, mDisplayListBuilder);
+        sIndent--;
+        MOZ_RELEASE_ASSERT(
+            createdWRCommands,
+            "active transforms should always succeed at creating "
+            "WebRender commands");
+      }
 
       RefPtr<WebRenderGroupData> groupData =
           aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(
               item);
 
       // Initialize groupData->mFollowingGroup
       // TODO: compute the group bounds post-grouping, so that they can be
       // tighter for just the sublist that made it into this group.
@@ -1589,17 +1595,20 @@ void WebRenderCommandBuilder::CreateWebR
       // If we're going to create a new layer data for this item, stash the
       // ASR so that if we recurse into a sublist they will know where to stop
       // walking up their ASR chain when building scroll metadata.
       if (forceNewLayerData) {
         mAsrStack.push_back(asr);
       }
     }
 
-    mClipManager.BeginItem(item, aSc);
+    // This is where we emulate the clip/scroll stack that was previously
+    // implemented on the WR display list side.
+    auto spaceAndClipChain = mClipManager.SwitchItem(item, aSc);
+    wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain);
 
     {  // scope restoreDoGrouping
       AutoRestore<bool> restoreDoGrouping(mDoGrouping);
       if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) {
         // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper
         // display items (like animated transforms / opacity) share the same
         // animated geometry root, so we can combine subsequent items of that
         // type into the same image.
@@ -1700,19 +1709,19 @@ void WebRenderCommandBuilder::CreateWebR
       }
     }
   }
 
   mDumpIndent--;
   mClipManager.EndList(aSc);
 }
 
-void WebRenderCommandBuilder::PushOverrideForASR(const ActiveScrolledRoot* aASR,
-                                                 const wr::WrClipId& aClipId) {
-  mClipManager.PushOverrideForASR(aASR, aClipId);
+void WebRenderCommandBuilder::PushOverrideForASR(
+    const ActiveScrolledRoot* aASR, const wr::WrSpatialId& aSpatialId) {
+  mClipManager.PushOverrideForASR(aASR, aSpatialId);
 }
 
 void WebRenderCommandBuilder::PopOverrideForASR(
     const ActiveScrolledRoot* aASR) {
   mClipManager.PopOverrideForASR(aASR);
 }
 
 Maybe<wr::ImageKey> WebRenderCommandBuilder::CreateImageKey(
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -55,17 +55,17 @@ class WebRenderCommandBuilder {
                               wr::IpcResourceUpdateQueue& aResourceUpdates,
                               nsDisplayList* aDisplayList,
                               nsDisplayListBuilder* aDisplayListBuilder,
                               WebRenderScrollData& aScrollData,
                               wr::LayoutSize& aContentSize,
                               const nsTArray<wr::WrFilterOp>& aFilters);
 
   void PushOverrideForASR(const ActiveScrolledRoot* aASR,
-                          const wr::WrClipId& aClipId);
+                          const wr::WrSpatialId& aSpatialId);
   void PopOverrideForASR(const ActiveScrolledRoot* aASR);
 
   Maybe<wr::ImageKey> CreateImageKey(
       nsDisplayItem* aItem, ImageContainer* aContainer,
       mozilla::wr::DisplayListBuilder& aBuilder,
       mozilla::wr::IpcResourceUpdateQueue& aResources,
       mozilla::wr::ImageRendering aRendering, const StackingContextHelper& aSc,
       gfx::IntSize& aSize, const Maybe<LayoutDeviceRect>& aAsyncImageBounds);
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -12,22 +12,20 @@
 #include "mozilla/gfx/gfxVars.h"
 #include "mozilla/layers/CompositorThread.h"
 #include "mozilla/webrender/RenderCompositor.h"
 #include "mozilla/widget/CompositorWidget.h"
 #include "mozilla/layers/SynchronousTask.h"
 #include "TextDrawTarget.h"
 
 #define WRDL_LOG(...)
-/*
-#define WRDL_LOG(...) printf_stderr("WRDL(%p): " __VA_ARGS__)
-#define WRDL_LOG(...) if (XRE_IsContentProcess()) {              \
-                        printf_stderr("WRDL(%p): " __VA_ARGS__); \
-                      }
-*/
+//#define WRDL_LOG(...) printf_stderr("WRDL(%p): " __VA_ARGS__)
+//#define WRDL_LOG(...) if (XRE_IsContentProcess()) {              \
+//                        printf_stderr("WRDL(%p): " __VA_ARGS__); \
+//                      }
 
 namespace mozilla {
 namespace wr {
 
 using layers::Stringify;
 
 MOZ_DEFINE_MALLOC_SIZE_OF(WebRenderMallocSizeOf)
 MOZ_DEFINE_MALLOC_ENCLOSING_SIZE_OF(WebRenderMallocEnclosingSizeOf)
@@ -641,17 +639,18 @@ void WebRenderAPI::SetFrameStartTime(con
 void WebRenderAPI::RunOnRenderThread(UniquePtr<RendererEvent> aEvent) {
   auto event = reinterpret_cast<uintptr_t>(aEvent.release());
   wr_api_send_external_event(mDocHandle, event);
 }
 
 DisplayListBuilder::DisplayListBuilder(PipelineId aId,
                                        const wr::LayoutSize& aContentSize,
                                        size_t aCapacity)
-    : mActiveFixedPosTracker(nullptr) {
+    : mCurrentSpaceAndClipChain(wr::RootScrollNodeWithChain()),
+      mActiveFixedPosTracker(nullptr) {
   MOZ_COUNT_CTOR(DisplayListBuilder);
   mWrState = wr_state_new(aId, aContentSize, aCapacity);
 }
 
 DisplayListBuilder::~DisplayListBuilder() {
   MOZ_COUNT_DTOR(DisplayListBuilder);
   wr_state_delete(mWrState);
 }
@@ -667,17 +666,17 @@ usize DisplayListBuilder::Dump(usize aIn
 }
 
 void DisplayListBuilder::Finalize(wr::LayoutSize& aOutContentSize,
                                   BuiltDisplayList& aOutDisplayList) {
   wr_api_finalize_builder(mWrState, &aOutContentSize, &aOutDisplayList.dl_desc,
                           &aOutDisplayList.dl.inner);
 }
 
-Maybe<wr::WrClipId> DisplayListBuilder::PushStackingContext(
+Maybe<wr::WrSpatialId> DisplayListBuilder::PushStackingContext(
     const wr::LayoutRect& aBounds, const wr::WrClipId* aClipNodeId,
     const WrAnimationProperty* aAnimation, const float* aOpacity,
     const gfx::Matrix4x4* aTransform, wr::TransformStyle aTransformStyle,
     const gfx::Matrix4x4* aPerspective, const wr::MixBlendMode& aMixBlendMode,
     const nsTArray<wr::WrFilterOp>& aFilters, bool aIsBackfaceVisible,
     const wr::RasterSpace& aRasterSpace) {
   MOZ_ASSERT(mClipChainLeaf.isNothing(),
              "Non-empty leaf from clip chain given, but not used with SC!");
@@ -693,25 +692,23 @@ Maybe<wr::WrClipId> DisplayListBuilder::
   }
 
   const wr::LayoutTransform* maybePerspective =
       aPerspective ? &perspective : nullptr;
   WRDL_LOG("PushStackingContext b=%s t=%s\n", mWrState,
            Stringify(aBounds).c_str(),
            aTransform ? Stringify(*aTransform).c_str() : "none");
 
-  bool outIsReferenceFrame = false;
-  uintptr_t outReferenceFrameId = 0;
-  wr_dp_push_stacking_context(
-      mWrState, aBounds, aClipNodeId, aAnimation, aOpacity, maybeTransform,
-      aTransformStyle, maybePerspective, aMixBlendMode, aFilters.Elements(),
-      aFilters.Length(), aIsBackfaceVisible, aRasterSpace, &outIsReferenceFrame,
-      &outReferenceFrameId);
-  return outIsReferenceFrame ? Some(wr::WrClipId{outReferenceFrameId})
-                             : Nothing();
+  auto spatialId = wr_dp_push_stacking_context(
+      mWrState, aBounds, mCurrentSpaceAndClipChain.space, aClipNodeId,
+      aAnimation, aOpacity, maybeTransform, aTransformStyle, maybePerspective,
+      aMixBlendMode, aFilters.Elements(), aFilters.Length(), aIsBackfaceVisible,
+      aRasterSpace);
+
+  return spatialId.id != 0 ? Some(spatialId) : Nothing();
 }
 
 void DisplayListBuilder::PopStackingContext(bool aIsReferenceFrame) {
   WRDL_LOG("PopStackingContext\n", mWrState);
   wr_dp_pop_stacking_context(mWrState, aIsReferenceFrame);
 }
 
 wr::WrClipChainId DisplayListBuilder::DefineClipChain(
@@ -722,158 +719,176 @@ wr::WrClipChainId DisplayListBuilder::De
                              aClips.Elements(), aClips.Length());
   WRDL_LOG("DefineClipChain id=%" PRIu64 " p=%s clips=%zu\n", mWrState,
            clipchainId, aParent ? Stringify(aParent->id).c_str() : "(nil)",
            aClips.Length());
   return wr::WrClipChainId{clipchainId};
 }
 
 wr::WrClipId DisplayListBuilder::DefineClip(
-    const Maybe<wr::WrClipId>& aParentId, const wr::LayoutRect& aClipRect,
+    const Maybe<wr::WrSpaceAndClip>& aParent, const wr::LayoutRect& aClipRect,
     const nsTArray<wr::ComplexClipRegion>* aComplex,
     const wr::WrImageMask* aMask) {
-  size_t clip_id =
-      wr_dp_define_clip(mWrState, aParentId.ptrOr(nullptr), aClipRect,
-                        aComplex ? aComplex->Elements() : nullptr,
-                        aComplex ? aComplex->Length() : 0, aMask);
+  WrClipId clipId;
+  if (aParent) {
+    clipId = wr_dp_define_clip_with_parent_clip(
+        mWrState, aParent.ptr(), aClipRect,
+        aComplex ? aComplex->Elements() : nullptr,
+        aComplex ? aComplex->Length() : 0, aMask);
+  } else {
+    clipId = wr_dp_define_clip_with_parent_clip_chain(
+        mWrState, &mCurrentSpaceAndClipChain, aClipRect,
+        aComplex ? aComplex->Elements() : nullptr,
+        aComplex ? aComplex->Length() : 0, aMask);
+  }
+
   WRDL_LOG("DefineClip id=%zu p=%s r=%s m=%p b=%s complex=%zu\n", mWrState,
-           clip_id, aParentId ? Stringify(aParentId->id).c_str() : "(nil)",
+           clipId.id, aParent ? Stringify(aParent->clip.id).c_str() : "(nil)",
            Stringify(aClipRect).c_str(), aMask,
            aMask ? Stringify(aMask->rect).c_str() : "none",
            aComplex ? aComplex->Length() : 0);
-  return wr::WrClipId{clip_id};
+
+  return clipId;
 }
 
-void DisplayListBuilder::PushClip(const wr::WrClipId& aClipId) {
-  WRDL_LOG("PushClip id=%zu\n", mWrState, aClipId.id);
-  wr_dp_push_clip(mWrState, aClipId);
-}
-
-void DisplayListBuilder::PopClip() {
-  WRDL_LOG("PopClip\n", mWrState);
-  wr_dp_pop_clip(mWrState);
-}
-
-wr::WrClipId DisplayListBuilder::DefineStickyFrame(
+wr::WrSpatialId DisplayListBuilder::DefineStickyFrame(
     const wr::LayoutRect& aContentRect, const float* aTopMargin,
     const float* aRightMargin, const float* aBottomMargin,
     const float* aLeftMargin, const StickyOffsetBounds& aVerticalBounds,
     const StickyOffsetBounds& aHorizontalBounds,
     const wr::LayoutVector2D& aAppliedOffset) {
-  size_t id = wr_dp_define_sticky_frame(
-      mWrState, aContentRect, aTopMargin, aRightMargin, aBottomMargin,
-      aLeftMargin, aVerticalBounds, aHorizontalBounds, aAppliedOffset);
+  auto spatialId = wr_dp_define_sticky_frame(
+      mWrState, mCurrentSpaceAndClipChain.space, aContentRect, aTopMargin,
+      aRightMargin, aBottomMargin, aLeftMargin, aVerticalBounds,
+      aHorizontalBounds, aAppliedOffset);
+
   WRDL_LOG("DefineSticky id=%zu c=%s t=%s r=%s b=%s l=%s v=%s h=%s a=%s\n",
-           mWrState, id, Stringify(aContentRect).c_str(),
+           mWrState, spatialId.id, Stringify(aContentRect).c_str(),
            aTopMargin ? Stringify(*aTopMargin).c_str() : "none",
            aRightMargin ? Stringify(*aRightMargin).c_str() : "none",
            aBottomMargin ? Stringify(*aBottomMargin).c_str() : "none",
            aLeftMargin ? Stringify(*aLeftMargin).c_str() : "none",
            Stringify(aVerticalBounds).c_str(),
            Stringify(aHorizontalBounds).c_str(),
            Stringify(aAppliedOffset).c_str());
-  return wr::WrClipId{id};
+
+  return spatialId;
 }
 
-Maybe<wr::WrClipId> DisplayListBuilder::GetScrollIdForDefinedScrollLayer(
+Maybe<wr::WrSpaceAndClip> DisplayListBuilder::GetScrollIdForDefinedScrollLayer(
     layers::ScrollableLayerGuid::ViewID aViewId) const {
   if (aViewId == layers::ScrollableLayerGuid::NULL_SCROLL_ID) {
     return Some(wr::RootScrollNode());
   }
 
   auto it = mScrollIds.find(aViewId);
   if (it == mScrollIds.end()) {
     return Nothing();
   }
 
   return Some(it->second);
 }
 
-wr::WrClipId DisplayListBuilder::DefineScrollLayer(
+wr::WrSpaceAndClip DisplayListBuilder::DefineScrollLayer(
     const layers::ScrollableLayerGuid::ViewID& aViewId,
-    const Maybe<wr::WrClipId>& aParentId, const wr::LayoutRect& aContentRect,
-    const wr::LayoutRect& aClipRect) {
+    const Maybe<wr::WrSpaceAndClip>& aParent,
+    const wr::LayoutRect& aContentRect, const wr::LayoutRect& aClipRect) {
   auto it = mScrollIds.find(aViewId);
   if (it != mScrollIds.end()) {
     return it->second;
   }
 
+  auto rootSpaceAndClip = wr::RootScrollNode();
   // We haven't defined aViewId before, so let's define it now.
-  size_t numericScrollId = wr_dp_define_scroll_layer(
-      mWrState, aViewId, aParentId.ptrOr(nullptr), aContentRect, aClipRect);
+  auto spaceAndClip = wr_dp_define_scroll_layer(
+      mWrState, aViewId, aParent ? aParent.ptr() : &rootSpaceAndClip,
+      aContentRect, aClipRect);
 
   WRDL_LOG("DefineScrollLayer id=%" PRIu64 "/%zu p=%s co=%s cl=%s\n", mWrState,
-           aViewId, numericScrollId,
-           aParentId ? Stringify(aParentId->id).c_str() : "(nil)",
+           aViewId, spaceAndClip.space.id,
+           aParent ? Stringify(aParent->space.id).c_str() : "(nil)",
            Stringify(aContentRect).c_str(), Stringify(aClipRect).c_str());
 
-  auto clipId = wr::WrClipId{numericScrollId};
-  mScrollIds[aViewId] = clipId;
-  return clipId;
-}
-
-void DisplayListBuilder::PushClipAndScrollInfo(
-    const wr::WrClipId* aScrollId, const wr::WrClipChainId* aClipChainId,
-    const Maybe<wr::LayoutRect>& aClipChainLeaf) {
-  if (aScrollId) {
-    WRDL_LOG("PushClipAndScroll s=%zu c=%s\n", mWrState, aScrollId->id,
-             aClipChainId ? Stringify(aClipChainId->id).c_str() : "none");
-    wr_dp_push_clip_and_scroll_info(
-        mWrState, *aScrollId, aClipChainId ? &(aClipChainId->id) : nullptr);
-  }
-  mClipChainLeaf = aClipChainLeaf;
-}
-
-void DisplayListBuilder::PopClipAndScrollInfo(const wr::WrClipId* aScrollId) {
-  if (aScrollId) {
-    WRDL_LOG("PopClipAndScroll\n", mWrState);
-    wr_dp_pop_clip_and_scroll_info(mWrState);
-  }
-  mClipChainLeaf.reset();
+  mScrollIds[aViewId] = spaceAndClip;
+  return spaceAndClip;
 }
 
 void DisplayListBuilder::PushRect(const wr::LayoutRect& aBounds,
                                   const wr::LayoutRect& aClip,
                                   bool aIsBackfaceVisible,
                                   const wr::ColorF& aColor) {
   wr::LayoutRect clip = MergeClipLeaf(aClip);
   WRDL_LOG("PushRect b=%s cl=%s c=%s\n", mWrState, Stringify(aBounds).c_str(),
            Stringify(clip).c_str(), Stringify(aColor).c_str());
-  wr_dp_push_rect(mWrState, aBounds, clip, aIsBackfaceVisible, aColor);
+  wr_dp_push_rect(mWrState, aBounds, clip, aIsBackfaceVisible,
+                  &mCurrentSpaceAndClipChain, aColor);
+}
+
+void DisplayListBuilder::PushRoundedRect(const wr::LayoutRect& aBounds,
+                                         const wr::LayoutRect& aClip,
+                                         bool aIsBackfaceVisible,
+                                         const wr::ColorF& aColor) {
+  wr::LayoutRect clip = MergeClipLeaf(aClip);
+  WRDL_LOG("PushRoundedRect b=%s cl=%s c=%s\n", mWrState,
+           Stringify(aBounds).c_str(), Stringify(clip).c_str(),
+           Stringify(aColor).c_str());
+
+  AutoTArray<wr::ComplexClipRegion, 1> clips;
+  clips.AppendElement(wr::SimpleRadii(aBounds, aBounds.size.width / 2));
+  // TODO: use `mCurrentSpaceAndClipChain.clip_chain` as a parent?
+  auto clipId = DefineClip(Nothing(), aBounds, &clips, nullptr);
+  auto spaceAndClip = WrSpaceAndClip{mCurrentSpaceAndClipChain.space, clipId};
+
+  wr_dp_push_rect_with_parent_clip(mWrState, aBounds, clip, aIsBackfaceVisible,
+                                   &spaceAndClip, aColor);
 }
 
 void DisplayListBuilder::PushClearRect(const wr::LayoutRect& aBounds) {
   wr::LayoutRect clip = MergeClipLeaf(aBounds);
   WRDL_LOG("PushClearRect b=%s c=%s\n", mWrState, Stringify(aBounds).c_str(),
            Stringify(clip).c_str());
-  wr_dp_push_clear_rect(mWrState, aBounds, clip);
+  wr_dp_push_clear_rect(mWrState, aBounds, clip, &mCurrentSpaceAndClipChain);
+}
+
+void DisplayListBuilder::PushClearRectWithComplexRegion(
+    const wr::LayoutRect& aBounds, const wr::ComplexClipRegion& aRegion) {
+  wr::LayoutRect clip = MergeClipLeaf(aBounds);
+  WRDL_LOG("PushClearRectWithComplexRegion b=%s c=%s\n", mWrState,
+           Stringify(aBounds).c_str(), Stringify(clip).c_str());
+
+  AutoTArray<wr::ComplexClipRegion, 1> clips;
+  auto clipId = DefineClip(Nothing(), aBounds, &clips, nullptr);
+  auto spaceAndClip = WrSpaceAndClip{mCurrentSpaceAndClipChain.space, clipId};
+
+  wr_dp_push_clear_rect_with_parent_clip(mWrState, aBounds, clip,
+                                         &spaceAndClip);
 }
 
 void DisplayListBuilder::PushLinearGradient(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, const wr::LayoutPoint& aStartPoint,
     const wr::LayoutPoint& aEndPoint, const nsTArray<wr::GradientStop>& aStops,
     wr::ExtendMode aExtendMode, const wr::LayoutSize aTileSize,
     const wr::LayoutSize aTileSpacing) {
-  wr_dp_push_linear_gradient(mWrState, aBounds, MergeClipLeaf(aClip),
-                             aIsBackfaceVisible, aStartPoint, aEndPoint,
-                             aStops.Elements(), aStops.Length(), aExtendMode,
-                             aTileSize, aTileSpacing);
+  wr_dp_push_linear_gradient(
+      mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+      &mCurrentSpaceAndClipChain, aStartPoint, aEndPoint, aStops.Elements(),
+      aStops.Length(), aExtendMode, aTileSize, aTileSpacing);
 }
 
 void DisplayListBuilder::PushRadialGradient(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, const wr::LayoutPoint& aCenter,
     const wr::LayoutSize& aRadius, const nsTArray<wr::GradientStop>& aStops,
     wr::ExtendMode aExtendMode, const wr::LayoutSize aTileSize,
     const wr::LayoutSize aTileSpacing) {
-  wr_dp_push_radial_gradient(mWrState, aBounds, MergeClipLeaf(aClip),
-                             aIsBackfaceVisible, aCenter, aRadius,
-                             aStops.Elements(), aStops.Length(), aExtendMode,
-                             aTileSize, aTileSpacing);
+  wr_dp_push_radial_gradient(
+      mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+      &mCurrentSpaceAndClipChain, aCenter, aRadius, aStops.Elements(),
+      aStops.Length(), aExtendMode, aTileSize, aTileSpacing);
 }
 
 void DisplayListBuilder::PushImage(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, wr::ImageRendering aFilter, wr::ImageKey aImage,
     bool aPremultipliedAlpha, const wr::ColorF& aColor) {
   wr::LayoutSize size;
   size.width = aBounds.size.width;
@@ -886,156 +901,163 @@ void DisplayListBuilder::PushImage(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, const wr::LayoutSize& aStretchSize,
     const wr::LayoutSize& aTileSpacing, wr::ImageRendering aFilter,
     wr::ImageKey aImage, bool aPremultipliedAlpha, const wr::ColorF& aColor) {
   wr::LayoutRect clip = MergeClipLeaf(aClip);
   WRDL_LOG("PushImage b=%s cl=%s s=%s t=%s\n", mWrState,
            Stringify(aBounds).c_str(), Stringify(clip).c_str(),
            Stringify(aStretchSize).c_str(), Stringify(aTileSpacing).c_str());
-  wr_dp_push_image(mWrState, aBounds, clip, aIsBackfaceVisible, aStretchSize,
-                   aTileSpacing, aFilter, aImage, aPremultipliedAlpha, aColor);
+  wr_dp_push_image(mWrState, aBounds, clip, aIsBackfaceVisible,
+                   &mCurrentSpaceAndClipChain, aStretchSize, aTileSpacing,
+                   aFilter, aImage, aPremultipliedAlpha, aColor);
 }
 
 void DisplayListBuilder::PushYCbCrPlanarImage(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
     wr::ImageKey aImageChannel1, wr::ImageKey aImageChannel2,
     wr::WrColorDepth aColorDepth, wr::WrYuvColorSpace aColorSpace,
     wr::ImageRendering aRendering) {
   wr_dp_push_yuv_planar_image(mWrState, aBounds, MergeClipLeaf(aClip),
-                              aIsBackfaceVisible, aImageChannel0,
-                              aImageChannel1, aImageChannel2, aColorDepth,
-                              aColorSpace, aRendering);
+                              aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
+                              aImageChannel0, aImageChannel1, aImageChannel2,
+                              aColorDepth, aColorSpace, aRendering);
 }
 
 void DisplayListBuilder::PushNV12Image(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
     wr::ImageKey aImageChannel1, wr::WrColorDepth aColorDepth,
     wr::WrYuvColorSpace aColorSpace, wr::ImageRendering aRendering) {
   wr_dp_push_yuv_NV12_image(mWrState, aBounds, MergeClipLeaf(aClip),
-                            aIsBackfaceVisible, aImageChannel0, aImageChannel1,
-                            aColorDepth, aColorSpace, aRendering);
+                            aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
+                            aImageChannel0, aImageChannel1, aColorDepth,
+                            aColorSpace, aRendering);
 }
 
 void DisplayListBuilder::PushYCbCrInterleavedImage(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, wr::ImageKey aImageChannel0,
     wr::WrColorDepth aColorDepth, wr::WrYuvColorSpace aColorSpace,
     wr::ImageRendering aRendering) {
   wr_dp_push_yuv_interleaved_image(mWrState, aBounds, MergeClipLeaf(aClip),
-                                   aIsBackfaceVisible, aImageChannel0,
+                                   aIsBackfaceVisible,
+                                   &mCurrentSpaceAndClipChain, aImageChannel0,
                                    aColorDepth, aColorSpace, aRendering);
 }
 
 void DisplayListBuilder::PushIFrame(const wr::LayoutRect& aBounds,
                                     bool aIsBackfaceVisible,
                                     PipelineId aPipeline,
                                     bool aIgnoreMissingPipeline) {
   wr_dp_push_iframe(mWrState, aBounds, MergeClipLeaf(aBounds),
-                    aIsBackfaceVisible, aPipeline, aIgnoreMissingPipeline);
+                    aIsBackfaceVisible, &mCurrentSpaceAndClipChain, aPipeline,
+                    aIgnoreMissingPipeline);
 }
 
 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,
                                     wr::AntialiasBorder aAntialias) {
   MOZ_ASSERT(aSides.length() == 4);
   if (aSides.length() != 4) {
     return;
   }
   wr_dp_push_border(mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
-                    aAntialias, aWidths, aSides[0], aSides[1], aSides[2],
-                    aSides[3], aRadius);
+                    &mCurrentSpaceAndClipChain, 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, const int32_t aWidth, const int32_t aHeight,
     const wr::SideOffsets2D<int32_t>& aSlice,
     const wr::SideOffsets2D<float>& aOutset,
     const wr::RepeatMode& aRepeatHorizontal,
     const wr::RepeatMode& aRepeatVertical) {
   wr_dp_push_border_image(mWrState, aBounds, MergeClipLeaf(aClip),
-                          aIsBackfaceVisible, aWidths, aImage, aWidth, aHeight,
-                          aSlice, aOutset, aRepeatHorizontal, aRepeatVertical);
+                          aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
+                          aWidths, aImage, aWidth, aHeight, aSlice, aOutset,
+                          aRepeatHorizontal, aRepeatVertical);
 }
 
 void DisplayListBuilder::PushBorderGradient(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths,
     const int32_t aWidth, const int32_t aHeight,
     const wr::SideOffsets2D<int32_t>& aSlice,
     const wr::LayoutPoint& aStartPoint, const wr::LayoutPoint& aEndPoint,
     const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
     const wr::SideOffsets2D<float>& aOutset) {
-  wr_dp_push_border_gradient(mWrState, aBounds, MergeClipLeaf(aClip),
-                             aIsBackfaceVisible, aWidths, aWidth, aHeight,
-                             aSlice, aStartPoint, aEndPoint, aStops.Elements(),
-                             aStops.Length(), aExtendMode, aOutset);
+  wr_dp_push_border_gradient(
+      mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+      &mCurrentSpaceAndClipChain, aWidths, aWidth, aHeight, aSlice, aStartPoint,
+      aEndPoint, aStops.Elements(), aStops.Length(), aExtendMode, aOutset);
 }
 
 void DisplayListBuilder::PushBorderRadialGradient(
     const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, const wr::LayoutSideOffsets& aWidths,
     const wr::LayoutPoint& aCenter, const wr::LayoutSize& aRadius,
     const nsTArray<wr::GradientStop>& aStops, wr::ExtendMode aExtendMode,
     const wr::SideOffsets2D<float>& aOutset) {
-  wr_dp_push_border_radial_gradient(mWrState, aBounds, MergeClipLeaf(aClip),
-                                    aIsBackfaceVisible, aWidths, aCenter,
-                                    aRadius, aStops.Elements(), aStops.Length(),
-                                    aExtendMode, aOutset);
+  wr_dp_push_border_radial_gradient(
+      mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
+      &mCurrentSpaceAndClipChain, aWidths, aCenter, aRadius, aStops.Elements(),
+      aStops.Length(), aExtendMode, aOutset);
 }
 
 void DisplayListBuilder::PushText(const wr::LayoutRect& aBounds,
                                   const wr::LayoutRect& aClip,
                                   bool aIsBackfaceVisible,
                                   const wr::ColorF& aColor,
                                   wr::FontInstanceKey aFontKey,
                                   Range<const wr::GlyphInstance> aGlyphBuffer,
                                   const wr::GlyphOptions* aGlyphOptions) {
   wr_dp_push_text(mWrState, aBounds, MergeClipLeaf(aClip), aIsBackfaceVisible,
-                  aColor, aFontKey, &aGlyphBuffer[0], aGlyphBuffer.length(),
-                  aGlyphOptions);
+                  &mCurrentSpaceAndClipChain, aColor, aFontKey,
+                  &aGlyphBuffer[0], aGlyphBuffer.length(), aGlyphOptions);
 }
 
 void DisplayListBuilder::PushLine(const wr::LayoutRect& aClip,
                                   bool aIsBackfaceVisible,
                                   const wr::Line& aLine) {
   wr::LayoutRect clip = MergeClipLeaf(aClip);
-  wr_dp_push_line(mWrState, &clip, aIsBackfaceVisible, &aLine.bounds,
+  wr_dp_push_line(mWrState, &clip, aIsBackfaceVisible,
+                  &mCurrentSpaceAndClipChain, &aLine.bounds,
                   aLine.wavyLineThickness, aLine.orientation, &aLine.color,
                   aLine.style);
 }
 
 void DisplayListBuilder::PushShadow(const wr::LayoutRect& aRect,
                                     const wr::LayoutRect& aClip,
                                     bool aIsBackfaceVisible,
                                     const wr::Shadow& aShadow) {
   wr_dp_push_shadow(mWrState, aRect, MergeClipLeaf(aClip), aIsBackfaceVisible,
-                    aShadow);
+                    &mCurrentSpaceAndClipChain, aShadow);
 }
 
 void DisplayListBuilder::PopAllShadows() { wr_dp_pop_all_shadows(mWrState); }
 
 void DisplayListBuilder::PushBoxShadow(
     const wr::LayoutRect& aRect, const wr::LayoutRect& aClip,
     bool aIsBackfaceVisible, const wr::LayoutRect& aBoxBounds,
     const wr::LayoutVector2D& aOffset, const wr::ColorF& aColor,
     const float& aBlurRadius, const float& aSpreadRadius,
     const wr::BorderRadius& aBorderRadius,
     const wr::BoxShadowClipMode& aClipMode) {
   wr_dp_push_box_shadow(mWrState, aRect, MergeClipLeaf(aClip),
-                        aIsBackfaceVisible, aBoxBounds, aOffset, aColor,
-                        aBlurRadius, aSpreadRadius, aBorderRadius, aClipMode);
+                        aIsBackfaceVisible, &mCurrentSpaceAndClipChain,
+                        aBoxBounds, aOffset, aColor, aBlurRadius, aSpreadRadius,
+                        aBorderRadius, aClipMode);
 }
 
 Maybe<layers::ScrollableLayerGuid::ViewID>
 DisplayListBuilder::GetContainingFixedPosScrollTarget(
     const ActiveScrolledRoot* aAsr) {
   return mActiveFixedPosTracker
              ? mActiveFixedPosTracker->GetScrollTargetForASR(aAsr)
              : Nothing();
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -323,64 +323,62 @@ class DisplayListBuilder {
   void Restore();
   void ClearSave();
   usize Dump(usize aIndent, const Maybe<usize>& aStart,
              const Maybe<usize>& aEnd);
 
   void Finalize(wr::LayoutSize& aOutContentSize,
                 wr::BuiltDisplayList& aOutDisplayList);
 
-  Maybe<wr::WrClipId> PushStackingContext(
+  Maybe<wr::WrSpatialId> PushStackingContext(
       const wr::LayoutRect&
           aBounds,  // TODO: We should work with strongly typed rects
       const wr::WrClipId* aClipNodeId,
       const wr::WrAnimationProperty* aAnimation, const float* aOpacity,
       const gfx::Matrix4x4* aTransform, wr::TransformStyle aTransformStyle,
       const gfx::Matrix4x4* aPerspective, const wr::MixBlendMode& aMixBlendMode,
       const nsTArray<wr::WrFilterOp>& aFilters, bool aIsBackfaceVisible,
       const wr::RasterSpace& aRasterSpace);
   void PopStackingContext(bool aIsReferenceFrame);
 
   wr::WrClipChainId DefineClipChain(const Maybe<wr::WrClipChainId>& aParent,
                                     const nsTArray<wr::WrClipId>& aClips);
 
   wr::WrClipId DefineClip(
-      const Maybe<wr::WrClipId>& aParentId, const wr::LayoutRect& aClipRect,
+      const Maybe<wr::WrSpaceAndClip>& aParent, const wr::LayoutRect& aClipRect,
       const nsTArray<wr::ComplexClipRegion>* aComplex = nullptr,
       const wr::WrImageMask* aMask = nullptr);
-  void PushClip(const wr::WrClipId& aClipId);
-  void PopClip();
 
-  wr::WrClipId DefineStickyFrame(const wr::LayoutRect& aContentRect,
-                                 const float* aTopMargin,
-                                 const float* aRightMargin,
-                                 const float* aBottomMargin,
-                                 const float* aLeftMargin,
-                                 const StickyOffsetBounds& aVerticalBounds,
-                                 const StickyOffsetBounds& aHorizontalBounds,
-                                 const wr::LayoutVector2D& aAppliedOffset);
+  wr::WrSpatialId DefineStickyFrame(const wr::LayoutRect& aContentRect,
+                                    const float* aTopMargin,
+                                    const float* aRightMargin,
+                                    const float* aBottomMargin,
+                                    const float* aLeftMargin,
+                                    const StickyOffsetBounds& aVerticalBounds,
+                                    const StickyOffsetBounds& aHorizontalBounds,
+                                    const wr::LayoutVector2D& aAppliedOffset);
 
-  Maybe<wr::WrClipId> GetScrollIdForDefinedScrollLayer(
+  Maybe<wr::WrSpaceAndClip> GetScrollIdForDefinedScrollLayer(
       layers::ScrollableLayerGuid::ViewID aViewId) const;
-  wr::WrClipId DefineScrollLayer(
+  wr::WrSpaceAndClip DefineScrollLayer(
       const layers::ScrollableLayerGuid::ViewID& aViewId,
-      const Maybe<wr::WrClipId>& aParentId,
+      const Maybe<wr::WrSpaceAndClip>& aParent,
       const wr::LayoutRect&
           aContentRect,  // TODO: We should work with strongly typed rects
       const wr::LayoutRect& aClipRect);
 
-  void PushClipAndScrollInfo(const wr::WrClipId* aScrollId,
-                             const wr::WrClipChainId* aClipChainId,
-                             const Maybe<wr::LayoutRect>& aClipChainLeaf);
-  void PopClipAndScrollInfo(const wr::WrClipId* aScrollId);
-
   void PushRect(const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
                 bool aIsBackfaceVisible, const wr::ColorF& aColor);
+  void PushRoundedRect(const wr::LayoutRect& aBounds,
+                       const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
+                       const wr::ColorF& aColor);
 
   void PushClearRect(const wr::LayoutRect& aBounds);
+  void PushClearRectWithComplexRegion(const wr::LayoutRect& aBounds,
+                                      const wr::ComplexClipRegion& aRegion);
 
   void PushLinearGradient(const wr::LayoutRect& aBounds,
                           const wr::LayoutRect& aClip, bool aIsBackfaceVisible,
                           const wr::LayoutPoint& aStartPoint,
                           const wr::LayoutPoint& aEndPoint,
                           const nsTArray<wr::GradientStop>& aStops,
                           wr::ExtendMode aExtendMode,
                           const wr::LayoutSize aTileSize,
@@ -505,16 +503,20 @@ class DisplayListBuilder {
       wr::IpcResourceUpdateQueue& aResources,
       const layers::StackingContextHelper& aSc,
       layers::RenderRootStateManager* aManager, nsDisplayItem* aItem,
       nsRect& aBounds, const gfx::Point& aDeviceOffset);
 
   // Try to avoid using this when possible.
   wr::WrState* Raw() { return mWrState; }
 
+  void SetClipChainLeaf(const Maybe<wr::LayoutRect>& aClipRect) {
+    mClipChainLeaf = aClipRect;
+  }
+
   // A chain of RAII objects, each holding a (ASR, ViewID) tuple of data. The
   // topmost object is pointed to by the mActiveFixedPosTracker pointer in
   // the wr::DisplayListBuilder.
   class MOZ_RAII FixedPosScrollTargetTracker {
    public:
     FixedPosScrollTargetTracker(DisplayListBuilder& aBuilder,
                                 const ActiveScrolledRoot* aAsr,
                                 layers::ScrollableLayerGuid::ViewID aScrollId);
@@ -537,30 +539,66 @@ class DisplayListBuilder {
     return aClip;
   }
 
   wr::WrState* mWrState;
 
   // Track each scroll id that we encountered. We use this structure to
   // ensure that we don't define a particular scroll layer multiple times,
   // as that results in undefined behaviour in WR.
-  std::unordered_map<layers::ScrollableLayerGuid::ViewID, wr::WrClipId>
+  std::unordered_map<layers::ScrollableLayerGuid::ViewID, wr::WrSpaceAndClip>
       mScrollIds;
 
+  wr::WrSpaceAndClipChain mCurrentSpaceAndClipChain;
+
   // Contains the current leaf of the clip chain to be merged with the
   // display item's clip rect when pushing an item. May be set to Nothing() if
   // there is no clip rect to merge with.
   Maybe<wr::LayoutRect> mClipChainLeaf;
 
   RefPtr<layout::TextDrawTarget> mCachedTextDT;
   RefPtr<gfxContext> mCachedContext;
 
   FixedPosScrollTargetTracker* mActiveFixedPosTracker;
 
   friend class WebRenderAPI;
+  friend class SpaceAndClipChainHelper;
+};
+
+// This is a RAII class that overrides the current Wr's SpatialId and
+// ClipChainId.
+class MOZ_RAII SpaceAndClipChainHelper {
+ public:
+  SpaceAndClipChainHelper(DisplayListBuilder& aBuilder,
+                          wr::WrSpaceAndClipChain aSpaceAndClipChain
+                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mBuilder(aBuilder),
+        mOldSpaceAndClipChain(aBuilder.mCurrentSpaceAndClipChain) {
+    aBuilder.mCurrentSpaceAndClipChain = aSpaceAndClipChain;
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  }
+  SpaceAndClipChainHelper(DisplayListBuilder& aBuilder,
+                          wr::WrSpatialId aSpatialId
+                              MOZ_GUARD_OBJECT_NOTIFIER_PARAM)
+      : mBuilder(aBuilder),
+        mOldSpaceAndClipChain(aBuilder.mCurrentSpaceAndClipChain) {
+    aBuilder.mCurrentSpaceAndClipChain.space = aSpatialId;
+    MOZ_GUARD_OBJECT_NOTIFIER_INIT;
+  }
+
+  ~SpaceAndClipChainHelper() {
+    mBuilder.mCurrentSpaceAndClipChain = mOldSpaceAndClipChain;
+  }
+
+ private:
+  SpaceAndClipChainHelper(const SpaceAndClipChainHelper&);
+
+  DisplayListBuilder& mBuilder;
+  wr::WrSpaceAndClipChain mOldSpaceAndClipChain;
+  MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
 };
 
 Maybe<wr::ImageFormat> SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat);
 
 }  // namespace wr
 }  // namespace mozilla
 
 #endif
--- a/gfx/webrender_bindings/WebRenderTypes.cpp
+++ b/gfx/webrender_bindings/WebRenderTypes.cpp
@@ -23,14 +23,24 @@ void Assign_WrVecU8(wr::WrVecU8& aVec, m
   aVec.data = aOther.mData;
   aVec.length = aOther.mLen;
   aVec.capacity = aOther.mCapacity;
   aOther.mData = nullptr;
   aOther.mLen = 0;
   aOther.mCapacity = 0;
 }
 
-/*static*/ WrClipId RootScrollNode() {
-  return WrClipId{wr_root_scroll_node_id()};
+WrSpaceAndClip RootScrollNode() {
+  WrSpaceAndClip sac;
+  sac.clip = wr_root_clip_id();
+  sac.space = wr_root_scroll_node_id();
+  return sac;
+}
+
+WrSpaceAndClipChain RootScrollNodeWithChain() {
+  WrSpaceAndClipChain sacc;
+  sacc.clip_chain = wr::ROOT_CLIP_CHAIN;
+  sacc.space = wr_root_scroll_node_id();
+  return sacc;
 }
 
 }  // namespace wr
 }  // namespace mozilla
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -793,26 +793,33 @@ static inline wr::WrFilterOpType ToWrFil
       return wr::WrFilterOpType::Sepia;
     case NS_STYLE_FILTER_DROP_SHADOW:
       return wr::WrFilterOpType::DropShadow;
   }
   MOZ_ASSERT_UNREACHABLE("Tried to convert unknown filter type.");
   return wr::WrFilterOpType::Grayscale;
 }
 
-extern WrClipId RootScrollNode();
 
 // Corresponds to a clip id for a clip chain in webrender. Similar to
 // WrClipId but a separate struct so we don't get them mixed up in C++.
 struct WrClipChainId {
   uint64_t id;
 
   bool operator==(const WrClipChainId& other) const { return id == other.id; }
+
+  static WrClipChainId Empty() {
+    WrClipChainId id = {0};
+    return id;
+  }
 };
 
+WrSpaceAndClip RootScrollNode();
+WrSpaceAndClipChain RootScrollNodeWithChain();
+
 enum class WebRenderError : int8_t {
   INITIALIZE = 0,
   MAKE_CURRENT,
   RENDER,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
@@ -852,16 +859,16 @@ static inline wr::SyntheticItalics Degre
   return synthetic_italics;
 }
 
 }  // namespace wr
 }  // namespace mozilla
 
 namespace std {
 template <>
-struct hash<mozilla::wr::WrClipId> {
-  std::size_t operator()(mozilla::wr::WrClipId const& aKey) const noexcept {
+struct hash<mozilla::wr::WrSpatialId> {
+  std::size_t operator()(mozilla::wr::WrSpatialId const& aKey) const noexcept {
     return std::hash<size_t>{}(aKey.id);
   }
 };
 }  // namespace std
 
 #endif /* GFX_WEBRENDERTYPES_H */
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -39,16 +39,19 @@ use core_graphics::font::CGFont;
 extern "C" {
     #[cfg(target_os = "android")]
     fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int;
 }
 
 /// The unique id for WR resource identification.
 static NEXT_NAMESPACE_ID: AtomicUsize = AtomicUsize::new(1);
 
+/// Special value handled in this wrapper layer to signify a redundant clip chain.
+pub const ROOT_CLIP_CHAIN: u64 = !0;
+
 fn next_namespace_id() -> IdNamespace {
     IdNamespace(NEXT_NAMESPACE_ID.fetch_add(1, Ordering::Relaxed) as u32)
 }
 
 /// Whether a border should be antialiased.
 #[repr(C)]
 #[derive(Eq, PartialEq, Copy, Clone)]
 pub enum AntialiasBorder {
@@ -109,16 +112,60 @@ type WrImageKey = ImageKey;
 pub type WrFontKey = FontKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 pub type WrFontInstanceKey = FontInstanceKey;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrYuvColorSpace = YuvColorSpace;
 /// cbindgen:field-names=[mNamespace, mHandle]
 type WrColorDepth = ColorDepth;
 
+
+#[repr(C)]
+pub struct WrSpaceAndClip {
+    space: WrSpatialId,
+    clip: WrClipId,
+}
+
+impl WrSpaceAndClip {
+    fn from_webrender(sac: SpaceAndClipInfo) -> Self {
+        WrSpaceAndClip {
+            space: WrSpatialId { id: sac.spatial_id.0 },
+            clip: WrClipId::from_webrender(sac.clip_id),
+        }
+    }
+
+    fn to_webrender(&self, pipeline_id: WrPipelineId) -> SpaceAndClipInfo {
+        SpaceAndClipInfo {
+            spatial_id: self.space.to_webrender(pipeline_id),
+            clip_id: self.clip.to_webrender(pipeline_id),
+        }
+    }
+}
+
+#[repr(C)]
+pub struct WrSpaceAndClipChain {
+    space: WrSpatialId,
+    clip_chain: u64,
+}
+
+impl WrSpaceAndClipChain {
+    fn to_webrender(&self, pipeline_id: WrPipelineId) -> SpaceAndClipInfo {
+        //Warning: special case here to support dummy clip chain
+        SpaceAndClipInfo {
+            spatial_id: self.space.to_webrender(pipeline_id),
+            clip_id: if self.clip_chain == ROOT_CLIP_CHAIN {
+                ClipId::root(pipeline_id)
+            } else {
+                ClipId::ClipChain(ClipChainId(self.clip_chain, pipeline_id))
+            },
+        }
+    }
+}
+
+
 fn make_slice<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
     if ptr.is_null() {
         &[]
     } else {
         unsafe { slice::from_raw_parts(ptr, len) }
     }
 }
 
@@ -1854,29 +1901,29 @@ pub extern "C" fn wr_dp_restore(state: &
 #[no_mangle]
 pub extern "C" fn wr_dp_clear_save(state: &mut WrState) {
     state.frame_builder.dl_builder.clear_save();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_stacking_context(state: &mut WrState,
                                               mut bounds: LayoutRect,
+                                              spatial_id: WrSpatialId,
                                               clip_node_id: *const WrClipId,
                                               animation: *const WrAnimationProperty,
                                               opacity: *const f32,
                                               transform: *const LayoutTransform,
                                               transform_style: TransformStyle,
                                               perspective: *const LayoutTransform,
                                               mix_blend_mode: MixBlendMode,
                                               filters: *const WrFilterOp,
                                               filter_count: usize,
                                               is_backface_visible: bool,
                                               glyph_raster_space: RasterSpace,
-                                              out_is_reference_frame: &mut bool,
-                                              out_reference_frame_id: &mut usize) {
+                                              ) -> WrSpatialId {
     debug_assert!(unsafe { !is_in_render_thread() });
 
     let c_filters = make_slice(filters, filter_count);
     let mut filters : Vec<FilterOp> = c_filters.iter().map(|c_filter| {
         match c_filter.filter_type {
             WrFilterOpType::Blur => FilterOp::Blur(c_filter.argument),
             WrFilterOpType::Brightness => FilterOp::Brightness(c_filter.argument),
             WrFilterOpType::Contrast => FilterOp::Contrast(c_filter.argument),
@@ -1891,20 +1938,16 @@ pub extern "C" fn wr_dp_push_stacking_co
                                                                c_filter.color),
             WrFilterOpType::ColorMatrix => FilterOp::ColorMatrix(c_filter.matrix),
             WrFilterOpType::SrgbToLinear => FilterOp::SrgbToLinear,
             WrFilterOpType::LinearToSrgb => FilterOp::LinearToSrgb,
         }
     }).collect();
 
     let clip_node_id_ref = unsafe { clip_node_id.as_ref() };
-    let clip_node_id = match clip_node_id_ref {
-        Some(clip_node_id) => Some(unpack_clip_id(*clip_node_id, state.pipeline_id)),
-        None => None,
-    };
 
     let transform_ref = unsafe { transform.as_ref() };
     let mut transform_binding = match transform_ref {
         Some(transform) => Some(PropertyBinding::Value(transform.clone())),
         None => None,
     };
 
     let opacity_ref = unsafe { opacity.as_ref() };
@@ -1939,250 +1982,290 @@ pub extern "C" fn wr_dp_push_stacking_co
     }
 
     let perspective_ref = unsafe { perspective.as_ref() };
     let perspective = match perspective_ref {
         Some(perspective) => Some(perspective.clone()),
         None => None,
     };
 
-    *out_is_reference_frame = transform_binding.is_some() || perspective.is_some();
-    if *out_is_reference_frame {
-        let ref_frame_id = state.frame_builder
-            .dl_builder
-            .push_reference_frame(&bounds, transform_style, transform_binding, perspective);
-        *out_reference_frame_id = pack_clip_id(ref_frame_id);
+    let mut wr_spatial_id = spatial_id.to_webrender(state.pipeline_id);
+    let wr_clip_id = clip_node_id_ref.map(|id| id.to_webrender(state.pipeline_id));
+
+    let is_reference_frame = transform_binding.is_some() || perspective.is_some();
+    // Note: 0 has special meaning in WR land, standing for ROOT_REFERENCE_FRAME.
+    // However, it is never returned by `push_reference_frame`, and we need to return
+    // an option here across FFI, so we take that 0 value for the None semantics.
+    // This is resolved into proper `Maybe<WrSpatialId>` inside `WebRenderAPI::PushStackingContext`.
+    let mut result = WrSpatialId { id: 0 };
+    if is_reference_frame {
+        wr_spatial_id = state.frame_builder.dl_builder.push_reference_frame(
+            &bounds,
+            wr_spatial_id,
+            transform_style,
+            transform_binding,
+            perspective,
+        );
 
         bounds.origin = LayoutPoint::zero();
-        state.frame_builder.dl_builder.push_clip_id(ref_frame_id);
+        result.id = wr_spatial_id.0;
+        assert_ne!(wr_spatial_id.0, 0);
     }
 
     let prim_info = LayoutPrimitiveInfo {
         is_backface_visible,
         tag: state.current_tag,
         .. LayoutPrimitiveInfo::new(bounds)
     };
 
     state.frame_builder
          .dl_builder
          .push_stacking_context(&prim_info,
-                                clip_node_id,
+                                wr_spatial_id,
+                                wr_clip_id,
                                 transform_style,
                                 mix_blend_mode,
                                 &filters,
                                 glyph_raster_space);
+
+    result
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState,
                                              is_reference_frame: bool) {
     debug_assert!(unsafe { !is_in_render_thread() });
     state.frame_builder.dl_builder.pop_stacking_context();
     if is_reference_frame {
-        state.frame_builder.dl_builder.pop_clip_id();
         state.frame_builder.dl_builder.pop_reference_frame();
     }
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_define_clipchain(state: &mut WrState,
                                          parent_clipchain_id: *const u64,
                                          clips: *const WrClipId,
                                          clips_count: usize)
                                          -> u64 {
     debug_assert!(unsafe { is_in_main_thread() });
-    let parent = unsafe { parent_clipchain_id.as_ref() }.map(|id| ClipChainId(*id, state.pipeline_id));
+    let parent = unsafe { parent_clipchain_id.as_ref() }
+        .map(|id| ClipChainId(*id, state.pipeline_id));
+
     let pipeline_id = state.pipeline_id;
     let clips = make_slice(clips, clips_count)
         .iter()
-        .map(|id| unpack_clip_id(*id, pipeline_id));
+        .map(|clip_id| clip_id.to_webrender(pipeline_id));
 
     let clipchain_id = state.frame_builder.dl_builder.define_clip_chain(parent, clips);
     assert!(clipchain_id.1 == state.pipeline_id);
     clipchain_id.0
 }
 
 #[no_mangle]
-pub extern "C" fn wr_dp_define_clip(state: &mut WrState,
-                                    parent_id: *const WrClipId,
-                                    clip_rect: LayoutRect,
-                                    complex: *const ComplexClipRegion,
-                                    complex_count: usize,
-                                    mask: *const WrImageMask)
-                                    -> usize {
-    debug_assert!(unsafe { is_in_main_thread() });
-
-    let parent_id = unsafe { parent_id.as_ref() };
-    let complex_slice = make_slice(complex, complex_count);
-    let complex_iter = complex_slice.iter().cloned();
-    let mask : Option<ImageMask> = unsafe { mask.as_ref() }.map(|x| x.into());
-
-    let clip_id = if let Some(&pid) = parent_id {
-        state.frame_builder.dl_builder.define_clip_with_parent(
-            unpack_clip_id(pid, state.pipeline_id),
-            clip_rect, complex_iter, mask)
-    } else {
-        state.frame_builder.dl_builder.define_clip(clip_rect, complex_iter, mask)
-    };
-    pack_clip_id(clip_id)
+pub extern "C" fn wr_dp_define_clip_with_parent_clip(
+    state: &mut WrState,
+    parent: &WrSpaceAndClip,
+    clip_rect: LayoutRect,
+    complex: *const ComplexClipRegion,
+    complex_count: usize,
+    mask: *const WrImageMask,
+) -> WrClipId {
+    wr_dp_define_clip_impl(
+        &mut state.frame_builder,
+        parent.to_webrender(state.pipeline_id),
+        clip_rect,
+        make_slice(complex, complex_count),
+        unsafe { mask.as_ref() }.map(|m| m.into()),
+    )
 }
 
 #[no_mangle]
-pub extern "C" fn wr_dp_push_clip(state: &mut WrState,
-                                  clip_id: WrClipId) {
-    debug_assert!(unsafe { is_in_main_thread() });
-    state.frame_builder.dl_builder.push_clip_id(unpack_clip_id(clip_id, state.pipeline_id));
+pub extern "C" fn wr_dp_define_clip_with_parent_clip_chain(
+    state: &mut WrState,
+    parent: &WrSpaceAndClipChain,
+    clip_rect: LayoutRect,
+    complex: *const ComplexClipRegion,
+    complex_count: usize,
+    mask: *const WrImageMask,
+) -> WrClipId {
+    wr_dp_define_clip_impl(
+        &mut state.frame_builder,
+        parent.to_webrender(state.pipeline_id),
+        clip_rect,
+        make_slice(complex, complex_count),
+        unsafe { mask.as_ref() }.map(|m| m.into()),
+    )
 }
 
-#[no_mangle]
-pub extern "C" fn wr_dp_pop_clip(state: &mut WrState) {
-    debug_assert!(unsafe { !is_in_render_thread() });
-    state.frame_builder.dl_builder.pop_clip_id();
+fn wr_dp_define_clip_impl(
+    frame_builder: &mut WebRenderFrameBuilder,
+    parent: SpaceAndClipInfo,
+    clip_rect: LayoutRect,
+    complex_regions: &[ComplexClipRegion],
+    mask: Option<ImageMask>,
+) -> WrClipId {
+    debug_assert!(unsafe { is_in_main_thread() });
+    let clip_id = frame_builder.dl_builder.define_clip(
+        &parent,
+        clip_rect,
+        complex_regions.iter().cloned(),
+        mask,
+    );
+    WrClipId::from_webrender(clip_id)
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_define_sticky_frame(state: &mut WrState,
+                                            parent_spatial_id: WrSpatialId,
                                             content_rect: LayoutRect,
                                             top_margin: *const f32,
                                             right_margin: *const f32,
                                             bottom_margin: *const f32,
                                             left_margin: *const f32,
                                             vertical_bounds: StickyOffsetBounds,
                                             horizontal_bounds: StickyOffsetBounds,
                                             applied_offset: LayoutVector2D)
-                                            -> usize {
+                                            -> WrSpatialId {
     assert!(unsafe { is_in_main_thread() });
-    let clip_id = state.frame_builder.dl_builder.define_sticky_frame(
-        content_rect, SideOffsets2D::new(
+    let spatial_id = state.frame_builder.dl_builder.define_sticky_frame(
+        parent_spatial_id.to_webrender(state.pipeline_id),
+        content_rect,
+        SideOffsets2D::new(
             unsafe { top_margin.as_ref() }.cloned(),
             unsafe { right_margin.as_ref() }.cloned(),
             unsafe { bottom_margin.as_ref() }.cloned(),
             unsafe { left_margin.as_ref() }.cloned()
         ),
-        vertical_bounds, horizontal_bounds, applied_offset);
-    pack_clip_id(clip_id)
+        vertical_bounds,
+        horizontal_bounds,
+        applied_offset,
+    );
+
+    WrSpatialId { id: spatial_id.0 }
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_define_scroll_layer(state: &mut WrState,
-                                            scroll_id: u64,
-                                            parent_id: *const WrClipId,
+                                            external_scroll_id: u64,
+                                            parent: &WrSpaceAndClip,
                                             content_rect: LayoutRect,
                                             clip_rect: LayoutRect)
-                                            -> usize {
+                                            -> WrSpaceAndClip {
     assert!(unsafe { is_in_main_thread() });
 
-    let parent_id = unsafe { parent_id.as_ref() };
-
-    let new_id = if let Some(&pid) = parent_id {
-        state.frame_builder.dl_builder.define_scroll_frame_with_parent(
-            unpack_clip_id(pid, state.pipeline_id),
-            Some(ExternalScrollId(scroll_id, state.pipeline_id)),
-            content_rect,
-            clip_rect,
-            vec![],
-            None,
-            ScrollSensitivity::Script
-        )
-    } else {
-        state.frame_builder.dl_builder.define_scroll_frame(
-            Some(ExternalScrollId(scroll_id, state.pipeline_id)),
-            content_rect,
-            clip_rect,
-            vec![],
-            None,
-            ScrollSensitivity::Script
-        )
-    };
-
-    pack_clip_id(new_id)
-}
-
-#[no_mangle]
-pub extern "C" fn wr_dp_push_scroll_layer(state: &mut WrState,
-                                          scroll_id: WrClipId) {
-    debug_assert!(unsafe { is_in_main_thread() });
-    let clip_id = unpack_clip_id(scroll_id, state.pipeline_id);
-    state.frame_builder.dl_builder.push_clip_id(clip_id);
-}
-
-#[no_mangle]
-pub extern "C" fn wr_dp_pop_scroll_layer(state: &mut WrState) {
-    debug_assert!(unsafe { is_in_main_thread() });
-    state.frame_builder.dl_builder.pop_clip_id();
-}
-
-#[no_mangle]
-pub extern "C" fn wr_dp_push_clip_and_scroll_info(state: &mut WrState,
-                                                  scroll_id: WrClipId,
-                                                  clip_chain_id: *const u64) {
-    debug_assert!(unsafe { is_in_main_thread() });
-
-    let clip_chain_id = unsafe { clip_chain_id.as_ref() };
-    let info = if let Some(&ccid) = clip_chain_id {
-        ClipAndScrollInfo::new(
-            unpack_clip_id(scroll_id, state.pipeline_id),
-            ClipId::ClipChain(ClipChainId(ccid, state.pipeline_id)))
-    } else {
-        ClipAndScrollInfo::simple(unpack_clip_id(scroll_id, state.pipeline_id))
-    };
-    state.frame_builder.dl_builder.push_clip_and_scroll_info(info);
-}
-
-#[no_mangle]
-pub extern "C" fn wr_dp_pop_clip_and_scroll_info(state: &mut WrState) {
-    debug_assert!(unsafe { is_in_main_thread() });
-    state.frame_builder.dl_builder.pop_clip_id();
+    let space_and_clip = state.frame_builder.dl_builder.define_scroll_frame(
+        &parent.to_webrender(state.pipeline_id),
+        Some(ExternalScrollId(external_scroll_id, state.pipeline_id)),
+        content_rect,
+        clip_rect,
+        vec![],
+        None,
+        ScrollSensitivity::Script
+    );
+
+    WrSpaceAndClip::from_webrender(space_and_clip)
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_iframe(state: &mut WrState,
                                     rect: LayoutRect,
                                     clip: LayoutRect,
                                     is_backface_visible: bool,
+                                    parent: &WrSpaceAndClipChain,
                                     pipeline_id: WrPipelineId,
                                     ignore_missing_pipeline: bool) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     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_iframe(&prim_info, pipeline_id, ignore_missing_pipeline);
+    state.frame_builder.dl_builder.push_iframe(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        pipeline_id,
+        ignore_missing_pipeline,
+    );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_rect(state: &mut WrState,
                                   rect: LayoutRect,
                                   clip: LayoutRect,
                                   is_backface_visible: bool,
+                                  parent: &WrSpaceAndClipChain,
                                   color: ColorF) {
     debug_assert!(unsafe { !is_in_render_thread() });
 
     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_rect(&prim_info,
-                                             color);
+    state.frame_builder.dl_builder.push_rect(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        color,
+    );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_rect_with_parent_clip(
+    state: &mut WrState,
+    rect: LayoutRect,
+    clip: LayoutRect,
+    is_backface_visible: bool,
+    parent: &WrSpaceAndClip,
+    color: ColorF,
+) {
+    debug_assert!(unsafe { !is_in_render_thread() });
+
+    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_rect(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        color,
+    );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_clear_rect(state: &mut WrState,
                                         rect: LayoutRect,
-                                        clip: LayoutRect) {
+                                        clip: LayoutRect,
+                                        parent: &WrSpaceAndClipChain) {
     debug_assert!(unsafe { !is_in_render_thread() });
 
     let prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
-    state.frame_builder.dl_builder.push_clear_rect(&prim_info);
+    state.frame_builder.dl_builder.push_clear_rect(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+    );
+}
+
+#[no_mangle]
+pub extern "C" fn wr_dp_push_clear_rect_with_parent_clip(
+    state: &mut WrState,
+    rect: LayoutRect,
+    clip: LayoutRect,
+    parent: &WrSpaceAndClip,
+) {
+    debug_assert!(unsafe { !is_in_render_thread() });
+
+    let prim_info = LayoutPrimitiveInfo::with_clip_rect(rect, clip.into());
+    state.frame_builder.dl_builder.push_clear_rect(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+    );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_image(state: &mut WrState,
                                    bounds: LayoutRect,
                                    clip: LayoutRect,
                                    is_backface_visible: bool,
+                                   parent: &WrSpaceAndClipChain,
                                    stretch_size: LayoutSize,
                                    tile_spacing: LayoutSize,
                                    image_rendering: ImageRendering,
                                    key: WrImageKey,
                                    premultiplied_alpha: bool,
                                    color: ColorF) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
@@ -2192,175 +2275,192 @@ pub extern "C" fn wr_dp_push_image(state
     let alpha_type = if premultiplied_alpha {
         AlphaType::PremultipliedAlpha
     } else {
         AlphaType::Alpha
     };
     state.frame_builder
          .dl_builder
          .push_image(&prim_info,
+                     &parent.to_webrender(state.pipeline_id),
                      stretch_size,
                      tile_spacing,
                      image_rendering,
                      alpha_type,
                      key,
                      color);
 }
 
 /// Push a 3 planar yuv image.
 #[no_mangle]
 pub extern "C" fn wr_dp_push_yuv_planar_image(state: &mut WrState,
                                               bounds: LayoutRect,
                                               clip: LayoutRect,
                                               is_backface_visible: bool,
+                                              parent: &WrSpaceAndClipChain,
                                               image_key_0: WrImageKey,
                                               image_key_1: WrImageKey,
                                               image_key_2: WrImageKey,
                                               color_depth: WrColorDepth,
                                               color_space: WrYuvColorSpace,
                                               image_rendering: ImageRendering) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
     prim_info.tag = state.current_tag;
     state.frame_builder
          .dl_builder
          .push_yuv_image(&prim_info,
+                         &parent.to_webrender(state.pipeline_id),
                          YuvData::PlanarYCbCr(image_key_0, image_key_1, image_key_2),
                          color_depth,
                          color_space,
                          image_rendering);
 }
 
 /// Push a 2 planar NV12 image.
 #[no_mangle]
 pub extern "C" fn wr_dp_push_yuv_NV12_image(state: &mut WrState,
                                             bounds: LayoutRect,
                                             clip: LayoutRect,
                                             is_backface_visible: bool,
+                                            parent: &WrSpaceAndClipChain,
                                             image_key_0: WrImageKey,
                                             image_key_1: WrImageKey,
                                             color_depth: WrColorDepth,
                                             color_space: WrYuvColorSpace,
                                             image_rendering: ImageRendering) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
     prim_info.tag = state.current_tag;
     state.frame_builder
          .dl_builder
          .push_yuv_image(&prim_info,
+                         &parent.to_webrender(state.pipeline_id),
                          YuvData::NV12(image_key_0, image_key_1),
                          color_depth,
                          color_space,
                          image_rendering);
 }
 
 /// Push a yuv interleaved image.
 #[no_mangle]
 pub extern "C" fn wr_dp_push_yuv_interleaved_image(state: &mut WrState,
                                                    bounds: LayoutRect,
                                                    clip: LayoutRect,
                                                    is_backface_visible: bool,
+                                                   parent: &WrSpaceAndClipChain,
                                                    image_key_0: WrImageKey,
                                                    color_depth: WrColorDepth,
                                                    color_space: WrYuvColorSpace,
                                                    image_rendering: ImageRendering) {
     debug_assert!(unsafe { is_in_main_thread() || is_in_compositor_thread() });
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
     prim_info.tag = state.current_tag;
     state.frame_builder
          .dl_builder
          .push_yuv_image(&prim_info,
+                         &parent.to_webrender(state.pipeline_id),
                          YuvData::InterleavedYCbCr(image_key_0),
                          color_depth,
                          color_space,
                          image_rendering);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_text(state: &mut WrState,
                                   bounds: LayoutRect,
                                   clip: LayoutRect,
                                   is_backface_visible: bool,
+                                  parent: &WrSpaceAndClipChain,
                                   color: ColorF,
                                   font_key: WrFontInstanceKey,
                                   glyphs: *const GlyphInstance,
                                   glyph_count: u32,
                                   glyph_options: *const GlyphOptions) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     let glyph_slice = make_slice(glyphs, glyph_count as usize);
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
     prim_info.tag = state.current_tag;
     state.frame_builder
          .dl_builder
          .push_text(&prim_info,
+                    &parent.to_webrender(state.pipeline_id),
                     &glyph_slice,
                     font_key,
                     color,
                     unsafe { glyph_options.as_ref().cloned() });
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_shadow(state: &mut WrState,
                                     bounds: LayoutRect,
                                     clip: LayoutRect,
                                     is_backface_visible: bool,
+                                    parent: &WrSpaceAndClipChain,
                                     shadow: Shadow) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(bounds, clip.into());
     prim_info.is_backface_visible = is_backface_visible;
     prim_info.tag = state.current_tag;
-    state.frame_builder.dl_builder.push_shadow(&prim_info, shadow.into());
+    state.frame_builder.dl_builder.push_shadow(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        shadow.into(),
+    );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_all_shadows(state: &mut WrState) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     state.frame_builder.dl_builder.pop_all_shadows();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_line(state: &mut WrState,
                                   clip: &LayoutRect,
                                   is_backface_visible: bool,
+                                  parent: &WrSpaceAndClipChain,
                                   bounds: &LayoutRect,
                                   wavy_line_thickness: f32,
                                   orientation: LineOrientation,
                                   color: &ColorF,
                                   style: LineStyle) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     let mut prim_info = LayoutPrimitiveInfo::with_clip_rect(*bounds, (*clip).into());
     prim_info.is_backface_visible = is_backface_visible;
     prim_info.tag = state.current_tag;
     state.frame_builder
          .dl_builder
          .push_line(&prim_info,
+                    &parent.to_webrender(state.pipeline_id),
                     wavy_line_thickness,
                     orientation,
                     color,
                     style);
 
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border(state: &mut WrState,
                                     rect: LayoutRect,
                                     clip: LayoutRect,
                                     is_backface_visible: bool,
+                                    parent: &WrSpaceAndClipChain,
                                     do_aa: AntialiasBorder,
                                     widths: LayoutSideOffsets,
                                     top: BorderSide,
                                     right: BorderSide,
                                     bottom: BorderSide,
                                     left: BorderSide,
                                     radius: BorderRadius) {
     debug_assert!(unsafe { is_in_main_thread() });
@@ -2375,25 +2475,27 @@ pub extern "C" fn wr_dp_push_border(stat
     });
 
     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,
+                      &parent.to_webrender(state.pipeline_id),
                       widths,
                       border_details);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border_image(state: &mut WrState,
                                           rect: LayoutRect,
                                           clip: LayoutRect,
                                           is_backface_visible: bool,
+                                          parent: &WrSpaceAndClipChain,
                                           widths: LayoutSideOffsets,
                                           image: WrImageKey,
                                           width: i32,
                                           height: i32,
                                           slice: SideOffsets2D<i32>,
                                           outset: SideOffsets2D<f32>,
                                           repeat_horizontal: RepeatMode,
                                           repeat_vertical: RepeatMode) {
@@ -2406,25 +2508,30 @@ pub extern "C" fn wr_dp_push_border_imag
         fill: false,
         outset: outset.into(),
         repeat_horizontal: repeat_horizontal.into(),
         repeat_vertical: repeat_vertical.into(),
     });
     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.into(), border_details);
+    state.frame_builder.dl_builder.push_border(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        widths.into(),
+        border_details,
+    );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border_gradient(state: &mut WrState,
                                              rect: LayoutRect,
                                              clip: LayoutRect,
                                              is_backface_visible: bool,
+                                             parent: &WrSpaceAndClipChain,
                                              widths: LayoutSideOffsets,
                                              width: i32,
                                              height: i32,
                                              slice: SideOffsets2D<i32>,
                                              start_point: LayoutPoint,
                                              end_point: LayoutPoint,
                                              stops: *const GradientStop,
                                              stops_count: usize,
@@ -2451,26 +2558,30 @@ pub extern "C" fn wr_dp_push_border_grad
         outset: outset.into(),
         repeat_horizontal: RepeatMode::Stretch,
         repeat_vertical: RepeatMode::Stretch,
     });
 
     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.into(), border_details);
+    state.frame_builder.dl_builder.push_border(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        widths.into(),
+        border_details,
+    );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_border_radial_gradient(state: &mut WrState,
                                                     rect: LayoutRect,
                                                     clip: LayoutRect,
                                                     is_backface_visible: bool,
+                                                    parent: &WrSpaceAndClipChain,
                                                     widths: LayoutSideOffsets,
                                                     center: LayoutPoint,
                                                     radius: LayoutSize,
                                                     stops: *const GradientStop,
                                                     stops_count: usize,
                                                     extend_mode: ExtendMode,
                                                     outset: SideOffsets2D<f32>) {
     debug_assert!(unsafe { is_in_main_thread() });
@@ -2500,26 +2611,30 @@ pub extern "C" fn wr_dp_push_border_radi
         fill: false,
         outset: outset.into(),
         repeat_horizontal: RepeatMode::Stretch,
         repeat_vertical: RepeatMode::Stretch,
     });
     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.into(), border_details);
+    state.frame_builder.dl_builder.push_border(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        widths.into(),
+        border_details,
+    );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_linear_gradient(state: &mut WrState,
                                              rect: LayoutRect,
                                              clip: LayoutRect,
                                              is_backface_visible: bool,
+                                             parent: &WrSpaceAndClipChain,
                                              start_point: LayoutPoint,
                                              end_point: LayoutPoint,
                                              stops: *const GradientStop,
                                              stops_count: usize,
                                              extend_mode: ExtendMode,
                                              tile_size: LayoutSize,
                                              tile_spacing: LayoutSize) {
     debug_assert!(unsafe { is_in_main_thread() });
@@ -2531,29 +2646,31 @@ pub extern "C" fn wr_dp_push_linear_grad
                         .dl_builder
                         .create_gradient(start_point.into(),
                                          end_point.into(),
                                          stops_vector,
                                          extend_mode.into());
     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_gradient(&prim_info,
-                        gradient,
-                        tile_size.into(),
-                        tile_spacing.into());
+    state.frame_builder.dl_builder.push_gradient(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        gradient,
+        tile_size.into(),
+        tile_spacing.into(),
+    );
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_radial_gradient(state: &mut WrState,
                                              rect: LayoutRect,
                                              clip: LayoutRect,
                                              is_backface_visible: bool,
+                                             parent: &WrSpaceAndClipChain,
                                              center: LayoutPoint,
                                              radius: LayoutSize,
                                              stops: *const GradientStop,
                                              stops_count: usize,
                                              extend_mode: ExtendMode,
                                              tile_size: LayoutSize,
                                              tile_spacing: LayoutSize) {
     debug_assert!(unsafe { is_in_main_thread() });
@@ -2565,44 +2682,46 @@ pub extern "C" fn wr_dp_push_radial_grad
                         .dl_builder
                         .create_radial_gradient(center.into(),
                                                 radius.into(),
                                                 stops_vector,
                                                 extend_mode.into());
     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_radial_gradient(&prim_info,
-                               gradient,
-                               tile_size,
-                               tile_spacing);
+    state.frame_builder.dl_builder.push_radial_gradient(
+        &prim_info,
+        &parent.to_webrender(state.pipeline_id),
+        gradient,
+        tile_size,
+        tile_spacing);
 }
 
 #[no_mangle]
 pub extern "C" fn wr_dp_push_box_shadow(state: &mut WrState,
                                         rect: LayoutRect,
                                         clip: LayoutRect,
                                         is_backface_visible: bool,
+                                        parent: &WrSpaceAndClipChain,
                                         box_bounds: LayoutRect,
                                         offset: LayoutVector2D,
                                         color: ColorF,
                                         blur_radius: f32,
                                         spread_radius: f32,
                                         border_radius: BorderRadius,
                                         clip_mode: BoxShadowClipMode) {
     debug_assert!(unsafe { is_in_main_thread() });
 
     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_box_shadow(&prim_info,
+                          &parent.to_webrender(state.pipeline_id),
                           box_bounds,
                           offset,
                           color,
                           blur_radius,
                           spread_radius,
                           border_radius,
                           clip_mode);
 }
@@ -2709,47 +2828,58 @@ extern "C" {
                                tile_size: Option<&u16>,
                                tile_offset: Option<&TileOffset>,
                                dirty_rect: Option<&LayoutIntRect>,
                                output: MutByteSlice)
                                -> bool;
 }
 
 #[no_mangle]
-pub extern "C" fn wr_root_scroll_node_id() -> usize {
+pub extern "C" fn wr_root_scroll_node_id() -> WrSpatialId {
+    // The PipelineId doesn't matter here, since we just want the numeric part of the id
+    // produced for any given root reference frame.
+    WrSpatialId { id: SpatialId::root_scroll_node(PipelineId(0, 0)).0 }
+}
+
+#[no_mangle]
+pub extern "C" fn wr_root_clip_id() -> WrClipId {
     // The PipelineId doesn't matter here, since we just want the numeric part of the id
     // produced for any given root reference frame.
-    pack_clip_id(ClipId::root_scroll_node(PipelineId(0, 0)))
+    WrClipId::from_webrender(ClipId::root(PipelineId(0, 0)))
+ }
+
+
+#[repr(C)]
+#[derive(Clone, Copy)]
+pub struct WrClipId {
+    id: usize,
 }
 
-fn pack_clip_id(id: ClipId) -> usize {
-    let (id, type_value) = match id {
-        ClipId::Spatial(id, _) => (id, 0),
-        ClipId::Clip(id, _) => (id, 1),
-        ClipId::ClipChain(..) => unreachable!("Tried to pack a clip chain id"),
-    };
-
-    assert!(id <= usize::max_value() >> 1);
-    return (id << 1) + type_value;
+impl WrClipId {
+    fn to_webrender(&self, pipeline_id: WrPipelineId) -> ClipId {
+        ClipId::Clip(self.id, pipeline_id)
+    }
+
+    fn from_webrender(clip_id: ClipId) -> Self {
+        match clip_id {
+            ClipId::Clip(id, _) => WrClipId { id },
+            ClipId::ClipChain(_) => panic!("Unexpected clip chain"),
+        }
+    }
 }
 
 #[repr(C)]
 #[derive(Clone, Copy)]
-pub struct WrClipId {
-    id: usize
+pub struct WrSpatialId {
+    id: usize,
 }
 
-fn unpack_clip_id(id: WrClipId, pipeline_id: PipelineId) -> ClipId {
-    let type_value = id.id & 0b01;
-    let id = id.id >> 1;
-
-    match type_value {
-        0 => ClipId::Spatial(id, pipeline_id),
-        1 => ClipId::Clip(id, pipeline_id),
-        _ => unreachable!("Got a bizarre value for the clip type"),
+impl WrSpatialId {
+    fn to_webrender(&self, pipeline_id: WrPipelineId) -> SpatialId {
+        SpatialId::new(self.id, pipeline_id)
     }
 }
 
 /// cbindgen:postfix=WR_DESTRUCTOR_SAFE_FUNC
 #[no_mangle]
 pub unsafe extern "C" fn wr_device_delete(device: *mut Device) {
     Box::from_raw(device);
 }
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -13,16 +13,20 @@
 
 #include <cstdarg>
 #include <cstdint>
 #include <cstdlib>
 
 namespace mozilla {
 namespace wr {
 
+enum {
+  ROOT_CLIP_CHAIN = ~0,
+};
+
 /// Whether a border should be antialiased.
 enum class AntialiasBorder {
   No = 0,
   Yes,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
@@ -646,16 +650,34 @@ struct WrDebugFlags {
 struct WrClipId {
   uintptr_t id;
 
   bool operator==(const WrClipId& aOther) const {
     return id == aOther.id;
   }
 };
 
+struct WrSpatialId {
+  uintptr_t id;
+
+  bool operator==(const WrSpatialId& aOther) const {
+    return id == aOther.id;
+  }
+};
+
+struct WrSpaceAndClip {
+  WrSpatialId space;
+  WrClipId clip;
+
+  bool operator==(const WrSpaceAndClip& aOther) const {
+    return space == aOther.space &&
+           clip == aOther.clip;
+  }
+};
+
 /// A 2d Rectangle optionally tagged with a unit.
 template<typename T, typename U>
 struct TypedRect {
   TypedPoint2D<T, U> origin;
   TypedSize2D<T, U> size;
 
   bool operator==(const TypedRect& aOther) const {
     return origin == aOther.origin &&
@@ -721,16 +743,26 @@ struct WrImageMask {
 
   bool operator==(const WrImageMask& aOther) const {
     return image == aOther.image &&
            rect == aOther.rect &&
            repeat == aOther.repeat;
   }
 };
 
+struct WrSpaceAndClipChain {
+  WrSpatialId space;
+  uint64_t clip_chain;
+
+  bool operator==(const WrSpaceAndClipChain& aOther) const {
+    return space == aOther.space &&
+           clip_chain == aOther.clip_chain;
+  }
+};
+
 /// The minimum and maximum allowable offset for a sticky frame in a single dimension.
 struct StickyOffsetBounds {
   /// The minimum offset for this frame, typically a negative value, which specifies how
   /// far in the negative direction the sticky frame can offset its contents in this
   /// dimension.
   float min;
   /// The maximum offset for this frame, typically a positive value, which specifies how
   /// far in the positive direction the sticky frame can offset its contents in this
@@ -1305,91 +1337,91 @@ WR_INLINE
 void wr_device_delete(Device *aDevice)
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
 void wr_dp_clear_save(WrState *aState)
 WR_FUNC;
 
 WR_INLINE
-uintptr_t wr_dp_define_clip(WrState *aState,
-                            const WrClipId *aParentId,
-                            LayoutRect aClipRect,
-                            const ComplexClipRegion *aComplex,
-                            uintptr_t aComplexCount,
-                            const WrImageMask *aMask)
+WrClipId wr_dp_define_clip_with_parent_clip(WrState *aState,
+                                            const WrSpaceAndClip *aParent,
+                                            LayoutRect aClipRect,
+                                            const ComplexClipRegion *aComplex,
+                                            uintptr_t aComplexCount,
+                                            const WrImageMask *aMask)
+WR_FUNC;
+
+WR_INLINE
+WrClipId wr_dp_define_clip_with_parent_clip_chain(WrState *aState,
+                                                  const WrSpaceAndClipChain *aParent,
+                                                  LayoutRect aClipRect,
+                                                  const ComplexClipRegion *aComplex,
+                                                  uintptr_t aComplexCount,
+                                                  const WrImageMask *aMask)
 WR_FUNC;
 
 WR_INLINE
 uint64_t wr_dp_define_clipchain(WrState *aState,
                                 const uint64_t *aParentClipchainId,
                                 const WrClipId *aClips,
                                 uintptr_t aClipsCount)
 WR_FUNC;
 
 WR_INLINE
-uintptr_t wr_dp_define_scroll_layer(WrState *aState,
-                                    uint64_t aScrollId,
-                                    const WrClipId *aParentId,
-                                    LayoutRect aContentRect,
-                                    LayoutRect aClipRect)
+WrSpaceAndClip wr_dp_define_scroll_layer(WrState *aState,
+                                         uint64_t aExternalScrollId,
+                                         const WrSpaceAndClip *aParent,
+                                         LayoutRect aContentRect,
+                                         LayoutRect aClipRect)
 WR_FUNC;
 
 WR_INLINE
-uintptr_t wr_dp_define_sticky_frame(WrState *aState,
-                                    LayoutRect aContentRect,
-                                    const float *aTopMargin,
-                                    const float *aRightMargin,
-                                    const float *aBottomMargin,
-                                    const float *aLeftMargin,
-                                    StickyOffsetBounds aVerticalBounds,
-                                    StickyOffsetBounds aHorizontalBounds,
-                                    LayoutVector2D aAppliedOffset)
+WrSpatialId wr_dp_define_sticky_frame(WrState *aState,
+                                      WrSpatialId aParentSpatialId,
+                                      LayoutRect aContentRect,
+                                      const float *aTopMargin,
+                                      const float *aRightMargin,
+                                      const float *aBottomMargin,
+                                      const float *aLeftMargin,
+                                      StickyOffsetBounds aVerticalBounds,
+                                      StickyOffsetBounds aHorizontalBounds,
+                                      LayoutVector2D aAppliedOffset)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_pop_all_shadows(WrState *aState)
 WR_FUNC;
 
 WR_INLINE
-void wr_dp_pop_clip(WrState *aState)
-WR_FUNC;
-
-WR_INLINE
-void wr_dp_pop_clip_and_scroll_info(WrState *aState)
-WR_FUNC;
-
-WR_INLINE
-void wr_dp_pop_scroll_layer(WrState *aState)
-WR_FUNC;
-
-WR_INLINE
 void wr_dp_pop_stacking_context(WrState *aState,
                                 bool aIsReferenceFrame)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_border(WrState *aState,
                        LayoutRect aRect,
                        LayoutRect aClip,
                        bool aIsBackfaceVisible,
+                       const WrSpaceAndClipChain *aParent,
                        AntialiasBorder aDoAa,
                        LayoutSideOffsets aWidths,
                        BorderSide aTop,
                        BorderSide aRight,
                        BorderSide aBottom,
                        BorderSide aLeft,
                        BorderRadius aRadius)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_border_gradient(WrState *aState,
                                 LayoutRect aRect,
                                 LayoutRect aClip,
                                 bool aIsBackfaceVisible,
+                                const WrSpaceAndClipChain *aParent,
                                 LayoutSideOffsets aWidths,
                                 int32_t aWidth,
                                 int32_t aHeight,
                                 SideOffsets2D<int32_t> aSlice,
                                 LayoutPoint aStartPoint,
                                 LayoutPoint aEndPoint,
                                 const GradientStop *aStops,
                                 uintptr_t aStopsCount,
@@ -1397,214 +1429,228 @@ void wr_dp_push_border_gradient(WrState 
                                 SideOffsets2D<float> aOutset)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_border_image(WrState *aState,
                              LayoutRect aRect,
                              LayoutRect aClip,
                              bool aIsBackfaceVisible,
+                             const WrSpaceAndClipChain *aParent,
                              LayoutSideOffsets aWidths,
                              WrImageKey aImage,
                              int32_t aWidth,
                              int32_t aHeight,
                              SideOffsets2D<int32_t> aSlice,
                              SideOffsets2D<float> aOutset,
                              RepeatMode aRepeatHorizontal,
                              RepeatMode aRepeatVertical)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_border_radial_gradient(WrState *aState,
                                        LayoutRect aRect,
                                        LayoutRect aClip,
                                        bool aIsBackfaceVisible,
+                                       const WrSpaceAndClipChain *aParent,
                                        LayoutSideOffsets aWidths,
                                        LayoutPoint aCenter,
                                        LayoutSize aRadius,
                                        const GradientStop *aStops,
                                        uintptr_t aStopsCount,
                                        ExtendMode aExtendMode,
                                        SideOffsets2D<float> aOutset)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_box_shadow(WrState *aState,
                            LayoutRect aRect,
                            LayoutRect aClip,
                            bool aIsBackfaceVisible,
+                           const WrSpaceAndClipChain *aParent,
                            LayoutRect aBoxBounds,
                            LayoutVector2D aOffset,
                            ColorF aColor,
                            float aBlurRadius,
                            float aSpreadRadius,
                            BorderRadius aBorderRadius,
                            BoxShadowClipMode aClipMode)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_clear_rect(WrState *aState,
                            LayoutRect aRect,
-                           LayoutRect aClip)
+                           LayoutRect aClip,
+                           const WrSpaceAndClipChain *aParent)
 WR_FUNC;
 
 WR_INLINE
-void wr_dp_push_clip(WrState *aState,
-                     WrClipId aClipId)
-WR_FUNC;
-
-WR_INLINE
-void wr_dp_push_clip_and_scroll_info(WrState *aState,
-                                     WrClipId aScrollId,
-                                     const uint64_t *aClipChainId)
+void wr_dp_push_clear_rect_with_parent_clip(WrState *aState,
+                                            LayoutRect aRect,
+                                            LayoutRect aClip,
+                                            const WrSpaceAndClip *aParent)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_iframe(WrState *aState,
                        LayoutRect aRect,
                        LayoutRect aClip,
                        bool aIsBackfaceVisible,
+                       const WrSpaceAndClipChain *aParent,
                        WrPipelineId aPipelineId,
                        bool aIgnoreMissingPipeline)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_image(WrState *aState,
                       LayoutRect aBounds,
                       LayoutRect aClip,
                       bool aIsBackfaceVisible,
+                      const WrSpaceAndClipChain *aParent,
                       LayoutSize aStretchSize,
                       LayoutSize aTileSpacing,
                       ImageRendering aImageRendering,
                       WrImageKey aKey,
                       bool aPremultipliedAlpha,
                       ColorF aColor)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_line(WrState *aState,
                      const LayoutRect *aClip,
                      bool aIsBackfaceVisible,
+                     const WrSpaceAndClipChain *aParent,
                      const LayoutRect *aBounds,
                      float aWavyLineThickness,
                      LineOrientation aOrientation,
                      const ColorF *aColor,
                      LineStyle aStyle)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_linear_gradient(WrState *aState,
                                 LayoutRect aRect,
                                 LayoutRect aClip,
                                 bool aIsBackfaceVisible,
+                                const WrSpaceAndClipChain *aParent,
                                 LayoutPoint aStartPoint,
                                 LayoutPoint aEndPoint,
                                 const GradientStop *aStops,
                                 uintptr_t aStopsCount,
                                 ExtendMode aExtendMode,
                                 LayoutSize aTileSize,
                                 LayoutSize aTileSpacing)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_radial_gradient(WrState *aState,
                                 LayoutRect aRect,
                                 LayoutRect aClip,
                                 bool aIsBackfaceVisible,
+                                const WrSpaceAndClipChain *aParent,
                                 LayoutPoint aCenter,
                                 LayoutSize aRadius,
                                 const GradientStop *aStops,
                                 uintptr_t aStopsCount,
                                 ExtendMode aExtendMode,
                                 LayoutSize aTileSize,
                                 LayoutSize aTileSpacing)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_rect(WrState *aState,
                      LayoutRect aRect,
                      LayoutRect aClip,
                      bool aIsBackfaceVisible,
+                     const WrSpaceAndClipChain *aParent,
                      ColorF aColor)
 WR_FUNC;
 
 WR_INLINE
-void wr_dp_push_scroll_layer(WrState *aState,
-                             WrClipId aScrollId)
+void wr_dp_push_rect_with_parent_clip(WrState *aState,
+                                      LayoutRect aRect,
+                                      LayoutRect aClip,
+                                      bool aIsBackfaceVisible,
+                                      const WrSpaceAndClip *aParent,
+                                      ColorF aColor)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_shadow(WrState *aState,
                        LayoutRect aBounds,
                        LayoutRect aClip,
                        bool aIsBackfaceVisible,
+                       const WrSpaceAndClipChain *aParent,
                        Shadow aShadow)
 WR_FUNC;
 
 WR_INLINE
-void wr_dp_push_stacking_context(WrState *aState,
-                                 LayoutRect aBounds,
-                                 const WrClipId *aClipNodeId,
-                                 const WrAnimationProperty *aAnimation,
-                                 const float *aOpacity,
-                                 const LayoutTransform *aTransform,
-                                 TransformStyle aTransformStyle,
-                                 const LayoutTransform *aPerspective,
-                                 MixBlendMode aMixBlendMode,
-                                 const WrFilterOp *aFilters,
-                                 uintptr_t aFilterCount,
-                                 bool aIsBackfaceVisible,
-                                 RasterSpace aGlyphRasterSpace,
-                                 bool *aOutIsReferenceFrame,
-                                 uintptr_t *aOutReferenceFrameId)
+WrSpatialId wr_dp_push_stacking_context(WrState *aState,
+                                        LayoutRect aBounds,
+                                        WrSpatialId aSpatialId,
+                                        const WrClipId *aClipId,
+                                        const WrAnimationProperty *aAnimation,
+                                        const float *aOpacity,
+                                        const LayoutTransform *aTransform,
+                                        TransformStyle aTransformStyle,
+                                        const LayoutTransform *aPerspective,
+                                        MixBlendMode aMixBlendMode,
+                                        const WrFilterOp *aFilters,
+                                        uintptr_t aFilterCount,
+                                        bool aIsBackfaceVisible,
+                                        RasterSpace aGlyphRasterSpace)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_text(WrState *aState,
                      LayoutRect aBounds,
                      LayoutRect aClip,
                      bool aIsBackfaceVisible,
+                     const WrSpaceAndClipChain *aParent,
                      ColorF aColor,
                      WrFontInstanceKey aFontKey,
                      const GlyphInstance *aGlyphs,
                      uint32_t aGlyphCount,
                      const GlyphOptions *aGlyphOptions)
 WR_FUNC;
 
 /// Push a 2 planar NV12 image.
 WR_INLINE
 void wr_dp_push_yuv_NV12_image(WrState *aState,
                                LayoutRect aBounds,
                                LayoutRect aClip,
                                bool aIsBackfaceVisible,
+                               const WrSpaceAndClipChain *aParent,
                                WrImageKey aImageKey0,
                                WrImageKey aImageKey1,
                                WrColorDepth aColorDepth,
                                WrYuvColorSpace aColorSpace,
                                ImageRendering aImageRendering)
 WR_FUNC;
 
 /// Push a yuv interleaved image.
 WR_INLINE
 void wr_dp_push_yuv_interleaved_image(WrState *aState,
                                       LayoutRect aBounds,
                                       LayoutRect aClip,
                                       bool aIsBackfaceVisible,
+                                      const WrSpaceAndClipChain *aParent,
                                       WrImageKey aImageKey0,
                                       WrColorDepth aColorDepth,
                                       WrYuvColorSpace aColorSpace,
                                       ImageRendering aImageRendering)
 WR_FUNC;
 
 /// Push a 3 planar yuv image.
 WR_INLINE
 void wr_dp_push_yuv_planar_image(WrState *aState,
                                  LayoutRect aBounds,
                                  LayoutRect aClip,
                                  bool aIsBackfaceVisible,
+                                 const WrSpaceAndClipChain *aParent,
                                  WrImageKey aImageKey0,
                                  WrImageKey aImageKey1,
                                  WrImageKey aImageKey2,
                                  WrColorDepth aColorDepth,
                                  WrYuvColorSpace aColorSpace,
                                  ImageRendering aImageRendering)
 WR_FUNC;
 
@@ -1818,17 +1864,21 @@ WR_FUNC;
 WR_INLINE
 void wr_resource_updates_update_image(Transaction *aTxn,
                                       WrImageKey aKey,
                                       const WrImageDescriptor *aDescriptor,
                                       WrVecU8 *aBytes)
 WR_FUNC;
 
 WR_INLINE
-uintptr_t wr_root_scroll_node_id()
+WrClipId wr_root_clip_id()
+WR_FUNC;
+
+WR_INLINE
+WrSpatialId wr_root_scroll_node_id()
 WR_FUNC;
 
 extern void wr_schedule_render(WrWindowId aWindowId);
 
 WR_INLINE
 void wr_set_item_tag(WrState *aState,
                      uint64_t aScrollId,
                      uint16_t aHitInfo)
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -480,22 +480,17 @@ bool BulletRenderer::CreateWebRenderComm
       wr::BorderSide sides[4] = {side, side, side, side};
       Range<const wr::BorderSide> sidesRange(sides, 4);
       aBuilder.PushBorder(dest, dest, isBackfaceVisible, borderWidths,
                           sidesRange,
                           wr::ToBorderRadius(radii, radii, radii, radii));
       return true;
     }
     case NS_STYLE_LIST_STYLE_DISC: {
-      AutoTArray<wr::ComplexClipRegion, 1> clips;
-      clips.AppendElement(wr::SimpleRadii(dest, dest.size.width / 2));
-      auto clipId = aBuilder.DefineClip(Nothing(), dest, &clips, nullptr);
-      aBuilder.PushClip(clipId);
-      aBuilder.PushRect(dest, dest, isBackfaceVisible, color);
-      aBuilder.PopClip();
+      aBuilder.PushRoundedRect(dest, dest, isBackfaceVisible, color);
       return true;
     }
     case NS_STYLE_LIST_STYLE_SQUARE: {
       aBuilder.PushRect(dest, dest, isBackfaceVisible, color);
       return true;
     }
     default:
       if (!aManager->CommandBuilder().PushItemAsImage(
--- a/layout/generic/nsImageFrame.cpp
+++ b/layout/generic/nsImageFrame.cpp
@@ -1661,22 +1661,17 @@ ImgDrawResult nsImageFrame::DisplayAltFe
       // filled circle in bottom right quadrant of stroked rect:
       nscoord twoPX = nsPresContext::CSSPixelsToAppUnits(2);
       rect = nsRect(iconXPos + size / 2, inner.y + size / 2, size / 2 - twoPX,
                     size / 2 - twoPX);
       devPxRect = LayoutDeviceRect::FromAppUnits(
           rect, PresContext()->AppUnitsPerDevPixel());
       dest = wr::ToRoundedLayoutRect(devPxRect);
 
-      AutoTArray<wr::ComplexClipRegion, 1> clips;
-      clips.AppendElement(wr::SimpleRadii(dest, dest.size.width / 2));
-      auto clipId = aBuilder.DefineClip(Nothing(), dest, &clips, nullptr);
-      aBuilder.PushClip(clipId);
-      aBuilder.PushRect(dest, wrBounds, isBackfaceVisible, color);
-      aBuilder.PopClip();
+      aBuilder.PushRoundedRect(dest, wrBounds, isBackfaceVisible, color);
     }
 
     // Reduce the inner rect by the width of the icon, and leave an
     // additional ICON_PADDING pixels for padding
     int32_t paddedIconSize =
         nsPresContext::CSSPixelsToAppUnits(ICON_SIZE + ICON_PADDING);
     if (wm.IsVertical()) {
       inner.y += paddedIconSize;
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -3297,45 +3297,40 @@ void nsCSSBorderRenderer::DrawBorders() 
 }
 
 void nsCSSBorderRenderer::CreateWebRenderCommands(
     nsDisplayItem* aItem, wr::DisplayListBuilder& aBuilder,
     wr::IpcResourceUpdateQueue& aResources,
     const layers::StackingContextHelper& aSc) {
   LayoutDeviceRect outerRect = LayoutDeviceRect::FromUnknownRect(mOuterRect);
   wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(outerRect);
+  wr::LayoutRect clipRect = roundedRect;
   wr::BorderSide side[4];
   NS_FOR_CSS_SIDES(i) {
     side[i] =
         wr::ToBorderSide(ToDeviceColor(mBorderColors[i]), mBorderStyles[i]);
   }
 
   wr::BorderRadius borderRadius =
       wr::ToBorderRadius(LayoutDeviceSize::FromUnknownSize(mBorderRadii[0]),
                          LayoutDeviceSize::FromUnknownSize(mBorderRadii[1]),
                          LayoutDeviceSize::FromUnknownSize(mBorderRadii[3]),
                          LayoutDeviceSize::FromUnknownSize(mBorderRadii[2]));
 
   if (mLocalClip) {
-    LayoutDeviceRect clip =
+    LayoutDeviceRect localClip =
         LayoutDeviceRect::FromUnknownRect(mLocalClip.value());
-    wr::LayoutRect clipRect = wr::ToRoundedLayoutRect(clip);
-    wr::WrClipId clipId = aBuilder.DefineClip(Nothing(), clipRect);
-    aBuilder.PushClip(clipId);
+    clipRect = wr::ToRoundedLayoutRect(localClip.Intersect(outerRect));
   }
 
   Range<const wr::BorderSide> wrsides(side, 4);
-  aBuilder.PushBorder(roundedRect, roundedRect, mBackfaceIsVisible,
+  aBuilder.PushBorder(roundedRect, clipRect, mBackfaceIsVisible,
                       wr::ToBorderWidths(mBorderWidths[0], mBorderWidths[1],
                                          mBorderWidths[2], mBorderWidths[3]),
                       wrsides, borderRadius);
-
-  if (mLocalClip) {
-    aBuilder.PopClip();
-  }
 }
 
 /* static */ Maybe<nsCSSBorderImageRenderer>
 nsCSSBorderImageRenderer::CreateBorderImageRenderer(
     nsPresContext* aPresContext, nsIFrame* aForFrame, const nsRect& aBorderArea,
     const nsStyleBorder& aStyleBorder, const nsRect& aDirtyRect,
     Sides aSkipSides, uint32_t aFlags, ImgDrawResult* aDrawResult) {
   MOZ_ASSERT(aDrawResult);
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -6875,16 +6875,18 @@ bool nsDisplayStickyPosition::CreateWebR
     // the sticky items correctly on the gecko side and WR will never need to
     // adjust their position itself.
     if (!stickyScrollContainer->ScrollFrame()
              ->IsMaybeAsynchronouslyScrolled()) {
       stickyScrollContainer = nullptr;
     }
   }
 
+  Maybe<wr::SpaceAndClipChainHelper> saccHelper;
+
   if (stickyScrollContainer) {
     float auPerDevPixel = mFrame->PresContext()->AppUnitsPerDevPixel();
 
     bool snap;
     nsRect itemBounds = GetBounds(aDisplayListBuilder, &snap);
 
     Maybe<float> topMargin;
     Maybe<float> rightMargin;
@@ -6992,35 +6994,34 @@ bool nsDisplayStickyPosition::CreateWebR
       }
     }
 
     LayoutDeviceRect bounds =
         LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
     wr::LayoutVector2D applied = {
         NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
         NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)};
-    wr::WrClipId id = aBuilder.DefineStickyFrame(
+    wr::WrSpatialId spatialId = aBuilder.DefineStickyFrame(
         wr::ToRoundedLayoutRect(bounds), topMargin.ptrOr(nullptr),
         rightMargin.ptrOr(nullptr), bottomMargin.ptrOr(nullptr),
         leftMargin.ptrOr(nullptr), vBounds, hBounds, applied);
 
-    aBuilder.PushClip(id);
-    aManager->CommandBuilder().PushOverrideForASR(mContainerASR, id);
+    saccHelper.emplace(aBuilder, spatialId);
+    aManager->CommandBuilder().PushOverrideForASR(mContainerASR, spatialId);
   }
 
   {
     StackingContextHelper sc(aSc, GetActiveScrolledRoot(), mFrame, this,
                              aBuilder);
     nsDisplayWrapList::CreateWebRenderCommands(aBuilder, aResources, sc,
                                                aManager, aDisplayListBuilder);
   }
 
   if (stickyScrollContainer) {
     aManager->CommandBuilder().PopOverrideForASR(mContainerASR);
-    aBuilder.PopClip();
   }
 
   return true;
 }
 
 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
     nsDisplayListBuilder* aBuilder, nsIFrame* aScrolledFrame,
     nsIFrame* aScrollFrame)
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -3886,24 +3886,20 @@ void nsWindow::UpdateThemeGeometries(
   layerManager->SetRegionToClear(clearRegion);
 }
 
 void nsWindow::AddWindowOverlayWebRenderCommands(
     layers::WebRenderBridgeChild* aWrBridge, wr::DisplayListBuilder& aBuilder,
     wr::IpcResourceUpdateQueue& aResources) {
   if (mWindowButtonsRect) {
     wr::LayoutRect rect = wr::ToLayoutRect(*mWindowButtonsRect);
-    nsTArray<wr::ComplexClipRegion> roundedClip;
-    roundedClip.AppendElement(wr::ToComplexClipRegion(
+    auto complexRegion = wr::ToComplexClipRegion(
         RoundedRect(IntRectToRect(mWindowButtonsRect->ToUnknownRect()),
-                    RectCornerRadii(0, 0, 3, 3))));
-    wr::WrClipId clipId = aBuilder.DefineClip(Nothing(), rect, &roundedClip);
-    aBuilder.PushClip(clipId);
-    aBuilder.PushClearRect(rect);
-    aBuilder.PopClip();
+                    RectCornerRadii(0, 0, 3, 3)));
+    aBuilder.PushClearRectWithComplexRegion(rect, complexRegion);
   }
 }
 
 uint32_t nsWindow::GetMaxTouchPoints() const {
   return WinUtils::GetMaxTouchPoints();
 }
 
 void nsWindow::SetWindowClass(const nsAString& xulWinType) {