Bug 1377187 - Rewrite the clipping code to use the new clipchain API. r?mstange draft
authorKartikaya Gupta <kgupta@mozilla.com>
Tue, 08 May 2018 09:08:39 -0400
changeset 792512 305c0f4f1121da55e63879ede69d317f80190811
parent 792511 86170dec8abf8881e2a3f1aa077653df10b113b4
child 792513 f98505abd0d032338df4d1709a08770b4d8e4d1d
push id109117
push userkgupta@mozilla.com
push dateTue, 08 May 2018 13:09:28 +0000
reviewersmstange
bugs1377187
milestone62.0a1
Bug 1377187 - Rewrite the clipping code to use the new clipchain API. r?mstange The clip chain API in webrender allows us to build the clip state in WR so that it matches the gecko display list more closely. This patch throws away ScrollingLayersHelper.* and introduces ClipManager.* which pushes the clip state to WR using the new method. A quick summary of the new method is below. Each display item in gecko has a DisplayItemClipChain which is a chain of individual clips. The individual clips are defined in WR, and the clip ids for those clips are put into a WR clip chain using the new define_clip_chain API. Furthermore, each clip chain can also have a parent chain, which is used to link a DisplayItemClipChain to the parent display item's DisplayItemClipChain. This allows the WR clip state to closely match the structure of the gecko display list clip state, resulting in more correct behaviour. There are a few other major changes that are lumped into this patch and that were tricky to separate into their own patches: - The collapsing of WrScrollId and WrStickyId into WrClipId. On the WR side all the clip ids are treated the same anyway. Trying to preserve the arbitrary distinction on the gecko side was resulting in increasingly convoluted code, with different kinds of Variant<..> types in the method signatures. It was much simpler and resulted in a bunch of code deletion to just collapse the types. - Moving the "override" mechanism from WebRenderAPI to ClipManager. The override mechanism (explained in ClipManager.h) was simplified by moving it into ClipManager, because it removed the need for tracking additional clip stack state in WebRenderAPI. MozReview-Commit-ID: GGbdFyJGprK
gfx/layers/moz.build
gfx/layers/wr/ClipManager.cpp
gfx/layers/wr/ClipManager.h
gfx/layers/wr/ScrollingLayersHelper.cpp
gfx/layers/wr/ScrollingLayersHelper.h
gfx/layers/wr/StackingContextHelper.cpp
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/nsHTMLCanvasFrame.cpp
layout/painting/nsCSSRenderingBorders.cpp
layout/painting/nsDisplayList.cpp
layout/reftests/async-scrolling/reftest.list
layout/reftests/backgrounds/reftest.list
layout/reftests/bugs/reftest.list
layout/reftests/forms/placeholder/reftest.list
layout/reftests/forms/textarea/reftest.list
widget/windows/nsWindow.cpp
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -229,18 +229,18 @@ EXPORTS.mozilla.layers += [
     'SourceSurfaceSharedData.h',
     'SourceSurfaceVolatileData.h',
     'SyncObject.h',
     'TextureSourceProvider.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
     'UpdateImageHelper.h',
     'wr/AsyncImagePipelineManager.h',
+    'wr/ClipManager.h',
     'wr/IpcResourceUpdateQueue.h',
-    'wr/ScrollingLayersHelper.h',
     'wr/StackingContextHelper.h',
     'wr/WebRenderBridgeChild.h',
     'wr/WebRenderBridgeParent.h',
     'wr/WebRenderCanvasRenderer.h',
     'wr/WebRenderCommandBuilder.h',
     'wr/WebRenderDrawEventRecorder.h',
     'wr/WebRenderImageHost.h',
     'wr/WebRenderLayerManager.h',
@@ -468,18 +468,18 @@ UNIFIED_SOURCES += [
     'RotatedBuffer.cpp',
     'ShareableCanvasRenderer.cpp',
     'SourceSurfaceSharedData.cpp',
     'SourceSurfaceVolatileData.cpp',
     'SyncObject.cpp',
     'TextureSourceProvider.cpp',
     'TextureWrapperImage.cpp',
     'wr/AsyncImagePipelineManager.cpp',
+    'wr/ClipManager.cpp',
     'wr/IpcResourceUpdateQueue.cpp',
-    'wr/ScrollingLayersHelper.cpp',
     'wr/StackingContextHelper.cpp',
     'wr/WebRenderBridgeChild.cpp',
     'wr/WebRenderBridgeParent.cpp',
     'wr/WebRenderCanvasRenderer.cpp',
     'wr/WebRenderCommandBuilder.cpp',
     'wr/WebRenderDrawEventRecorder.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderLayerManager.cpp',
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/ClipManager.cpp
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/layers/ClipManager.h"
+
+#include "DisplayItemClipChain.h"
+#include "FrameMetrics.h"
+#include "LayersLogging.h"
+#include "mozilla/layers/StackingContextHelper.h"
+#include "mozilla/layers/WebRenderLayerManager.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "nsDisplayList.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__)
+
+namespace mozilla {
+namespace layers {
+
+ClipManager::ClipManager()
+  : mManager(nullptr)
+  , mBuilder(nullptr)
+{
+}
+
+void
+ClipManager::BeginBuild(WebRenderLayerManager* aManager,
+                        wr::DisplayListBuilder& aBuilder)
+{
+  MOZ_ASSERT(!mManager);
+  mManager = aManager;
+  MOZ_ASSERT(!mBuilder);
+  mBuilder = &aBuilder;
+  MOZ_ASSERT(mCacheStack.empty());
+  mCacheStack.emplace();
+  MOZ_ASSERT(mASROverride.empty());
+  MOZ_ASSERT(mItemClipStack.empty());
+}
+
+void
+ClipManager::EndBuild()
+{
+  mBuilder = nullptr;
+  mManager = nullptr;
+  mCacheStack.pop();
+  MOZ_ASSERT(mCacheStack.empty());
+  MOZ_ASSERT(mASROverride.empty());
+  MOZ_ASSERT(mItemClipStack.empty());
+}
+
+void
+ClipManager::BeginList(const StackingContextHelper& aStackingContext)
+{
+  if (aStackingContext.AffectsClipPositioning()) {
+    PushOverrideForASR(
+        mItemClipStack.empty() ? nullptr : mItemClipStack.top().mASR,
+        Nothing());
+  }
+
+  ItemClips clips(nullptr, nullptr);
+  if (!mItemClipStack.empty()) {
+    clips.CopyOutputsFrom(mItemClipStack.top());
+  }
+  mItemClipStack.push(clips);
+}
+
+void
+ClipManager::EndList(const StackingContextHelper& aStackingContext)
+{
+  MOZ_ASSERT(!mItemClipStack.empty());
+  mItemClipStack.top().Unapply(mBuilder);
+  mItemClipStack.pop();
+
+  if (aStackingContext.AffectsClipPositioning()) {
+    PopOverrideForASR(
+        mItemClipStack.empty() ? nullptr : mItemClipStack.top().mASR);
+  }
+}
+
+void
+ClipManager::PushOverrideForASR(const ActiveScrolledRoot* aASR,
+                                const Maybe<wr::WrClipId>& aClipId)
+{
+  layers::FrameMetrics::ViewID viewId = aASR
+      ? aASR->GetViewId() : layers::FrameMetrics::NULL_SCROLL_ID;
+  Maybe<wr::WrClipId> scrollId = mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
+  MOZ_ASSERT(scrollId.isSome());
+
+  CLIP_LOG("Pushing override %" PRIu64 " -> %s\n", scrollId->id,
+      aClipId ? Stringify(aClipId->id).c_str() : "(none)");
+  auto it = mASROverride.insert({ *scrollId, std::stack<Maybe<wr::WrClipId>>() });
+  it.first->second.push(aClipId);
+
+  // Start a new cache
+  mCacheStack.emplace();
+}
+
+void
+ClipManager::PopOverrideForASR(const ActiveScrolledRoot* aASR)
+{
+  MOZ_ASSERT(!mCacheStack.empty());
+  mCacheStack.pop();
+
+  layers::FrameMetrics::ViewID viewId = aASR
+      ? aASR->GetViewId() : layers::FrameMetrics::NULL_SCROLL_ID;
+  Maybe<wr::WrClipId> scrollId = mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
+  MOZ_ASSERT(scrollId.isSome());
+
+  auto it = mASROverride.find(*scrollId);
+  MOZ_ASSERT(it != mASROverride.end());
+  MOZ_ASSERT(!(it->second.empty()));
+  CLIP_LOG("Popping override %" PRIu64 " -> %s\n", scrollId->id,
+      it->second.top() ? Stringify(it->second.top()->id).c_str() : "(none)");
+  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);
+  if (it == mASROverride.end()) {
+    return aClipId;
+  }
+  MOZ_ASSERT(!it->second.empty());
+  CLIP_LOG("Overriding %" PRIu64 " with %s\n", aClipId->id,
+      it->second.top() ? Stringify(it->second.top()->id).c_str() : "(none)");
+  return it->second.top();
+}
+
+void
+ClipManager::BeginItem(nsDisplayItem* aItem,
+                       const StackingContextHelper& aStackingContext)
+{
+  CLIP_LOG("processing item %p\n", aItem);
+
+  const DisplayItemClipChain* clip = aItem->GetClipChain();
+  const ActiveScrolledRoot* asr = aItem->GetActiveScrolledRoot();
+
+  ItemClips clips(asr, clip);
+  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;
+  }
+  // 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 = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
+  if (aItem->GetType() == DisplayItemType::TYPE_ZOOM) {
+    auPerDevPixel = static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
+  }
+
+  // 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 = 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 && clip->mASR == asr) {
+    // If the clip's ASR is the same as the item's ASR, then we want to use
+    // the clip as the "scrollframe" for the item, as WR will do the right thing
+    // when building the ClipScrollTree and ensure the item scrolls with the
+    // ASR. Note in particular that we don't want to use scroll id of |asr| here
+    // because we might have a situation where there is a stacking context
+    // between |asr| and |aItem|, and if we used |asr|'s scroll id, then WR
+    // would effectively hoist the item out of the stacking context and attach
+    // it directly to |asr|. This can produce incorrect results. Using the clip
+    // instead of the ASR is strictly better because the clip is usually defined
+    // inside the stacking context, and so the item also stays "inside" the
+    // stacking context rather than geting hoisted out. Note that there might
+    // be cases where the clip is also "outside" the stacking context and in
+    // theory that situation might not be handled correctly, but I haven't seen
+    // it in practice so far.
+    const ClipIdMap& cache = mCacheStack.top();
+    auto it = cache.find(clip);
+    MOZ_ASSERT(it != cache.end());
+    clips.mScrollId = Some(it->second);
+  } else if (clip) {
+    // If the clip's ASR is different, then we need to set the scroll id
+    // explicitly to match the desired ASR.
+    FrameMetrics::ViewID viewId = asr
+        ? asr->GetViewId()
+        : FrameMetrics::NULL_SCROLL_ID;
+    Maybe<wr::WrClipId> scrollId =
+        mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
+    MOZ_ASSERT(scrollId.isSome());
+    clips.mScrollId = 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.
+  }
+
+  // Now that we have the scroll id and a clip id for the item, push it onto
+  // the WR stack.
+  clips.Apply(mBuilder);
+  mItemClipStack.push(clips);
+
+  CLIP_LOG("done setup for %p\n", aItem);
+}
+
+Maybe<wr::WrClipId>
+ClipManager::DefineScrollLayers(const ActiveScrolledRoot* aASR,
+                                nsDisplayItem* aItem,
+                                const StackingContextHelper& aSc)
+{
+  if (!aASR) {
+    // Recursion base case
+    return Nothing();
+  }
+  FrameMetrics::ViewID viewId = aASR->GetViewId();
+  Maybe<wr::WrClipId> scrollId = mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
+  if (scrollId) {
+    // If we've already defined this scroll layer before, we can early-exit
+    return scrollId;
+  }
+  // Recurse to define the ancestors
+  Maybe<wr::WrClipId> ancestorScrollId = DefineScrollLayers(aASR->mParent, aItem, aSc);
+
+  Maybe<ScrollMetadata> metadata = aASR->mScrollableFrame->ComputeScrollMetadata(
+      mManager, aItem->ReferenceFrame(), ContainerLayerParameters(), nullptr);
+  MOZ_ASSERT(metadata);
+  FrameMetrics& metrics = metadata->GetMetrics();
+
+  if (!metrics.IsScrollable()) {
+    // This item is a scrolling no-op, skip over it in the ASR chain.
+    return ancestorScrollId;
+  }
+
+  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
+  // is, both of them should be relative to the stacking context `aSc`.
+  // 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::WrClipChainId>
+ClipManager::DefineClipChain(const DisplayItemClipChain* aChain,
+                             int32_t aAppUnitsPerDevPixel,
+                             const StackingContextHelper& aSc)
+{
+  nsTArray<wr::WrClipId> clipIds;
+  // Iterate through the clips in the current item's clip chain, define them
+  // in WR, and put their IDs into |clipIds|.
+  for (const DisplayItemClipChain* chain = aChain; chain; chain = chain->mParent) {
+    ClipIdMap& cache = mCacheStack.top();
+    auto it = cache.find(chain);
+    if (it != cache.end()) {
+      // Found it in the currently-active cache, so just use the id we have for
+      // it.
+      CLIP_LOG("cache[%p] => %" PRIu64 "\n", chain, it->second.id);
+      clipIds.AppendElement(it->second);
+      continue;
+    }
+    if (!chain->mClip.HasClip()) {
+      // This item in the chain is a no-op, skip over it
+      continue;
+    }
+
+    LayoutDeviceRect clip = LayoutDeviceRect::FromAppUnits(
+        chain->mClip.GetClipRect(), aAppUnitsPerDevPixel);
+    nsTArray<wr::ComplexClipRegion> wrRoundedRects;
+    chain->mClip.ToComplexClipRegions(aAppUnitsPerDevPixel, aSc, wrRoundedRects);
+
+    FrameMetrics::ViewID viewId = chain->mASR
+        ? chain->mASR->GetViewId()
+        : FrameMetrics::NULL_SCROLL_ID;
+    Maybe<wr::WrClipId> scrollId =
+      mBuilder->GetScrollIdForDefinedScrollLayer(viewId);
+    // Before calling DefineClipChain we defined the ASRs by calling
+    // DefineScrollLayers, so we must have a scrollId here.
+    MOZ_ASSERT(scrollId.isSome());
+
+    // Define the clip
+    Maybe<wr::WrClipId> parent = ClipIdAfterOverride(scrollId);
+    wr::WrClipId clipId = mBuilder->DefineClip(
+        parent,
+        wr::ToRoundedLayoutRect(clip), &wrRoundedRects);
+    clipIds.AppendElement(clipId);
+    cache[chain] = clipId;
+    CLIP_LOG("cache[%p] <= %" PRIu64 "\n", chain, clipId.id);
+  }
+
+  // Now find the parent display item's clipchain id
+  Maybe<wr::WrClipChainId> parentChainId;
+  if (!mItemClipStack.empty()) {
+    parentChainId = mItemClipStack.top().mClipChainId;
+  }
+
+  // And define the current display item's clipchain using the clips and the
+  // parent. If the current item has no clips of its own, just use the parent
+  // item's clipchain.
+  Maybe<wr::WrClipChainId> chainId;
+  if (clipIds.Length() > 0) {
+    chainId = Some(mBuilder->DefineClipChain(parentChainId, clipIds));
+  } else {
+    chainId = parentChainId;
+  }
+  return chainId;
+}
+
+ClipManager::~ClipManager()
+{
+  MOZ_ASSERT(!mBuilder);
+  MOZ_ASSERT(mCacheStack.empty());
+  MOZ_ASSERT(mItemClipStack.empty());
+}
+
+ClipManager::ItemClips::ItemClips(const ActiveScrolledRoot* aASR,
+                                  const DisplayItemClipChain* aChain)
+  : mASR(aASR)
+  , mChain(aChain)
+  , mApplied(false)
+{
+}
+
+void
+ClipManager::ItemClips::Apply(wr::DisplayListBuilder* aBuilder)
+{
+  MOZ_ASSERT(!mApplied);
+  mApplied = true;
+  if (mScrollId) {
+    aBuilder->PushClipAndScrollInfo(*mScrollId,
+                                    mClipChainId.ptrOr(nullptr));
+  }
+}
+
+void
+ClipManager::ItemClips::Unapply(wr::DisplayListBuilder* aBuilder)
+{
+  if (mApplied) {
+    mApplied = false;
+    if (mScrollId) {
+      aBuilder->PopClipAndScrollInfo();
+    }
+  }
+}
+
+bool
+ClipManager::ItemClips::HasSameInputs(const ItemClips& aOther)
+{
+  return mASR == aOther.mASR &&
+         mChain == aOther.mChain;
+}
+
+void
+ClipManager::ItemClips::CopyOutputsFrom(const ItemClips& aOther)
+{
+  mScrollId = aOther.mScrollId;
+  mClipChainId = aOther.mClipChainId;
+}
+
+} // namespace layers
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/ClipManager.h
@@ -0,0 +1,155 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_CLIPMANAGER_H
+#define GFX_CLIPMANAGER_H
+
+#include <stack>
+#include <unordered_map>
+
+#include "mozilla/Attributes.h"
+#include "mozilla/webrender/WebRenderAPI.h"
+
+class nsDisplayItem;
+
+namespace mozilla {
+
+struct ActiveScrolledRoot;
+struct DisplayItemClipChain;
+
+namespace wr {
+class DisplayListBuilder;
+}
+
+namespace layers {
+
+class StackingContextHelper;
+class WebRenderLayerManager;
+
+/**
+ * This class manages creating and assigning scroll layers and clips in WebRender
+ * based on the gecko display list. It has a few public functions that are
+ * intended to be invoked while traversing the Gecko display list, and it uses
+ * the ASR and clip information from the display list to create the necessary
+ * clip state in WebRender.
+ *
+ * The structure of the clip state in WebRender ends up quite similar to how
+ * it is in Gecko. For each ASR in Gecko, we create a scroll layer (i.e. a
+ * scrolling clip) in WebRender; these form a tree structure similar to the
+ * ASR tree structure. Ancestors of scroll layers are always other scroll
+ * layers, or the root scroll node.
+ * The DisplayItemClipChain list of clips from the gecko display list is
+ * converted to a WR clip chain and pushed on the stack prior to creating
+ * any WR commands for that item, and is popped afterwards. In addition,
+ * the WR clip chain has a parent pointer, which points to the clip chain for
+ * any enclosing stacking context. This again results in a strucuture very
+ * similar to that in Gecko, where the clips from container display items get
+ * applied to the contained display items.
+ */
+class ClipManager
+{
+public:
+  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);
+  ~ClipManager();
+
+  void PushOverrideForASR(const ActiveScrolledRoot* aASR,
+                          const Maybe<wr::WrClipId>& aClipId);
+  void PopOverrideForASR(const ActiveScrolledRoot* aASR);
+
+private:
+  Maybe<wr::WrClipId> ClipIdAfterOverride(const Maybe<wr::WrClipId>& aClipId);
+
+  Maybe<wr::WrClipId>
+  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;
+
+  // Stack of clip caches. Each cache contains a map from gecko
+  // DisplayItemClipChain objects to webrender WrClipIds, which allows us to
+  // avoid redefining identical clips in WR. However, the gecko
+  // DisplayItemClipChain items get deduplicated quite aggressively, without
+  // regard to things like the enclosing reference frame or mask. On the WR
+  // side, we cannot deduplicate clips that aggressively. So what we do is
+  // any time we enter a new reference frame (for example) we create a new clip
+  // cache on mCacheStack. This ensures we continue caching stuff within a given
+  // reference frame, but disallow caching stuff across reference frames. In
+  // general we need to do this anytime PushOverrideForASR is called, as that is
+  // called for the same set of conditions for which we cannot deduplicate clips.
+  typedef std::unordered_map<const DisplayItemClipChain*, wr::WrClipId> ClipIdMap;
+  std::stack<ClipIdMap> mCacheStack;
+
+  // A map that holds the cache overrides created by (a) "out of band" clips,
+  // i.e. clips that are generated by display items but that ClipManager
+  // doesn't know about and (b) stacking contexts that affect clip positioning.
+  // These are called "cache overrides" because while we're inside these things,
+  // we cannot use the ASR from the gecko display list as-is. Fundamentally this
+  // results from a mismatch between the ASR+clip items on the gecko side and
+  // the ClipScrollTree on the WR side; the WR side incorporates things like
+  // transforms and stacking context origins while the gecko side manages those
+  // 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<Maybe<wr::WrClipId>>,
+                     wr::WrClipId::HashFn> mASROverride;
+
+  // This holds some clip state for a single nsDisplayItem
+  struct ItemClips {
+    ItemClips(const ActiveScrolledRoot* aASR,
+              const DisplayItemClipChain* aChain);
+
+    // These are the "inputs" - they come from the nsDisplayItem
+    const ActiveScrolledRoot* mASR;
+    const DisplayItemClipChain* mChain;
+
+    // These are the "outputs" - they are pushed to WR as needed
+    Maybe<wr::WrClipId> mScrollId;
+    Maybe<wr::WrClipChainId> mClipChainId;
+
+    // State tracking
+    bool mApplied;
+
+    void Apply(wr::DisplayListBuilder* aBuilder);
+    void Unapply(wr::DisplayListBuilder* aBuilder);
+    bool HasSameInputs(const ItemClips& aOther);
+    void CopyOutputsFrom(const ItemClips& aOther);
+  };
+
+  // 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;
+};
+
+} // namespace layers
+} // namespace mozilla
+
+#endif
deleted file mode 100644
--- a/gfx/layers/wr/ScrollingLayersHelper.cpp
+++ /dev/null
@@ -1,597 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#include "mozilla/layers/ScrollingLayersHelper.h"
-
-#include "DisplayItemClipChain.h"
-#include "FrameMetrics.h"
-#include "mozilla/layers/StackingContextHelper.h"
-#include "mozilla/layers/WebRenderLayerManager.h"
-#include "mozilla/webrender/WebRenderAPI.h"
-#include "nsDisplayList.h"
-#include "UnitTransforms.h"
-
-#define SLH_LOG(...)
-//#define SLH_LOG(...) printf_stderr("SLH: " __VA_ARGS__)
-//#define SLH_LOG(...) if (XRE_IsContentProcess()) printf_stderr("SLH: " __VA_ARGS__)
-
-namespace mozilla {
-namespace layers {
-
-ScrollingLayersHelper::ScrollingLayersHelper()
-  : mManager(nullptr)
-  , mBuilder(nullptr)
-{
-}
-
-void
-ScrollingLayersHelper::BeginBuild(WebRenderLayerManager* aManager,
-                                  wr::DisplayListBuilder& aBuilder)
-{
-  MOZ_ASSERT(!mManager);
-  mManager = aManager;
-  MOZ_ASSERT(!mBuilder);
-  mBuilder = &aBuilder;
-  MOZ_ASSERT(mCacheStack.empty());
-  mCacheStack.emplace_back();
-  MOZ_ASSERT(mItemClipStack.empty());
-}
-
-void
-ScrollingLayersHelper::EndBuild()
-{
-  mBuilder = nullptr;
-  mManager = nullptr;
-  mCacheStack.pop_back();
-  MOZ_ASSERT(mCacheStack.empty());
-  MOZ_ASSERT(mItemClipStack.empty());
-}
-
-void
-ScrollingLayersHelper::BeginList(const StackingContextHelper& aStackingContext)
-{
-  if (aStackingContext.AffectsClipPositioning()) {
-    mCacheStack.emplace_back();
-  }
-  mItemClipStack.emplace_back(nullptr, nullptr);
-}
-
-void
-ScrollingLayersHelper::EndList(const StackingContextHelper& aStackingContext)
-{
-  MOZ_ASSERT(!mItemClipStack.empty());
-  mItemClipStack.back().Unapply(mBuilder);
-  mItemClipStack.pop_back();
-  if (aStackingContext.AffectsClipPositioning()) {
-    mCacheStack.pop_back();
-  }
-}
-
-void
-ScrollingLayersHelper::BeginItem(nsDisplayItem* aItem,
-                                 const StackingContextHelper& aStackingContext)
-{
-  SLH_LOG("processing item %p\n", aItem);
-
-  const DisplayItemClipChain* clip = aItem->GetClipChain();
-  clip = ExtendChain(clip);
-
-  ItemClips clips(aItem->GetActiveScrolledRoot(), clip);
-  MOZ_ASSERT(!mItemClipStack.empty());
-  if (clips.HasSameInputs(mItemClipStack.back())) {
-    // Early-exit because if the clips are the same 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.
-    SLH_LOG("early-exit for %p\n", aItem);
-    return;
-  }
-  mItemClipStack.back().Unapply(mBuilder);
-  mItemClipStack.pop_back();
-
-  // 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 = aItem->Frame()->PresContext()->AppUnitsPerDevPixel();
-  if (aItem->GetType() == DisplayItemType::TYPE_ZOOM) {
-    auPerDevPixel = static_cast<nsDisplayZoom*>(aItem)->GetParentAppUnitsPerDevPixel();
-  }
-
-  // There are two ASR chains here that we need to be fully defined. One is the
-  // ASR chain pointed to by aItem->GetActiveScrolledRoot(). 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.
-  // The leafmost clip is trivially going to be |clip|.
-  // So we call DefineClipChain with these two leafmost things, and it will
-  // recursively define all the clips and scroll layers with the appropriate
-  // parents, but will not actually push anything onto the WR stack.
-  const ActiveScrolledRoot* leafmostASR = aItem->GetActiveScrolledRoot();
-  if (clip) {
-    leafmostASR = ActiveScrolledRoot::PickDescendant(leafmostASR, clip->mASR);
-  }
-  auto ids = DefineClipChain(aItem, leafmostASR, clip,
-      auPerDevPixel, aStackingContext);
-
-  // Now that stuff is defined, we need to ensure the right items are on the
-  // stack. We need this primarily for the WR display items that will be
-  // generated while processing aItem. However those display items only care
-  // about the topmost clip on the stack. If that were all we cared about we
-  // would only need to push one thing here and we would be done. However, we
-  // also care about the ScrollingLayersHelper instance that might be created
-  // for nested display items, in the case where aItem is a wrapper item. The
-  // nested ScrollingLayersHelper may rely on things like TopmostScrollId and
-  // TopmostClipId, so now we need to push at most two things onto the stack.
-
-  wr::WrScrollId rootId = wr::WrScrollId::RootScrollNode();
-  wr::WrScrollId leafmostId = ids.first.valueOr(rootId);
-
-  FrameMetrics::ViewID viewId = aItem->GetActiveScrolledRoot()
-      ? aItem->GetActiveScrolledRoot()->GetViewId()
-      : FrameMetrics::NULL_SCROLL_ID;
-  wr::WrScrollId scrollId =
-      mBuilder->GetScrollIdForDefinedScrollLayer(viewId).valueOr(rootId);
-
-  // If the leafmost ASR is not the same as the item's ASR then we are dealing
-  // with a case where the item's clip chain is scrolled by something other than
-  // the item's ASR. So for those cases we need to use the ClipAndScroll API.
-  bool needClipAndScroll = (leafmostId != scrollId);
-
-  // The other scenario where we need to push a ClipAndScroll is when we are
-  // in a nested display item where the enclosing item pushed a ClipAndScroll,
-  // and our clip chain extends from that item's clip chain. To check this we
-  // want to make sure that (a) we are inside a ClipAndScroll, and (b) nothing
-  // else was pushed onto mBuilder's stack since that ClipAndScroll.
-  if (!needClipAndScroll &&
-      mBuilder->TopmostScrollId() == scrollId &&
-      !mBuilder->TopmostIsClip()) {
-    if (auto cs = EnclosingClipAndScroll()) {
-      MOZ_ASSERT(cs->first == scrollId);
-      needClipAndScroll = true;
-    }
-  }
-
-  // If we don't need a ClipAndScroll, ensure the item's ASR is at the top of
-  // the scroll stack
-  if (!needClipAndScroll && mBuilder->TopmostScrollId() != scrollId) {
-    MOZ_ASSERT(leafmostId == scrollId); // because !needClipAndScroll
-    clips.mScrollId = Some(scrollId);
-  }
-  // And ensure the leafmost clip, if scrolled by that ASR, is at the top of the
-  // stack.
-  if (ids.second && clip->mASR == leafmostASR) {
-    clips.mClipId = ids.second;
-  }
-  // If we need the ClipAndScroll, we want to replace the topmost scroll layer
-  // with the item's ASR but preseve the topmost clip (which is scrolled by
-  // some other ASR).
-  if (needClipAndScroll) {
-    // If mClipId is set that means we want to push it such that it's going
-    // to be the TopmostClipId(), but we haven't actually pushed it yet.
-    // But we still want to take that instead of the actual current TopmostClipId().
-    Maybe<wr::WrClipId> clipId = clips.mClipId;
-    if (!clipId) {
-      clipId = mBuilder->TopmostClipId();
-    }
-
-    clips.mClipAndScroll = Some(std::make_pair(scrollId, clipId));
-  }
-
-  clips.Apply(mBuilder);
-  mItemClipStack.push_back(clips);
-
-  SLH_LOG("done setup for %p\n", aItem);
-}
-
-std::pair<Maybe<wr::WrScrollId>, Maybe<wr::WrClipId>>
-ScrollingLayersHelper::DefineClipChain(nsDisplayItem* aItem,
-                                       const ActiveScrolledRoot* aAsr,
-                                       const DisplayItemClipChain* aChain,
-                                       int32_t aAppUnitsPerDevPixel,
-                                       const StackingContextHelper& aStackingContext)
-{
-  // This is the main entry point for defining the clip chain for a display
-  // item. This function recursively walks up the ASR chain and the display
-  // item's clip chain to define all the ASRs and clips necessary. Each level
-  // of the recursion defines one item, if it hasn't been defined already.
-  // The |aAsr| and |aChain| parameters are the important ones to track during
-  // the recursion; the rest of the parameters don't change.
-  // At each level of the recursion, the return value is the pair of identifiers
-  // that correspond to aAsr and aChain, respectively.
-
-  // These are the possible cases when recursing:
-  //
-  // aAsr is null, aChain is null     => base case; return
-  // aAsr is non-null, aChain is null => recurse(aAsr->mParent, null),
-  //                                     then define aAsr
-  // aAsr is null, aChain is non-null => assert(aChain->mASR == null),
-  //                                     recurse(null, aChain->mParent),
-  //                                     then define aChain
-  // aChain->mASR == aAsr             => recurse(aAsr, aChain->mParent),
-  //                                     then define aChain
-  // aChain->mASR != aAsr             => recurse(aAsr->mParent, aChain),
-  //                                     then define aAsr
-  //
-  // These can basically be collapsed down into two codepaths; one that recurses
-  // on the ASR chain and one that recurses on the clip chain; that's what the
-  // code below does.
-
-  // in all of these cases, this invariant should hold:
-  //   PickDescendant(aChain->mASR, aAsr) == aAsr
-  MOZ_ASSERT(!aChain || ActiveScrolledRoot::PickDescendant(aChain->mASR, aAsr) == aAsr);
-
-  if (aChain && aChain->mASR == aAsr) {
-    return RecurseAndDefineClip(aItem, aAsr, aChain, aAppUnitsPerDevPixel, aStackingContext);
-  }
-  if (aAsr) {
-    return RecurseAndDefineAsr(aItem, aAsr, aChain, aAppUnitsPerDevPixel, aStackingContext);
-  }
-
-  MOZ_ASSERT(!aChain && !aAsr);
-
-  return std::make_pair(Nothing(), Nothing());
-}
-
-std::pair<Maybe<wr::WrScrollId>, Maybe<wr::WrClipId>>
-ScrollingLayersHelper::RecurseAndDefineClip(nsDisplayItem* aItem,
-                                            const ActiveScrolledRoot* aAsr,
-                                            const DisplayItemClipChain* aChain,
-                                            int32_t aAppUnitsPerDevPixel,
-                                            const StackingContextHelper& aSc)
-{
-  MOZ_ASSERT(aChain);
-
-  // This will hold our return value
-  std::pair<Maybe<wr::WrScrollId>, Maybe<wr::WrClipId>> ids;
-
-  if (mBuilder->HasExtraClip()) {
-    // We can't use the clip cache directly. However if there's an out-of-band clip that
-    // was pushed on top of aChain, we should return the id for that OOB clip,
-    // so that anything we want to define as a descendant of aChain we actually
-    // end up defining as a descendant of the OOB clip.
-    ids.second = mBuilder->GetCacheOverride(aChain);
-  } else {
-    const ClipIdMap& cache = mCacheStack.back();
-    auto it = cache.find(aChain);
-    if (it != cache.end()) {
-      ids.second = Some(it->second);
-    }
-  }
-  if (ids.second) {
-    // If we've already got an id for this clip, we can early-exit
-    if (aAsr) {
-      ids.first = mBuilder->GetScrollIdForDefinedScrollLayer(aAsr->GetViewId());
-      MOZ_ASSERT(ids.first);
-    }
-    return ids;
-  }
-
-  // If not, recurse to ensure all the ancestors are defined
-  auto ancestorIds = DefineClipChain(
-      aItem, aAsr, aChain->mParent, aAppUnitsPerDevPixel, aSc);
-  ids = ancestorIds;
-
-  if (!aChain->mClip.HasClip()) {
-    // This item in the chain is a no-op, skip over it
-    return ids;
-  }
-
-  // Now we need to figure out whether the new clip we're defining should be
-  // a child of aChain->mParent, or of aAsr.
-  if (aChain->mParent) {
-    if (mBuilder->GetCacheOverride(aChain->mParent)) {
-      // If the parent clip had an override (i.e. the parent display item pushed
-      // an out-of-band clip), then we definitely want to use that as the parent
-      // because everything defined inside that clip should have it as an
-      // ancestor.
-      ancestorIds.first = Nothing();
-    } else if (aChain->mParent->mASR == aAsr) {
-      // If the parent clip item shares the ASR, then this clip needs to be
-      // a child of the aChain->mParent, which will already be a descendant of
-      // the ASR.
-      ancestorIds.first = Nothing();
-    } else {
-      // But if the ASRs are different, this is the outermost clip that's
-      // still inside aAsr, and we need to make it a child of aAsr rather
-      // than aChain->mParent.
-      ancestorIds.second = Nothing();
-    }
-  } else {
-    MOZ_ASSERT(!ancestorIds.second);
-    FrameMetrics::ViewID viewId = aChain->mASR ? aChain->mASR->GetViewId() : FrameMetrics::NULL_SCROLL_ID;
-
-    wr::WrScrollId rootId = wr::WrScrollId::RootScrollNode();
-    auto scrollId = mBuilder->GetScrollIdForDefinedScrollLayer(viewId).valueOr(rootId);
-    if (mBuilder->TopmostScrollId() == scrollId) {
-      if (mBuilder->TopmostIsClip()) {
-        // If aChain->mASR is already the topmost scroll layer on the stack, but
-        // but there was another clip pushed *on top* of that ASR, then that clip
-        // shares the ASR, and we need to make our clip a child of that clip, which
-        // in turn will already be a descendant of the correct ASR.
-        // This covers the cases where e.g. the Gecko display list has nested items,
-        // and the clip chain on the nested item implicitly extends from the clip
-        // chain on the containing wrapper item. In this case the aChain->mParent
-        // pointer will be null for the nested item but the containing wrapper's
-        // clip will be on the stack already and we can pick it up from there.
-        // Another way of thinking about this is that if the clip chain were
-        // "fully completed" then aChain->mParent wouldn't be null but would point
-        // to the clip corresponding to mBuilder->TopmostClipId(), and we would
-        // have gone into the |aChain->mParent->mASR == aAsr| branch above.
-        ancestorIds.first = Nothing();
-        ancestorIds.second = mBuilder->TopmostClipId();
-      } else if (auto cs = EnclosingClipAndScroll()) {
-        // If aChain->mASR is already the topmost scroll layer on the stack, but
-        // it was pushed as part of a "clip and scroll" entry (i.e. because an
-        // item had a clip scrolled by a different ASR than the item itself),
-        // then we have need to propagate that behaviour as well. For example if
-        // the enclosing display item pushed a ClipAndScroll with (scrollid=S,
-        // clipid=C), then then clip we're defining here (call it D) needs to be
-        // defined as a child of C, and we'll need to push the ClipAndScroll
-        // (S, D) for this item. This hunk of code ensures that we define D
-        // as a child of C, and when we set the needClipAndScroll flag elsewhere
-        // in this file we make sure to set it for this scenario.
-        MOZ_ASSERT(cs->first == scrollId);
-        ancestorIds.first = Nothing();
-        ancestorIds.second = cs->second;
-      }
-    }
-  }
-  // At most one of the ancestor pair should be defined here, and the one that
-  // is defined will be the parent clip for the new clip that we're defining.
-  MOZ_ASSERT(!(ancestorIds.first && ancestorIds.second));
-
-  LayoutDeviceRect clip = LayoutDeviceRect::FromAppUnits(
-      aChain->mClip.GetClipRect(), aAppUnitsPerDevPixel);
-  nsTArray<wr::ComplexClipRegion> wrRoundedRects;
-  aChain->mClip.ToComplexClipRegions(aAppUnitsPerDevPixel, aSc, wrRoundedRects);
-
-  // Define the clip
-  wr::WrClipId clipId = mBuilder->DefineClip(
-      ancestorIds.first, ancestorIds.second,
-      wr::ToRoundedLayoutRect(clip), &wrRoundedRects);
-  if (!mBuilder->HasExtraClip()) {
-    mCacheStack.back()[aChain] = clipId;
-  }
-
-  ids.second = Some(clipId);
-  return ids;
-}
-
-std::pair<Maybe<wr::WrScrollId>, Maybe<wr::WrClipId>>
-ScrollingLayersHelper::RecurseAndDefineAsr(nsDisplayItem* aItem,
-                                           const ActiveScrolledRoot* aAsr,
-                                           const DisplayItemClipChain* aChain,
-                                           int32_t aAppUnitsPerDevPixel,
-                                           const StackingContextHelper& aSc)
-{
-  MOZ_ASSERT(aAsr);
-
-  // This will hold our return value
-  std::pair<Maybe<wr::WrScrollId>, Maybe<wr::WrClipId>> ids;
-
-  FrameMetrics::ViewID viewId = aAsr->GetViewId();
-  if (auto scrollId = mBuilder->GetScrollIdForDefinedScrollLayer(viewId)) {
-    // If we've already defined this scroll layer before, we can early-exit
-    ids.first = scrollId;
-    if (aChain) {
-      if (mBuilder->HasExtraClip()) {
-        ids.second = mBuilder->GetCacheOverride(aChain);
-      } else {
-        const ClipIdMap& cache = mCacheStack.back();
-        auto it = cache.find(aChain);
-        // If |it == cache.end()| here then we have run into a case where the
-        // scroll layer was previously defined with a specific parent clip, and
-        // now here it has a different parent clip. Gecko can create display
-        // lists like this because it treats the ASR chain and clipping chain
-        // more independently, but we can't yet represent this in WR. This is
-        // tracked by bug 1409442. For now we'll just leave ids.second as
-        // Nothing() which will effectively ignore the clip |aChain|. Once WR
-        // supports multiple ancestors on a scroll layer we can deal with this
-        // better. The layout/reftests/text/wordwrap-08.html has a Text display
-        // item that exercises this case.
-        if (it != cache.end()) {
-          ids.second = Some(it->second);
-        }
-      }
-    }
-    return ids;
-  }
-
-  // If not, recurse to ensure all the ancestors are defined
-  auto ancestorIds = DefineClipChain(
-      aItem, aAsr->mParent, aChain, aAppUnitsPerDevPixel, aSc);
-  ids = ancestorIds;
-
-  Maybe<ScrollMetadata> metadata = aAsr->mScrollableFrame->ComputeScrollMetadata(
-      mManager, aItem->ReferenceFrame(), ContainerLayerParameters(), nullptr);
-  MOZ_ASSERT(metadata);
-  FrameMetrics& metrics = metadata->GetMetrics();
-
-  if (!metrics.IsScrollable()) {
-    // This item in the chain is a no-op, skip over it
-    return ids;
-  }
-
-  // Now we need to figure out whether the new clip we're defining should be
-  // a child of aChain, or of aAsr->mParent, if we have both as a possibility.
-  if (ancestorIds.first && ancestorIds.second) {
-    MOZ_ASSERT(aAsr->mParent); // because ancestorIds.first
-    MOZ_ASSERT(aChain); // because ancestorIds.second
-    if (aChain->mASR && aChain->mASR == aAsr->mParent) {
-      // aChain is scrolled by aAsr's parent, so we should use aChain as the
-      // ancestor when defining the aAsr scroll layer.
-      ancestorIds.first = Nothing();
-    } else {
-      // This scenario never seems to occur in practice, but if it did it would
-      // mean that aChain is scrolled by one of aAsr's ancestors beyond the
-      // parent, in which case we should use aAsr->mParent as the ancestor
-      // when defining the aAsr scroll layer.
-      ancestorIds.second = Nothing();
-    }
-  }
-  // At most one of the ancestor pair should be defined here, and the one that
-  // is defined will be the parent clip for the new scrollframe that we're
-  // defining.
-  MOZ_ASSERT(!(ancestorIds.first && ancestorIds.second));
-
-  LayoutDeviceRect contentRect =
-      metrics.GetExpandedScrollableRect() * metrics.GetDevPixelsPerCSSPixel();
-  // TODO: check coordinate systems are sane here
-  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
-  // is, both of them should be relative to the stacking context `aSc`.
-  // 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());
-
-  auto scrollId = mBuilder->DefineScrollLayer(viewId,
-      ancestorIds.first,
-      ancestorIds.second,
-      wr::ToRoundedLayoutRect(contentRect),
-      wr::ToRoundedLayoutRect(clipBounds));
-
-  ids.first = Some(scrollId);
-  return ids;
-}
-
-const DisplayItemClipChain*
-ScrollingLayersHelper::ExtendChain(const DisplayItemClipChain* aClip)
-{
-  // The intent of this function is to handle Gecko display list scenarios
-  // like so:
-  // nsDisplayFixedPosition with clip chain A -> B -> nullptr
-  //   nsDisplayBackgroundColor with clip chain B -> nullptr
-  //
-  // The specific types are not relevant, but the important part is that there
-  // is a display item whose clip chain is a subchain of the enclosing display
-  // item.
-  //
-  // The semantics of the gecko display items means that the two clip chains
-  // should be intersected for the child display item; because one clip chain
-  // is a subset of the other the intersection comes out to be clip chain from
-  // the parent.
-  // However, WebRender doesn't let us (yet) intersect clip chains, so one of
-  // the jobs of ScrollingLayersHelper is to generate as-good-as-possible clip
-  // chains by merging the necessary clips into a new clip chain. In the example
-  // above, we really want the nsDisplayBackgroundColor to use the clip chain
-  // from A rather than from B in order to get the right clips, and this
-  // function "extends" an input of |B| and returns |A|.
-
-  if (!aClip) {
-    return aClip;
-  }
-  // mItemClipStack has the clips that we pushed for ancestor display items.
-  size_t clipDepth = mItemClipStack.size();
-  MOZ_ASSERT(clipDepth > 0);
-  while (--clipDepth > 0) {
-    const DisplayItemClipChain* enclosingClip = mItemClipStack[clipDepth - 1].mChain;
-    if (!enclosingClip) {
-      // This is a special case; if an item has a nullptr clipchain it basically
-      // inherits the clipchain from its ancestor, so let's skip to that.
-      continue;
-    }
-    if (aClip == enclosingClip) {
-      // The ancestor clip chain is the same as our item's clip chain, so
-      // we're done. Note that because this function will have run on the
-      // ancestor as well, we can be assured via induction that there is no
-      // ancestor beyond this one that has a longer superset-clipchain.
-      return aClip;
-    }
-    const ClipIdMap& cache = mCacheStack.back();
-    if (cache.find(enclosingClip) == cache.end()) {
-      // The ancestor clip chain isn't in our clip cache, which means there
-      // must be a reference frame between the ancestor item and this item.
-      // Therefore we cannot use the enclosing clip, so let's abort
-      return aClip;
-    }
-    for (const DisplayItemClipChain* i = enclosingClip->mParent; i; i = i->mParent) {
-      if (i == aClip) {
-        // aClip is contained inside the enclosingClip clipchain. Since the
-        // enclosingClip also applies to the item we're currently processing,
-        // we should use that as it is a better approximation to the real clip
-        // set that applies to the item.
-        SLH_LOG("extending clip %p to %p\n", aClip, enclosingClip);
-        return enclosingClip;
-      }
-    }
-    break;
-  }
-  return aClip;
-}
-
-Maybe<ScrollingLayersHelper::ClipAndScroll>
-ScrollingLayersHelper::EnclosingClipAndScroll() const
-{
-  for (auto it = mItemClipStack.rbegin(); it != mItemClipStack.rend(); it++) {
-    if (it->mClipAndScroll) {
-      return it->mClipAndScroll;
-    }
-    // If an entry in the stack pushed a single clip or scroll without pushing
-    // a mClipAndScroll, we abort because we are effectively no longer inside
-    // a ClipAndScroll
-    if (it->mClipId || it->mScrollId) {
-      break;
-    }
-  }
-  return Nothing();
-}
-
-ScrollingLayersHelper::~ScrollingLayersHelper()
-{
-  MOZ_ASSERT(!mBuilder);
-  MOZ_ASSERT(mCacheStack.empty());
-  MOZ_ASSERT(mItemClipStack.empty());
-}
-
-ScrollingLayersHelper::ItemClips::ItemClips(const ActiveScrolledRoot* aAsr,
-                                            const DisplayItemClipChain* aChain)
-  : mAsr(aAsr)
-  , mChain(aChain)
-{
-}
-
-void
-ScrollingLayersHelper::ItemClips::Apply(wr::DisplayListBuilder* aBuilder)
-{
-  if (mScrollId) {
-    aBuilder->PushScrollLayer(mScrollId.ref());
-  }
-  if (mClipId) {
-    aBuilder->PushClip(mClipId.ref());
-  }
-  if (mClipAndScroll) {
-    aBuilder->PushClipAndScrollInfo(mClipAndScroll->first,
-                                    mClipAndScroll->second.ptrOr(nullptr));
-  }
-}
-
-void
-ScrollingLayersHelper::ItemClips::Unapply(wr::DisplayListBuilder* aBuilder)
-{
-  if (mClipAndScroll) {
-    aBuilder->PopClipAndScrollInfo();
-  }
-  if (mClipId) {
-    aBuilder->PopClip();
-  }
-  if (mScrollId) {
-    aBuilder->PopScrollLayer();
-  }
-}
-
-bool
-ScrollingLayersHelper::ItemClips::HasSameInputs(const ItemClips& aOther)
-{
-  return mAsr == aOther.mAsr &&
-         mChain == aOther.mChain;
-}
-
-} // namespace layers
-} // namespace mozilla
deleted file mode 100644
--- a/gfx/layers/wr/ScrollingLayersHelper.h
+++ /dev/null
@@ -1,113 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
-/* vim: set ts=8 sts=2 et sw=2 tw=80: */
-/* This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
-
-#ifndef GFX_SCROLLINGLAYERSHELPER_H
-#define GFX_SCROLLINGLAYERSHELPER_H
-
-#include <unordered_map>
-
-#include "mozilla/Attributes.h"
-#include "mozilla/webrender/WebRenderAPI.h"
-#include "FrameMetrics.h"
-
-class nsDisplayItem;
-
-namespace mozilla {
-
-struct ActiveScrolledRoot;
-struct DisplayItemClipChain;
-
-namespace wr {
-class DisplayListBuilder;
-}
-
-namespace layers {
-
-struct FrameMetrics;
-class StackingContextHelper;
-class WebRenderLayerManager;
-
-class ScrollingLayersHelper
-{
-public:
-  ScrollingLayersHelper();
-
-  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);
-  ~ScrollingLayersHelper();
-
-private:
-  typedef std::pair<wr::WrScrollId, Maybe<wr::WrClipId>> ClipAndScroll;
-
-  std::pair<Maybe<wr::WrScrollId>, Maybe<wr::WrClipId>>
-  DefineClipChain(nsDisplayItem* aItem,
-                  const ActiveScrolledRoot* aAsr,
-                  const DisplayItemClipChain* aChain,
-                  int32_t aAppUnitsPerDevPixel,
-                  const StackingContextHelper& aStackingContext);
-
-  std::pair<Maybe<wr::WrScrollId>, Maybe<wr::WrClipId>>
-  RecurseAndDefineClip(nsDisplayItem* aItem,
-                       const ActiveScrolledRoot* aAsr,
-                       const DisplayItemClipChain* aChain,
-                       int32_t aAppUnitsPerDevPixel,
-                       const StackingContextHelper& aSc);
-
-  std::pair<Maybe<wr::WrScrollId>, Maybe<wr::WrClipId>>
-  RecurseAndDefineAsr(nsDisplayItem* aItem,
-                      const ActiveScrolledRoot* aAsr,
-                      const DisplayItemClipChain* aChain,
-                      int32_t aAppUnitsPerDevPixel,
-                      const StackingContextHelper& aSc);
-
-  const DisplayItemClipChain* ExtendChain(const DisplayItemClipChain* aClip);
-  Maybe<ClipAndScroll> EnclosingClipAndScroll() const;
-
-  typedef std::unordered_map<const DisplayItemClipChain*, wr::WrClipId> ClipIdMap;
-
-  WebRenderLayerManager* MOZ_NON_OWNING_REF mManager;
-  wr::DisplayListBuilder* mBuilder;
-  // Stack of clip caches. There is one entry in the stack for each reference
-  // frame that is currently pushed in WR (a reference frame is a stacking
-  // context with a non-identity transform). Each entry contains a map that
-  // maps gecko DisplayItemClipChain objects to webrender WrClipIds, which
-  // allows us to avoid redefining identical clips in WR. We need to keep a
-  // separate cache per reference frame because the DisplayItemClipChain items
-  // themselves get deduplicated without regard to reference frames, but on the
-  // WR side we need to create different clips if they are in different
-  // reference frames.
-  std::vector<ClipIdMap> mCacheStack;
-
-  struct ItemClips {
-    ItemClips(const ActiveScrolledRoot* aAsr,
-              const DisplayItemClipChain* aChain);
-
-    const ActiveScrolledRoot* mAsr;
-    const DisplayItemClipChain* mChain;
-
-    Maybe<wr::WrScrollId> mScrollId;
-    Maybe<wr::WrClipId> mClipId;
-    Maybe<ClipAndScroll> mClipAndScroll;
-
-    void Apply(wr::DisplayListBuilder* aBuilder);
-    void Unapply(wr::DisplayListBuilder* aBuilder);
-    bool HasSameInputs(const ItemClips& aOther);
-  };
-
-  std::vector<ItemClips> mItemClipStack;
-};
-
-} // namespace layers
-} // namespace mozilla
-
-#endif
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -69,17 +69,18 @@ StackingContextHelper::StackingContextHe
                                 aPerspectivePtr,
                                 wr::ToMixBlendMode(aMixBlendMode),
                                 aFilters,
                                 aBackfaceVisible,
                                 rasterSpace);
 
   mAffectsClipPositioning =
       (aTransformPtr && !aTransformPtr->IsIdentity()) ||
-      (aBounds.TopLeft() != LayoutDevicePoint());
+      (aBounds.TopLeft() != LayoutDevicePoint()) ||
+      (aAnimation && aAnimation->effect_type == wr::WrAnimationType::Transform);
 }
 
 StackingContextHelper::~StackingContextHelper()
 {
   if (mBuilder) {
     mBuilder->PopStackingContext();
   }
 }
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -5,21 +5,21 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderCommandBuilder.h"
 
 #include "BasicLayers.h"
 #include "mozilla/AutoRestore.h"
 #include "mozilla/gfx/2D.h"
 #include "mozilla/gfx/Types.h"
+#include "mozilla/layers/ClipManager.h"
 #include "mozilla/layers/ImageClient.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
-#include "mozilla/layers/ScrollingLayersHelper.h"
 #include "mozilla/layers/SharedSurfacesChild.h"
 #include "mozilla/layers/SourceSurfaceSharedData.h"
 #include "mozilla/layers/StackingContextHelper.h"
 #include "mozilla/layers/UpdateImageHelper.h"
 #include "mozilla/layers/WebRenderDrawEventRecorder.h"
 #include "UnitTransforms.h"
 #include "gfxEnv.h"
 #include "nsDisplayListInvalidation.h"
@@ -198,24 +198,24 @@ TakeExternalSurfaces(WebRenderDrawEventR
     auto sharedSurface = static_cast<SourceSurfaceSharedData*>(surface.get());
     SharedSurfacesChild::Share(sharedSurface, aManager, aResources, key);
   }
 }
 
 struct DIGroup;
 struct Grouper
 {
-  explicit Grouper(ScrollingLayersHelper& aScrollingHelper)
-   : mScrollingHelper(aScrollingHelper)
+  explicit Grouper(ClipManager& aClipManager)
+   : mClipManager(aClipManager)
   {}
 
   int32_t mAppUnitsPerDevPixel;
   std::vector<nsDisplayItem*> mItemStack;
   nsDisplayListBuilder* mDisplayListBuilder;
-  ScrollingLayersHelper& mScrollingHelper;
+  ClipManager& mClipManager;
   Matrix mTransform;
 
   // Paint the list of aChildren display items.
   void PaintContainerItem(DIGroup* aGroup, nsDisplayItem* aItem, const IntRect& aItemBounds,
                           nsDisplayList* aChildren, gfxContext* aContext,
                           WebRenderDrawEventRecorder* aRecorder);
 
   // Builds groups of display items split based on 'layer activity'
@@ -911,17 +911,17 @@ Grouper::ConstructGroups(WebRenderComman
   DIGroup* currentGroup = aGroup;
 
   nsDisplayItem* item = aList->GetBottom();
   nsDisplayItem* startOfCurrentGroup = item;
   while (item) {
     nsDisplayList* children = item->GetChildren();
     if (IsItemProbablyActive(item, mDisplayListBuilder)) {
       currentGroup->EndGroup(aCommandBuilder->mManager, aBuilder, aResources, this, startOfCurrentGroup, item);
-      mScrollingHelper.BeginItem(item, aSc);
+      mClipManager.BeginItem(item, aSc);
       sIndent++;
       // Note: this call to CreateWebRenderCommands can recurse back into
       // this function.
       bool createdWRCommands =
         item->CreateWebRenderCommands(aBuilder, aResources, aSc, aCommandBuilder->mManager,
                                       mDisplayListBuilder);
       sIndent--;
       MOZ_RELEASE_ASSERT(createdWRCommands, "active transforms should always succeed at creating WebRender commands");
@@ -1044,18 +1044,18 @@ WebRenderCommandBuilder::DoGroupingForDi
                                                   const StackingContextHelper& aSc,
                                                   wr::DisplayListBuilder& aBuilder,
                                                   wr::IpcResourceUpdateQueue& aResources)
 {
   if (!aList->GetBottom()) {
     return;
   }
 
-  mScrollingHelper.BeginList(aSc);
-  Grouper g(mScrollingHelper);
+  mClipManager.BeginList(aSc);
+  Grouper g(mClipManager);
   int32_t appUnitsPerDevPixel = aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   GP("DoGroupingForDisplayList\n");
 
   g.mDisplayListBuilder = aDisplayListBuilder;
   RefPtr<WebRenderGroupData> groupData = CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
   bool snapped;
   nsRect groupBounds = aWrappingItem->GetBounds(aDisplayListBuilder, &snapped);
   DIGroup& group = groupData->mSubGroup;
@@ -1087,17 +1087,17 @@ WebRenderCommandBuilder::DoGroupingForDi
   g.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
   g.mTransform = Matrix::Scaling(scale.width, scale.height);
   group.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
   group.mGroupBounds = groupBounds;
   group.mScale = scale;
   group.mLayerBounds = LayerIntRect::FromUnknownRect(group.mGroupBounds.ScaleToOutsidePixels(scale.width, scale.height, group.mAppUnitsPerDevPixel));
   group.mAnimatedGeometryRootOrigin = group.mGroupBounds.TopLeft();
   g.ConstructGroups(this, aBuilder, aResources, &group, aList, aSc);
-  mScrollingHelper.EndList(aSc);
+  mClipManager.EndList(aSc);
 }
 
 void
 WebRenderCommandBuilder::Destroy()
 {
   mLastCanvasDatas.Clear();
   ClearCachedResources();
 }
@@ -1130,17 +1130,17 @@ WebRenderCommandBuilder::BuildWebRenderC
                                                 wr::LayoutSize& aContentSize,
                                                 const nsTArray<wr::WrFilterOp>& aFilters)
 {
   StackingContextHelper sc;
   aScrollData = WebRenderScrollData(mManager);
   MOZ_ASSERT(mLayerScrollData.empty());
   mLastCanvasDatas.Clear();
   mLastAsr = nullptr;
-  mScrollingHelper.BeginBuild(mManager, aBuilder);
+  mClipManager.BeginBuild(mManager, aBuilder);
 
   {
     StackingContextHelper pageRootSc(sc, aBuilder, aFilters);
     CreateWebRenderCommandsFromDisplayList(aDisplayList, nullptr, aDisplayListBuilder,
                                            pageRootSc, aBuilder, aResourceUpdates);
   }
 
   // Make a "root" layer data that has everything else as descendants
@@ -1155,17 +1155,17 @@ WebRenderCommandBuilder::BuildWebRenderC
   }
   // Append the WebRenderLayerScrollData items into WebRenderScrollData
   // in reverse order, from topmost to bottommost. This is in keeping with
   // the semantics of WebRenderScrollData.
   for (auto i = mLayerScrollData.crbegin(); i != mLayerScrollData.crend(); i++) {
     aScrollData.AddLayerData(*i);
   }
   mLayerScrollData.clear();
-  mScrollingHelper.EndBuild();
+  mClipManager.EndBuild();
 
   // Remove the user data those are not displayed on the screen and
   // also reset the data to unused for next transaction.
   RemoveUnusedAndResetWebRenderUserData();
 }
 
 void
 WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* aDisplayList,
@@ -1177,17 +1177,17 @@ WebRenderCommandBuilder::CreateWebRender
 {
   if (mDoGrouping) {
     MOZ_RELEASE_ASSERT(aWrappingItem, "Only the root list should have a null wrapping item, and mDoGrouping should never be true for the root list.");
     GP("actually entering the grouping code\n");
     DoGroupingForDisplayList(aDisplayList, aWrappingItem, aDisplayListBuilder, aSc, aBuilder, aResources);
     return;
   }
 
-  mScrollingHelper.BeginList(aSc);
+  mClipManager.BeginList(aSc);
 
   bool apzEnabled = mManager->AsyncPanZoomEnabled();
   EventRegions eventRegions;
 
   FlattenedDisplayItemIterator iter(aDisplayListBuilder, aDisplayList);
   while (nsDisplayItem* i = iter.GetNext()) {
     nsDisplayItem* item = i;
     DisplayItemType itemType = item->GetType();
@@ -1282,17 +1282,17 @@ WebRenderCommandBuilder::CreateWebRender
       // 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);
       }
     }
 
-    mScrollingHelper.BeginItem(item, aSc);
+    mClipManager.BeginItem(item, aSc);
 
     if (itemType != DisplayItemType::TYPE_LAYER_EVENT_REGIONS) {
       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.
@@ -1347,17 +1347,30 @@ WebRenderCommandBuilder::CreateWebRender
   // the most recently created layer data will have been created by an item
   // with matching ASRs.
   if (!eventRegions.IsEmpty()) {
     MOZ_ASSERT(apzEnabled);
     MOZ_ASSERT(!mLayerScrollData.empty());
     mLayerScrollData.back().AddEventRegions(eventRegions);
   }
 
-  mScrollingHelper.EndList(aSc);
+  mClipManager.EndList(aSc);
+}
+
+void
+WebRenderCommandBuilder::PushOverrideForASR(const ActiveScrolledRoot* aASR,
+                                            const Maybe<wr::WrClipId>& aClipId)
+{
+  mClipManager.PushOverrideForASR(aASR, aClipId);
+}
+
+void
+WebRenderCommandBuilder::PopOverrideForASR(const ActiveScrolledRoot* aASR)
+{
+  mClipManager.PopOverrideForASR(aASR);
 }
 
 Maybe<wr::ImageKey>
 WebRenderCommandBuilder::CreateImageKey(nsDisplayItem* aItem,
                                         ImageContainer* aContainer,
                                         mozilla::wr::DisplayListBuilder& aBuilder,
                                         mozilla::wr::IpcResourceUpdateQueue& aResources,
                                         const StackingContextHelper& aSc,
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -3,17 +3,17 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_WEBRENDERCOMMANDBUILDER_H
 #define GFX_WEBRENDERCOMMANDBUILDER_H
 
 #include "mozilla/webrender/WebRenderAPI.h"
-#include "mozilla/layers/ScrollingLayersHelper.h"
+#include "mozilla/layers/ClipManager.h"
 #include "mozilla/layers/WebRenderMessages.h"
 #include "mozilla/layers/WebRenderScrollData.h"
 #include "mozilla/layers/WebRenderUserData.h"
 #include "nsDisplayList.h"
 #include "nsIFrame.h"
 
 namespace mozilla {
 
@@ -50,16 +50,20 @@ public:
   void BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                               wr::IpcResourceUpdateQueue& aResourceUpdates,
                               nsDisplayList* aDisplayList,
                               nsDisplayListBuilder* aDisplayListBuilder,
                               WebRenderScrollData& aScrollData,
                               wr::LayoutSize& aContentSize,
                               const nsTArray<wr::WrFilterOp>& aFilters);
 
+  void PushOverrideForASR(const ActiveScrolledRoot* aASR,
+                          const Maybe<wr::WrClipId>& aClipId);
+  void PopOverrideForASR(const ActiveScrolledRoot* aASR);
+
   Maybe<wr::ImageKey> CreateImageKey(nsDisplayItem* aItem,
                                      ImageContainer* aContainer,
                                      mozilla::wr::DisplayListBuilder& aBuilder,
                                      mozilla::wr::IpcResourceUpdateQueue& aResources,
                                      const StackingContextHelper& aSc,
                                      gfx::IntSize& aSize,
                                      const Maybe<LayoutDeviceRect>& aAsyncImageBounds);
 
@@ -154,18 +158,19 @@ public:
     if (T::Type() == WebRenderUserData::UserDataType::eCanvas) {
       mLastCanvasDatas.PutEntry(data->AsCanvasData());
     }
     RefPtr<T> res = static_cast<T*>(data.get());
     return res.forget();
   }
 
   WebRenderLayerManager* mManager;
+
 private:
-  ScrollingLayersHelper mScrollingHelper;
+  ClipManager mClipManager;
 
   // We use this as a temporary data structure while building the mScrollData
   // inside a layers-free transaction.
   std::vector<WebRenderLayerScrollData> mLayerScrollData;
   // We use this as a temporary data structure to track the current display
   // item's ASR as we recurse in CreateWebRenderCommandsFromDisplayList. We
   // need this so that WebRenderLayerScrollData items that deeper in the
   // tree don't duplicate scroll metadata that their ancestors already have.
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -1,17 +1,16 @@
 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "WebRenderAPI.h"
 
-#include "DisplayItemClipChain.h"
 #include "gfxPrefs.h"
 #include "LayersLogging.h"
 #include "mozilla/webrender/RendererOGL.h"
 #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"
@@ -800,122 +799,69 @@ DisplayListBuilder::PushStackingContext(
 
 void
 DisplayListBuilder::PopStackingContext()
 {
   WRDL_LOG("PopStackingContext\n", mWrState);
   wr_dp_pop_stacking_context(mWrState);
 }
 
+wr::WrClipChainId
+DisplayListBuilder::DefineClipChain(const Maybe<wr::WrClipChainId>& aParent,
+                                    const nsTArray<wr::WrClipId>& aClips)
+{
+  nsTArray<size_t> clipIds;
+  for (wr::WrClipId id : aClips) {
+    clipIds.AppendElement(id.id);
+  }
+  uint64_t clipchainId = wr_dp_define_clipchain(mWrState,
+      aParent ? &(aParent->id) : nullptr,
+      clipIds.Elements(), clipIds.Length());
+  WRDL_LOG("DefineClipChain id=%" PRIu64 " p=%s clips=%zu\n", mWrState,
+      clipchainId,
+      aParent ? Stringify(aParent->id).c_str() : "(nil)",
+      clipIds.Length());
+  return wr::WrClipChainId{ clipchainId };
+}
+
 wr::WrClipId
-DisplayListBuilder::DefineClip(const Maybe<wr::WrScrollId>& aAncestorScrollId,
-                               const Maybe<wr::WrClipId>& aAncestorClipId,
+DisplayListBuilder::DefineClip(const Maybe<wr::WrClipId>& aParentId,
                                const wr::LayoutRect& aClipRect,
                                const nsTArray<wr::ComplexClipRegion>* aComplex,
                                const wr::WrImageMask* aMask)
 {
-  const size_t* parentId = nullptr;
-  if (aAncestorScrollId) {
-    parentId = &(aAncestorScrollId.ref().id);
-  } else if (aAncestorClipId) {
-    parentId = &(aAncestorClipId.ref().id);
-  }
   size_t clip_id = wr_dp_define_clip(mWrState,
-      parentId,
+      aParentId ? &(aParentId->id) : nullptr,
       aClipRect,
       aComplex ? aComplex->Elements() : nullptr,
       aComplex ? aComplex->Length() : 0,
       aMask);
-  WRDL_LOG("DefineClip id=%zu as=%s ac=%s r=%s m=%p b=%s complex=%zu\n", mWrState,
-      clip_id,
-      aAncestorScrollId ? Stringify(aAncestorScrollId.ref().id).c_str() : "(nil)",
-      aAncestorClipId ? Stringify(aAncestorClipId.ref().id).c_str() : "(nil)",
+  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)",
       Stringify(aClipRect).c_str(), aMask,
       aMask ? Stringify(aMask->rect).c_str() : "none",
       aComplex ? aComplex->Length() : 0);
   return wr::WrClipId { clip_id };
 }
 
 void
-DisplayListBuilder::PushClip(const wr::WrClipId& aClipId,
-                             const DisplayItemClipChain* aParent)
+DisplayListBuilder::PushClip(const wr::WrClipId& aClipId)
 {
-  wr_dp_push_clip(mWrState, aClipId.id);
   WRDL_LOG("PushClip id=%zu\n", mWrState, aClipId.id);
-  if (!aParent) {
-    mClipStack.push_back(wr::ScrollOrClipId(aClipId));
-  } else {
-    PushCacheOverride(aParent, aClipId);
-  }
-}
-
-void
-DisplayListBuilder::PopClip(const DisplayItemClipChain* aParent)
-{
-  WRDL_LOG("PopClip\n", mWrState);
-  if (!aParent) {
-    MOZ_ASSERT(mClipStack.back().is<wr::WrClipId>());
-    mClipStack.pop_back();
-  } else {
-    PopCacheOverride(aParent);
-  }
-  wr_dp_pop_clip(mWrState);
+  wr_dp_push_clip(mWrState, aClipId.id);
 }
 
 void
-DisplayListBuilder::PushCacheOverride(const DisplayItemClipChain* aParent,
-                                      const wr::WrClipId& aClipId)
+DisplayListBuilder::PopClip()
 {
-  // We need to walk the entire clip chain from aParent up and install aClipId
-  // as an override for all of them. This is so that nested display items end up
-  // with the correct parent clip regardless of which outside clip their clip
-  // chains are attached to. Example:
-  // nsDisplayStickyPosition with clipChain D -> C -> B -> A -> nullptr
-  //   nsDisplayItem with clipChain F -> E -> B -> A -> nullptr
-  // In this case the sticky clip that is generated by the sticky display item
-  // is defined as a child of D, which is a child of C and so on. When we go
-  // to define E for the nested display item, we want to make sure that it
-  // is defined as a child of sticky clip, regardless of which of {D, C, B, A}
-  // it has as a parent. {D, C, B, A} are what I refer to as the "outside clips"
-  // because they are "outside" the sticky clip, and if we define E as a child
-  // of any of those clips directly, then we end up losing the sticky clip from
-  // the WR clip chain of the nested item. This is why we install cache
-  // overrides for all of them so that when we walk the nested item's clip chain
-  // from E to B we discover that really we want to use the sticky clip as E's
-  // parent.
-  for (const DisplayItemClipChain* i = aParent; i; i = i->mParent) {
-    auto it = mCacheOverride.insert({ i, std::vector<wr::WrClipId>() });
-    it.first->second.push_back(aClipId);
-    WRDL_LOG("Pushing override %p -> %zu\n", mWrState, i, aClipId.id);
-  }
+  WRDL_LOG("PopClip\n", mWrState);
+  wr_dp_pop_clip(mWrState);
 }
 
-void
-DisplayListBuilder::PopCacheOverride(const DisplayItemClipChain* aParent)
-{
-  for (const DisplayItemClipChain* i = aParent; i; i = i->mParent) {
-    auto it = mCacheOverride.find(i);
-    MOZ_ASSERT(it != mCacheOverride.end());
-    MOZ_ASSERT(!(it->second.empty()));
-    WRDL_LOG("Popping override %p -> %zu\n", mWrState, i, it->second.back().id);
-    it->second.pop_back();
-    if (it->second.empty()) {
-      mCacheOverride.erase(it);
-    }
-  }
-}
-
-Maybe<wr::WrClipId>
-DisplayListBuilder::GetCacheOverride(const DisplayItemClipChain* aParent)
-{
-  auto it = mCacheOverride.find(aParent);
-  return it == mCacheOverride.end() ? Nothing() : Some(it->second.back());
-}
-
-wr::WrStickyId
+wr::WrClipId
 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)
@@ -928,127 +874,77 @@ DisplayListBuilder::DefineStickyFrame(co
       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::WrStickyId { id };
+  return wr::WrClipId { id };
 }
 
-void
-DisplayListBuilder::PushStickyFrame(const wr::WrStickyId& aStickyId,
-                                    const DisplayItemClipChain* aParent)
-{
-  wr_dp_push_clip(mWrState, aStickyId.id);
-  WRDL_LOG("PushSticky id=%zu\n", mWrState, aStickyId.id);
-  // inside WR, a sticky id is just a regular clip id. so we can do some
-  // handwaving here and turn the WrStickyId into a WrclipId and treat it
-  // like any other out-of-band clip.
-  wr::WrClipId stickyIdAsClipId;
-  stickyIdAsClipId.id = aStickyId.id;
-  PushCacheOverride(aParent, stickyIdAsClipId);
-}
-
-void
-DisplayListBuilder::PopStickyFrame(const DisplayItemClipChain* aParent)
-{
-  WRDL_LOG("PopSticky\n", mWrState);
-  PopCacheOverride(aParent);
-  wr_dp_pop_clip(mWrState);
-}
-
-Maybe<wr::WrScrollId>
+Maybe<wr::WrClipId>
 DisplayListBuilder::GetScrollIdForDefinedScrollLayer(layers::FrameMetrics::ViewID aViewId) const
 {
   if (aViewId == layers::FrameMetrics::NULL_SCROLL_ID) {
-    return Some(wr::WrScrollId::RootScrollNode());
+    return Some(wr::WrClipId::RootScrollNode());
   }
 
   auto it = mScrollIds.find(aViewId);
   if (it == mScrollIds.end()) {
     return Nothing();
   }
 
   return Some(it->second);
 }
 
-wr::WrScrollId
+wr::WrClipId
 DisplayListBuilder::DefineScrollLayer(const layers::FrameMetrics::ViewID& aViewId,
-                                      const Maybe<wr::WrScrollId>& aAncestorScrollId,
-                                      const Maybe<wr::WrClipId>& aAncestorClipId,
+                                      const Maybe<wr::WrClipId>& aParentId,
                                       const wr::LayoutRect& aContentRect,
                                       const wr::LayoutRect& aClipRect)
 {
-  const size_t* parentId = nullptr;
-  if (aAncestorScrollId) {
-    parentId = &(aAncestorScrollId.ref().id);
-  } else if (aAncestorClipId) {
-    parentId = &(aAncestorClipId.ref().id);
-  }
-  WRDL_LOG("DefineScrollLayer id=%" PRIu64 " as=%s ac=%s co=%s cl=%s\n", mWrState,
-      aViewId,
-      aAncestorScrollId ? Stringify(aAncestorScrollId.ref().id).c_str() : "(nil)",
-      aAncestorClipId ? Stringify(aAncestorClipId.ref().id).c_str() : "(nil)",
-      Stringify(aContentRect).c_str(), Stringify(aClipRect).c_str());
-
   auto it = mScrollIds.find(aViewId);
   if (it != mScrollIds.end()) {
     return it->second;
   }
 
   // We haven't defined aViewId before, so let's define it now.
   size_t numericScrollId = wr_dp_define_scroll_layer(
       mWrState,
       aViewId,
-      parentId,
+      aParentId ? &(aParentId->id) : nullptr,
       aContentRect,
       aClipRect);
-   auto wrScrollId = wr::WrScrollId { numericScrollId };
-   mScrollIds[aViewId] = wrScrollId;
-   return wrScrollId;
-}
 
-void
-DisplayListBuilder::PushScrollLayer(const wr::WrScrollId& aScrollId)
-{
-  WRDL_LOG("PushScrollLayer id=%zu\n", mWrState, aScrollId.id);
-  wr_dp_push_scroll_layer(mWrState, aScrollId.id);
-  mClipStack.push_back(wr::ScrollOrClipId(aScrollId));
+  WRDL_LOG("DefineScrollLayer id=%" PRIu64 "/%zu p=%s co=%s cl=%s\n", mWrState,
+      aViewId, numericScrollId,
+      aParentId ? Stringify(aParentId->id).c_str() : "(nil)",
+      Stringify(aContentRect).c_str(), Stringify(aClipRect).c_str());
+
+   auto clipId = wr::WrClipId { numericScrollId };
+   mScrollIds[aViewId] = clipId;
+   return clipId;
 }
 
 void
-DisplayListBuilder::PopScrollLayer()
-{
-  MOZ_ASSERT(mClipStack.back().is<wr::WrScrollId>());
-  WRDL_LOG("PopScrollLayer id=%zu\n", mWrState,
-      mClipStack.back().as<wr::WrScrollId>().id);
-  mClipStack.pop_back();
-  wr_dp_pop_scroll_layer(mWrState);
-}
-
-void
-DisplayListBuilder::PushClipAndScrollInfo(const wr::WrScrollId& aScrollId,
-                                          const wr::WrClipId* aClipId)
+DisplayListBuilder::PushClipAndScrollInfo(const wr::WrClipId& aScrollId,
+                                          const wr::WrClipChainId* aClipChainId)
 {
   WRDL_LOG("PushClipAndScroll s=%zu c=%s\n", mWrState, aScrollId.id,
-      aClipId ? Stringify(aClipId->id).c_str() : "none");
+      aClipChainId ? Stringify(aClipChainId->id).c_str() : "none");
   wr_dp_push_clip_and_scroll_info(mWrState, aScrollId.id,
-      aClipId ? &(aClipId->id) : nullptr);
-  mClipStack.push_back(wr::ScrollOrClipId(aScrollId));
+      aClipChainId ? &(aClipChainId->id) : nullptr);
 }
 
 void
 DisplayListBuilder::PopClipAndScrollInfo()
 {
-  MOZ_ASSERT(mClipStack.back().is<wr::WrScrollId>());
   WRDL_LOG("PopClipAndScroll\n", mWrState);
-  mClipStack.pop_back();
   wr_dp_pop_clip_and_scroll_info(mWrState);
 }
 
 void
 DisplayListBuilder::PushRect(const wr::LayoutRect& aBounds,
                              const wr::LayoutRect& aClip,
                              bool aIsBackfaceVisible,
                              const wr::ColorF& aColor)
@@ -1322,47 +1218,16 @@ DisplayListBuilder::PushBoxShadow(const 
                                   const wr::BoxShadowClipMode& aClipMode)
 {
   wr_dp_push_box_shadow(mWrState, aRect, aClip, aIsBackfaceVisible,
                         aBoxBounds, aOffset, aColor,
                         aBlurRadius, aSpreadRadius, aBorderRadius,
                         aClipMode);
 }
 
-Maybe<wr::WrClipId>
-DisplayListBuilder::TopmostClipId()
-{
-  for (auto it = mClipStack.crbegin(); it != mClipStack.crend(); it++) {
-    if (it->is<wr::WrClipId>()) {
-      return Some(it->as<wr::WrClipId>());
-    }
-  }
-  return Nothing();
-}
-
-wr::WrScrollId
-DisplayListBuilder::TopmostScrollId()
-{
-  for (auto it = mClipStack.crbegin(); it != mClipStack.crend(); it++) {
-    if (it->is<wr::WrScrollId>()) {
-      return it->as<wr::WrScrollId>();
-    }
-  }
-  return wr::WrScrollId::RootScrollNode();
-}
-
-bool
-DisplayListBuilder::TopmostIsClip()
-{
-  if (mClipStack.empty()) {
-    return false;
-  }
-  return mClipStack.back().is<wr::WrClipId>();
-}
-
 void
 DisplayListBuilder::SetHitTestInfo(const layers::FrameMetrics::ViewID& aScrollId,
                                    gfx::CompositorHitTestInfo aHitInfo)
 {
   static_assert(sizeof(gfx::CompositorHitTestInfo) == sizeof(uint16_t),
                 "CompositorHitTestInfo should be u16-sized");
   wr_set_item_tag(mWrState, aScrollId, static_cast<uint16_t>(aHitInfo));
 }
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -18,18 +18,16 @@
 #include "mozilla/webrender/webrender_ffi.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "FrameMetrics.h"
 #include "GLTypes.h"
 #include "Units.h"
 
 namespace mozilla {
 
-struct DisplayItemClipChain;
-
 namespace widget {
 class CompositorWidget;
 }
 
 namespace layers {
 class CompositorBridgeParent;
 class WebRenderBridgeParent;
 }
@@ -289,48 +287,43 @@ public:
                            wr::TransformStyle aTransformStyle,
                            const gfx::Matrix4x4* aPerspective,
                            const wr::MixBlendMode& aMixBlendMode,
                            const nsTArray<wr::WrFilterOp>& aFilters,
                            bool aIsBackfaceVisible,
                            const wr::GlyphRasterSpace& aRasterSpace);
   void PopStackingContext();
 
-  wr::WrClipId DefineClip(const Maybe<wr::WrScrollId>& aAncestorScrollId,
-                          const Maybe<wr::WrClipId>& aAncestorClipId,
+  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 nsTArray<wr::ComplexClipRegion>* aComplex = nullptr,
                           const wr::WrImageMask* aMask = nullptr);
-  void PushClip(const wr::WrClipId& aClipId, const DisplayItemClipChain* aParent = nullptr);
-  void PopClip(const DisplayItemClipChain* aParent = nullptr);
-  Maybe<wr::WrClipId> GetCacheOverride(const DisplayItemClipChain* aParent);
+  void PushClip(const wr::WrClipId& aClipId);
+  void PopClip();
 
-  wr::WrStickyId 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);
-  void PushStickyFrame(const wr::WrStickyId& aStickyId,
-                       const DisplayItemClipChain* aParent);
-  void PopStickyFrame(const DisplayItemClipChain* aParent);
+  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);
 
-  Maybe<wr::WrScrollId> GetScrollIdForDefinedScrollLayer(layers::FrameMetrics::ViewID aViewId) const;
-  wr::WrScrollId DefineScrollLayer(const layers::FrameMetrics::ViewID& aViewId,
-                                   const Maybe<wr::WrScrollId>& aAncestorScrollId,
-                                   const Maybe<wr::WrClipId>& aAncestorClipId,
-                                   const wr::LayoutRect& aContentRect, // TODO: We should work with strongly typed rects
-                                   const wr::LayoutRect& aClipRect);
-  void PushScrollLayer(const wr::WrScrollId& aScrollId);
-  void PopScrollLayer();
+  Maybe<wr::WrClipId> GetScrollIdForDefinedScrollLayer(layers::FrameMetrics::ViewID aViewId) const;
+  wr::WrClipId DefineScrollLayer(const layers::FrameMetrics::ViewID& aViewId,
+                                 const Maybe<wr::WrClipId>& aParentId,
+                                 const wr::LayoutRect& aContentRect, // TODO: We should work with strongly typed rects
+                                 const wr::LayoutRect& aClipRect);
 
-  void PushClipAndScrollInfo(const wr::WrScrollId& aScrollId,
-                             const wr::WrClipId* aClipId);
+  void PushClipAndScrollInfo(const wr::WrClipId& aScrollId,
+                             const wr::WrClipChainId* aClipChainId);
   void PopClipAndScrollInfo();
 
   void PushRect(const wr::LayoutRect& aBounds,
                 const wr::LayoutRect& aClip,
                 bool aIsBackfaceVisible,
                 const wr::ColorF& aColor);
 
   void PushClearRect(const wr::LayoutRect& aBounds);
@@ -465,69 +458,33 @@ public:
                      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);
 
-  // Returns the clip id that was most recently pushed with PushClip and that
-  // has not yet been popped with PopClip. Return Nothing() if the clip stack
-  // is empty.
-  Maybe<wr::WrClipId> TopmostClipId();
-  // Same as TopmostClipId() but for scroll layers.
-  wr::WrScrollId TopmostScrollId();
-  // If the topmost item on the stack is a clip or a scroll layer
-  bool TopmostIsClip();
-
   // Set the hit-test info to be used for all display items until the next call
   // to SetHitTestInfo or ClearHitTestInfo.
   void SetHitTestInfo(const layers::FrameMetrics::ViewID& aScrollId,
                       gfx::CompositorHitTestInfo aHitInfo);
   // Clears the hit-test info so that subsequent display items will not have it.
   void ClearHitTestInfo();
 
   // Try to avoid using this when possible.
   wr::WrState* Raw() { return mWrState; }
 
-  // Return true if the current clip stack has any extra clip.
-  bool HasExtraClip() { return !mCacheOverride.empty(); }
-
 protected:
-  void PushCacheOverride(const DisplayItemClipChain* aParent,
-                         const wr::WrClipId& aClipId);
-  void PopCacheOverride(const DisplayItemClipChain* aParent);
-
   wr::WrState* mWrState;
 
-  // Track the stack of clip ids and scroll layer ids that have been pushed
-  // (by PushClip and PushScrollLayer/PushClipAndScrollInfo, respectively) and
-  // haven't yet been popped.
-  std::vector<wr::ScrollOrClipId> mClipStack;
-
   // 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::FrameMetrics::ViewID, wr::WrScrollId> mScrollIds;
-
-  // A map that holds the cache overrides creates by "out of band" clips, i.e.
-  // clips that are generated by display items but that ScrollingLayersHelper
-  // doesn't know about. These are called "cache overrides" because while we're
-  // inside one of these clips, the WR clip stack is different from what
-  // ScrollingLayersHelper thinks it actually is (because of the out-of-band
-  // clip that was pushed onto the stack) and so ScrollingLayersHelper cannot
-  // use its clip cache as-is. Instead, any time ScrollingLayersHelper wants
-  // to define a new clip as a child of clip X, it should first check the
-  // cache overrides to see if there is an out-of-band clip Y that is already a
-  // child of X, and then define its clip as a child of Y instead. This map
-  // stores X -> ClipId of Y, which allows ScrollingLayersHelper to do the
-  // necessary lookup. Note that there theoretically might be multiple
-  // different "Y" clips which is why we need a vector.
-  std::unordered_map<const DisplayItemClipChain*, std::vector<wr::WrClipId>> mCacheOverride;
+  std::unordered_map<layers::FrameMetrics::ViewID, wr::WrClipId> mScrollIds;
 
   friend class WebRenderAPI;
 };
 
 Maybe<wr::ImageFormat>
 SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat);
 
 } // namespace wr
--- a/gfx/webrender_bindings/WebRenderTypes.cpp
+++ b/gfx/webrender_bindings/WebRenderTypes.cpp
@@ -27,14 +27,15 @@ Assign_WrVecU8(wr::WrVecU8& aVec, mozill
   aVec.data = aOther.mData;
   aVec.length = aOther.mLen;
   aVec.capacity = aOther.mCapacity;
   aOther.mData = nullptr;
   aOther.mLen = 0;
   aOther.mCapacity = 0;
 }
 
-WrScrollId WrScrollId::RootScrollNode() {
-  return WrScrollId { wr_root_scroll_node_id() };
+/*static*/ WrClipId
+WrClipId::RootScrollNode() {
+  return WrClipId { wr_root_scroll_node_id() };
 }
 
 } // namespace wr
 } // namespace mozilla
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -753,54 +753,52 @@ static inline wr::WrFilterOpType ToWrFil
   }
   MOZ_ASSERT_UNREACHABLE("Tried to convert unknown filter type.");
   return wr::WrFilterOpType::Grayscale;
 }
 
 // Corresponds to an "internal" webrender clip id. That is, a
 // ClipId::Clip(x,pipeline_id) maps to a WrClipId{x}. We use a struct wrapper
 // instead of a typedef so that this is a distinct type from ids generated
-// by scroll and position:sticky nodes  and the compiler will catch accidental
+// by scroll and position:sticky nodes and the compiler will catch accidental
 // conversions between them.
 struct WrClipId {
   size_t id;
 
   bool operator==(const WrClipId& other) const {
     return id == other.id;
   }
-};
 
-// Corresponds to a clip id for for a scroll frame in webrender. Similar
-// to WrClipId but a separate struct so we don't get them mixed up in C++.
-struct WrScrollId {
-  size_t id;
-
-  bool operator==(const WrScrollId& other) const {
-    return id == other.id;
+  bool operator!=(const WrClipId& other) const {
+    return !(*this == other);
   }
 
-  bool operator!=(const WrScrollId& other) const {
-    return id != other.id;
-  }
+  static WrClipId RootScrollNode();
 
-  static WrScrollId RootScrollNode();
+  // Helper struct that allows this class to be used as a key in
+  // std::unordered_map like so:
+  //   std::unordered_map<WrClipId, ValueType, WrClipId::HashFn> myMap;
+  struct HashFn {
+    std::size_t operator()(const WrClipId& aKey) const
+    {
+      return std::hash<size_t>{}(aKey.id);
+    }
+  };
 };
 
-// Corresponds to a clip id for a position:sticky clip in webrender. Similar
-// to WrClipId but a separate struct so we don't get them mixed up in C++.
-struct WrStickyId {
-  size_t id;
+// 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 WrStickyId& other) const {
+  bool operator==(const WrClipChainId& other) const {
     return id == other.id;
   }
 };
 
-typedef Variant<WrScrollId, WrClipId> ScrollOrClipId;
-
 enum class WebRenderError : int8_t {
   INITIALIZE = 0,
   MAKE_CURRENT,
   RENDER,
 
   Sentinel /* this must be last for serialization purposes. */
 };
 
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -1653,16 +1653,30 @@ pub extern "C" fn wr_dp_push_stacking_co
 
 #[no_mangle]
 pub extern "C" fn wr_dp_pop_stacking_context(state: &mut WrState) {
     debug_assert!(unsafe { !is_in_render_thread() });
     state.frame_builder.dl_builder.pop_stacking_context();
 }
 
 #[no_mangle]
+pub extern "C" fn wr_dp_define_clipchain(state: &mut WrState,
+                                         parent_clipchain_id: *const u64,
+                                         clips: *const usize,
+                                         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 clips_slice : Vec<ClipId> = make_slice(clips, clips_count).iter().map(|id| ClipId::Clip(*id, state.pipeline_id)).collect();
+    let clipchain_id = state.frame_builder.dl_builder.define_clip_chain(parent, clips_slice);
+    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 usize,
                                     clip_rect: LayoutRect,
                                     complex: *const ComplexClipRegion,
                                     complex_count: usize,
                                     mask: *const WrImageMask)
                                     -> usize {
     debug_assert!(unsafe { is_in_main_thread() });
@@ -1784,24 +1798,24 @@ pub extern "C" fn wr_dp_push_scroll_laye
 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: usize,
-                                                  clip_id: *const usize) {
+                                                  clip_chain_id: *const u64) {
     debug_assert!(unsafe { is_in_main_thread() });
 
-    let clip_id = unsafe { clip_id.as_ref() };
-    let info = if let Some(&cid) = clip_id {
+    let clip_chain_id = unsafe { clip_chain_id.as_ref() };
+    let info = if let Some(&ccid) = clip_chain_id {
         ClipAndScrollInfo::new(
             ClipId::Clip(scroll_id, state.pipeline_id),
-            ClipId::Clip(cid, state.pipeline_id))
+            ClipId::ClipChain(ClipChainId(ccid, state.pipeline_id)))
     } else {
         ClipAndScrollInfo::simple(
             ClipId::Clip(scroll_id, state.pipeline_id))
     };
     state.frame_builder.dl_builder.push_clip_and_scroll_info(info);
 }
 
 #[no_mangle]
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -1113,16 +1113,23 @@ uintptr_t wr_dp_define_clip(WrState *aSt
                             const uintptr_t *aParentId,
                             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 uintptr_t *aClips,
+                                uintptr_t aClipsCount)
+WR_FUNC;
+
+WR_INLINE
 uintptr_t wr_dp_define_scroll_layer(WrState *aState,
                                     uint64_t aScrollId,
                                     const uintptr_t *aParentId,
                                     LayoutRect aContentRect,
                                     LayoutRect aClipRect)
 WR_FUNC;
 
 WR_INLINE
@@ -1233,17 +1240,17 @@ WR_FUNC;
 WR_INLINE
 void wr_dp_push_clip(WrState *aState,
                      uintptr_t aClipId)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_clip_and_scroll_info(WrState *aState,
                                      uintptr_t aScrollId,
-                                     const uintptr_t *aClipId)
+                                     const uint64_t *aClipChainId)
 WR_FUNC;
 
 WR_INLINE
 void wr_dp_push_iframe(WrState *aState,
                        LayoutRect aRect,
                        bool aIsBackfaceVisible,
                        WrPipelineId aPipelineId)
 WR_FUNC;
--- a/layout/generic/nsBulletFrame.cpp
+++ b/layout/generic/nsBulletFrame.cpp
@@ -561,17 +561,17 @@ BulletRenderer::CreateWebRenderCommandsF
       return true;
     }
     case NS_STYLE_LIST_STYLE_DISC: {
       nsTArray<wr::ComplexClipRegion> clips;
       clips.AppendElement(wr::ToComplexClipRegion(
         RoundedRect(ThebesRect(mPathRect.ToUnknownRect()),
                     RectCornerRadii(dest.size.width / 2.0))
       ));
-      auto clipId = aBuilder.DefineClip(Nothing(), Nothing(), dest, &clips, nullptr);
+      auto clipId = aBuilder.DefineClip(Nothing(), dest, &clips, nullptr);
       aBuilder.PushClip(clipId);
       aBuilder.PushRect(dest, dest, isBackfaceVisible, color);
       aBuilder.PopClip();
       return true;
     }
     case NS_STYLE_LIST_STYLE_SQUARE: {
       aBuilder.PushRect(dest, dest, isBackfaceVisible, color);
       return true;
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -148,17 +148,16 @@ public:
         WebRenderCanvasRendererAsync* data =
           static_cast<WebRenderCanvasRendererAsync*>(canvasData->GetCanvasRenderer());
         MOZ_ASSERT(data);
         data->UpdateCompositableClient();
 
         // Push IFrame for async image pipeline.
         // XXX Remove this once partial display list update is supported.
 
-        /* ScrollingLayersHelper scroller(this, aBuilder, aResources, aSc); */
         nsIntSize canvasSizeInPx = data->GetSize();
         IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSizeInPx);
         nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSizeInPx);
 
         nsRect area = mFrame->GetContentRectRelativeToSelf() + ToReferenceFrame();
         nsRect dest =
           nsLayoutUtils::ComputeObjectDestRect(area, intrinsicSize, intrinsicRatio,
                                                mFrame->StylePosition());
--- a/layout/painting/nsCSSRenderingBorders.cpp
+++ b/layout/painting/nsCSSRenderingBorders.cpp
@@ -3435,30 +3435,30 @@ nsCSSBorderRenderer::CreateWebRenderComm
   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::FromUnknownRect(mLocalClip.value());
     wr::LayoutRect clipRect = wr::ToRoundedLayoutRect(clip);
-    wr::WrClipId clipId = aBuilder.DefineClip(Nothing(), Nothing(), clipRect);
-    aBuilder.PushClip(clipId, aItem->GetClipChain());
+    wr::WrClipId clipId = aBuilder.DefineClip(Nothing(), clipRect);
+    aBuilder.PushClip(clipId);
   }
 
   Range<const wr::BorderSide> wrsides(side, 4);
   aBuilder.PushBorder(roundedRect,
                       roundedRect,
                       mBackfaceIsVisible,
                       wr::ToBorderWidths(mBorderWidths[0], mBorderWidths[1], mBorderWidths[2], mBorderWidths[3]),
                       wrsides,
                       borderRadius);
 
   if (mLocalClip) {
-    aBuilder.PopClip(aItem->GetClipChain());
+    aBuilder.PopClip();
   }
 }
 
 /* static */Maybe<nsCSSBorderImageRenderer>
 nsCSSBorderImageRenderer::CreateBorderImageRenderer(nsPresContext* aPresContext,
                                                     nsIFrame* aForFrame,
                                                     const nsRect& aBorderArea,
                                                     const nsStyleBorder& aStyleBorder,
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -7643,29 +7643,31 @@ nsDisplayStickyPosition::CreateWebRender
       }
     }
 
     LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(itemBounds, auPerDevPixel);
     wr::LayoutVector2D applied = {
       NSAppUnitsToFloatPixels(appliedOffset.x, auPerDevPixel),
       NSAppUnitsToFloatPixels(appliedOffset.y, auPerDevPixel)
     };
-    wr::WrStickyId id = aBuilder.DefineStickyFrame(wr::ToRoundedLayoutRect(bounds),
+    wr::WrClipId id = aBuilder.DefineStickyFrame(wr::ToRoundedLayoutRect(bounds),
         topMargin.ptrOr(nullptr), rightMargin.ptrOr(nullptr),
         bottomMargin.ptrOr(nullptr), leftMargin.ptrOr(nullptr),
         vBounds, hBounds, applied);
 
-    aBuilder.PushStickyFrame(id, GetClipChain());
+    aBuilder.PushClip(id);
+    aManager->CommandBuilder().PushOverrideForASR(GetActiveScrolledRoot(), Some(id));
   }
 
   nsDisplayOwnLayer::CreateWebRenderCommands(aBuilder, aResources, aSc,
       aManager, aDisplayListBuilder);
 
   if (stickyScrollContainer) {
-    aBuilder.PopStickyFrame(GetClipChain());
+    aManager->CommandBuilder().PopOverrideForASR(GetActiveScrolledRoot());
+    aBuilder.PopClip();
   }
 
   return true;
 }
 
 nsDisplayScrollInfoLayer::nsDisplayScrollInfoLayer(
   nsDisplayListBuilder* aBuilder,
   nsIFrame* aScrolledFrame,
@@ -9699,17 +9701,17 @@ nsDisplayMask::CreateWebRenderCommands(m
 
   Maybe<wr::WrImageMask> mask = aManager->CommandBuilder().BuildWrMaskImage(this, aBuilder, aResources,
                                                                             aSc, aDisplayListBuilder,
                                                                             bounds);
   Maybe<StackingContextHelper> layer;
   const StackingContextHelper* sc = &aSc;
   if (mask) {
     auto layoutBounds = wr::ToRoundedLayoutRect(bounds);
-    wr::WrClipId clipId = aBuilder.DefineClip(Nothing(), Nothing(),
+    wr::WrClipId clipId = aBuilder.DefineClip(Nothing(),
         layoutBounds, nullptr, mask.ptr());
 
     // Create a new stacking context to attach the mask to, ensuring the mask is
     // applied to the aggregate, and not the individual elements.
 
     // The stacking context shouldn't have any offset.
     bounds.MoveTo(0, 0);
 
@@ -9723,20 +9725,25 @@ nsDisplayMask::CreateWebRenderCommands(m
                   /*aTransform: */ nullptr,
                   /*aPerspective: */ nullptr,
                   /*aMixBlendMode: */ gfx::CompositionOp::OP_OVER,
                   /*aBackfaceVisible: */ true,
                   /*aIsPreserve3D: */ false,
                   /*aTransformForScrollData: */ Nothing(),
                   /*aClipNodeId: */ &clipId);
     sc = layer.ptr();
+    aManager->CommandBuilder().PushOverrideForASR(GetActiveScrolledRoot(), Some(clipId));
   }
 
   nsDisplaySVGEffects::CreateWebRenderCommands(aBuilder, aResources, *sc, aManager, aDisplayListBuilder);
 
+  if (mask) {
+    aManager->CommandBuilder().PopOverrideForASR(GetActiveScrolledRoot());
+  }
+
   return true;
 }
 
 Maybe<nsRect>
 nsDisplayMask::GetClipWithRespectToASR(nsDisplayListBuilder* aBuilder,
                                        const ActiveScrolledRoot* aASR) const
 {
   if (const DisplayItemClip* clip = DisplayItemClipChain::ClipForASR(GetClipChain(), aASR)) {
--- a/layout/reftests/async-scrolling/reftest.list
+++ b/layout/reftests/async-scrolling/reftest.list
@@ -52,18 +52,18 @@ fuzzy-if(Android,7,4) skip-if(!asyncPan)
 fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-2.html perspective-scrolling-2-ref.html
 fuzzy-if(Android,7,4) fails-if(webrender) skip-if(!asyncPan) == perspective-scrolling-3.html perspective-scrolling-3-ref.html # bug 1361720 for webrender
 fuzzy-if(Android,7,4) skip-if(!asyncPan) == perspective-scrolling-4.html perspective-scrolling-4-ref.html
 pref(apz.disable_for_scroll_linked_effects,true) skip-if(!asyncPan) == disable-apz-for-sle-pages.html disable-apz-for-sle-pages-ref.html
 fuzzy-if(browserIsRemote&&d2d,1,20) skip-if(!asyncPan) == background-blend-mode-1.html background-blend-mode-1-ref.html
 skip-if(Android||!asyncPan) != opaque-fractional-displayport-1.html about:blank
 skip-if(Android||!asyncPan) != opaque-fractional-displayport-2.html about:blank
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == fixed-pos-scrolled-clip-1.html fixed-pos-scrolled-clip-1-ref.html
-fuzzy-if(Android,6,8) fails-if(webrender) skip-if(!asyncPan) == fixed-pos-scrolled-clip-2.html fixed-pos-scrolled-clip-2-ref.html   # bug 1377187 for webrender
-fuzzy-if(Android,6,8) fails-if(webrender) skip-if(!asyncPan) == fixed-pos-scrolled-clip-3.html fixed-pos-scrolled-clip-3-ref.html   # bug 1377187 for webrender
+fuzzy-if(Android,6,8) skip-if(!asyncPan) == fixed-pos-scrolled-clip-2.html fixed-pos-scrolled-clip-2-ref.html
+fuzzy-if(Android,6,8) fails-if(webrender) skip-if(!asyncPan) == fixed-pos-scrolled-clip-3.html fixed-pos-scrolled-clip-3-ref.html   # bug 1458598 for webrender
 fuzzy-if(Android,6,8) skip-if(!asyncPan) == fixed-pos-scrolled-clip-4.html fixed-pos-scrolled-clip-4-ref.html
 skip-if(!asyncPan) == fixed-pos-scrolled-clip-5.html fixed-pos-scrolled-clip-5-ref.html
 skip-if(!asyncPan) == position-sticky-bug1434250.html position-sticky-bug1434250-ref.html
 fuzzy-if(Android,6,4) skip-if(!asyncPan) == position-sticky-scrolled-clip-1.html position-sticky-scrolled-clip-1-ref.html
 fuzzy-if(Android,6,4) skip == position-sticky-scrolled-clip-2.html position-sticky-scrolled-clip-2-ref.html # bug ?????? - incorrectly applying clip to sticky contents
 skip-if(!asyncPan) == curtain-effect-1.html curtain-effect-1-ref.html
 
 # for the following tests, we want to disable the low-precision buffer
--- a/layout/reftests/backgrounds/reftest.list
+++ b/layout/reftests/backgrounds/reftest.list
@@ -167,17 +167,17 @@ fuzzy(50,500) fuzzy-if(skiaContent,51,32
 == attachment-local-clipping-image-2.html attachment-local-clipping-image-1-ref.html  # Same ref as the previous test.
 == attachment-local-clipping-image-3.html attachment-local-clipping-image-3-ref.html
 # The next three tests are fuzzy due to bug 1128229.
 fuzzy(16,69) fuzzy-if(skiaContent,95,2206) == attachment-local-clipping-image-4.html attachment-local-clipping-image-4-ref.html
 fuzzy(16,69) fuzzy-if(skiaContent,95,2206) == attachment-local-clipping-image-5.html attachment-local-clipping-image-4-ref.html
 fuzzy(80,500) fuzzy-if(skiaContent,109,908) == attachment-local-clipping-image-6.html attachment-local-clipping-image-6-ref.html
 
 fuzzy-if(skiaContent,1,8) fuzzy-if(webrender,1,84) == background-multiple-with-border-radius.html background-multiple-with-border-radius-ref.html
-fuzzy-if(webrender&&winWidget,73-73,49600-49600) == background-repeat-large-area.html background-repeat-large-area-ref.html
+fuzzy-if(webrender,73-93,49600-49600) == background-repeat-large-area.html background-repeat-large-area-ref.html
 
 fuzzy(30,474) fuzzy-if(skiaContent,31,474) == background-tiling-zoom-1.html background-tiling-zoom-1-ref.html
 
 skip-if(!cocoaWidget) == background-repeat-resampling.html background-repeat-resampling-ref.html
 
 fuzzy-if(winWidget,102,2032) fuzzy-if(skiaContent,102,2811) fuzzy-if(webrender&&winWidget,153-153,2866-2866) == background-clip-text-1a.html background-clip-text-1-ref.html
 fuzzy-if(winWidget,102,2032) fuzzy-if(skiaContent,102,2811) fuzzy-if(webrender&&winWidget,153-153,2866-2866) == background-clip-text-1b.html background-clip-text-1-ref.html
 fuzzy-if(winWidget,102,2032) fuzzy-if(skiaContent,102,2811) fuzzy-if(webrender&&winWidget,153-153,2866-2866) == background-clip-text-1c.html background-clip-text-1-ref.html
--- a/layout/reftests/bugs/reftest.list
+++ b/layout/reftests/bugs/reftest.list
@@ -1386,17 +1386,17 @@ pref(dom.use_xbl_scopes_for_remote_xul,t
 == 495385-5.html 495385-5-ref.html
 == 496032-1.html 496032-1-ref.html
 == 496840-1.html 496840-1-ref.html
 fuzzy-if(skiaContent,1,17000) == 498228-1.xul 498228-1-ref.xul
 == 501037.html 501037-ref.html
 == 501257-1a.html 501257-1-ref.html
 == 501257-1b.html 501257-1-ref.html
 == 501257-1.xhtml 501257-1-ref.xhtml
-fuzzy-if(webrender&&winWidget,5-5,83252-83252) == 501627-1.html 501627-1-ref.html
+fuzzy-if(webrender,5-6,83252-97456) == 501627-1.html 501627-1-ref.html
 == 502288-1.html 502288-1-ref.html
 fuzzy-if(gtkWidget,1,2) == 502447-1.html 502447-1-ref.html #Bug 1315834
 == 502795-1.html 502795-1-ref.html
 == 502942-1.html 502942-1-ref.html
 == 503364-1a.html 503364-1-ref.html
 == 503364-1b.html 503364-1-ref.html
 # Reftest for bug 503531 marked as failing; should be re-enabled when
 # bug 607548 gets resolved.
@@ -1767,17 +1767,17 @@ fuzzy-if(Android,4,400) == 815593-1.html
 fuzzy-if(skiaContent,1,5) == 816948-1.html 816948-1-ref.html
 == 817019-1.html about:blank
 fuzzy-if(skiaContent,1,5) == 818276-1.html 818276-1-ref.html
 fuzzy-if(asyncPan,190,510) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,510) == 825999.html 825999-ref.html
 == 827577-1a.html 827577-1-ref.html
 == 827577-1b.html 827577-1-ref.html
 == 827799-1.html about:blank
 == 829958.html 829958-ref.html
-== 836844-1.html 836844-1-ref.html
+fuzzy-if(webrender&&gtkWidget,1-2,44000-135600) == 836844-1.html 836844-1-ref.html
 == 841192-1.html 841192-1-ref.html
 == 844178.html 844178-ref.html
 fuzzy-if(OSX,1,364) fuzzy-if(skiaContent,1,320) == 846144-1.html 846144-1-ref.html
 == 847850-1.html 847850-1-ref.html
 == 848421-1.html 848421-1-ref.html
 == 849407-1.html 849407-1-ref.html
 == 849996-1.html 849996-1-ref.html
 == 858803-1.html 858803-1-ref.html
@@ -1812,17 +1812,17 @@ fuzzy-if(Android,1,1) fuzzy-if(skiaConte
 fuzzy-if(skiaContent,1,5) == 956513-1.svg 956513-1-ref.svg
 == 944291-1.html 944291-1-ref.html
 == 950436-1.html 950436-1-ref.html
 == 957770-1.svg 957770-1-ref.svg
 == 960277-1.html 960277-1-ref.html
 fuzzy-if(skiaContent,1,80) == 961887-1.html 961887-1-ref.html
 == 961887-2.html 961887-2-ref.html
 == 961887-3.html 961887-3-ref.html
-pref(layout.css.overflow-clip-box.enabled,true) fuzzy(50,145) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,3712) fails-if(webrender) == 966992-1.html 966992-1-ref.html
+pref(layout.css.overflow-clip-box.enabled,true) fuzzy(50,145) fuzzy-if(asyncPan&&!layersGPUAccelerated,102,3712) == 966992-1.html 966992-1-ref.html
 skip-if(Android) == 966510-1.html 966510-1-ref.html # scrollable elements other than the root probably won't work well on android until bug 776030 is fixed
 skip-if(Android) == 966510-2.html 966510-2-ref.html # same as above
 fuzzy-if(skiaContent,1,123) == 978911-1.svg 978911-1-ref.svg
 == 983084-1.html 983084-1-ref.html
 == 983084-2.html 983084-2-ref.html
 == 983084-3.html 983084-1-ref.html
 == 983691-1.html 983691-ref.html
 == 983985-1.html 983985-1-ref.html
--- a/layout/reftests/forms/placeholder/reftest.list
+++ b/layout/reftests/forms/placeholder/reftest.list
@@ -11,17 +11,17 @@
 == placeholder-1-text.html placeholder-visible-ref.html
 == placeholder-1-password.html placeholder-visible-ref.html
 == placeholder-1-textarea.html placeholder-visible-textarea-ref.html
 == placeholder-2.html placeholder-visible-ref.html
 == placeholder-2-textarea.html placeholder-visible-textarea-ref.html
 == placeholder-3.html placeholder-overridden-ref.html
 == placeholder-4.html placeholder-overridden-ref.html
 == placeholder-5.html placeholder-visible-ref.html
-fuzzy-if(winWidget,160,10) fuzzy-if(Android,1,1) fuzzy-if(asyncPan&&!layersGPUAccelerated,146,317) fuzzy-if(OSX==1010&&browserIsRemote,1,8) fails-if(webrender) == placeholder-6.html placeholder-overflow-ref.html
+fuzzy-if(winWidget,160,10) fuzzy-if(Android,1,1) fuzzy-if(asyncPan&&!layersGPUAccelerated,146,317) fuzzy-if(OSX==1010&&browserIsRemote,1,8) == placeholder-6.html placeholder-overflow-ref.html
 skip-if(Android&&asyncPan) == placeholder-6-textarea.html placeholder-overflow-textarea-ref.html
 # needs-focus == placeholder-7.html placeholder-focus-ref.html
 # needs-focus == placeholder-8.html placeholder-focus-ref.html
 # needs-focus == placeholder-9.html placeholder-focus-ref.html
 needs-focus == placeholder-10.html placeholder-visible-ref.html
 == placeholder-11.html placeholder-visible-ref.html
 == placeholder-12.html placeholder-visible-ref.html
 == placeholder-13.html placeholder-visible-ref.html
--- a/layout/reftests/forms/textarea/reftest.list
+++ b/layout/reftests/forms/textarea/reftest.list
@@ -5,10 +5,10 @@ skip-if(Android) != ltr.html rtl.html
 skip-if(Android) != ltr-scrollbar.html rtl-scrollbar.html
 skip-if(Android) != in-ltr-doc-scrollbar.html in-rtl-doc-scrollbar.html
 skip-if(Android) != ltr.html no-resize.html
 skip-if(Android) != rtl.html no-resize.html # bug 834724
 fuzzy-if(skiaContent,1,1) == rtl.html rtl-dynamic-attr.html
 fuzzy-if(skiaContent,1,1) == rtl.html rtl-dynamic-style.html
 == rtl.html in-dynamic-rtl-doc.html
 fuzzy-if(skiaContent,1,3) == setvalue-framereconstruction-1.html setvalue-framereconstruction-ref.html
-fuzzy-if(asyncPan&&!layersGPUAccelerated,102,4168) fails-if(webrender) == padding-scrollbar-placement.html padding-scrollbar-placement-ref.html
+fuzzy-if(asyncPan&&!layersGPUAccelerated,102,4168) == padding-scrollbar-placement.html padding-scrollbar-placement-ref.html
 == various-cols.html various-cols-ref.html
--- a/widget/windows/nsWindow.cpp
+++ b/widget/windows/nsWindow.cpp
@@ -4085,17 +4085,17 @@ nsWindow::AddWindowOverlayWebRenderComma
 {
   if (mWindowButtonsRect) {
     wr::LayoutRect rect = wr::ToLayoutRect(*mWindowButtonsRect);
     nsTArray<wr::ComplexClipRegion> roundedClip;
     roundedClip.AppendElement(wr::ToComplexClipRegion(
       RoundedRect(ThebesRect(mWindowButtonsRect->ToUnknownRect()),
                   RectCornerRadii(0, 0, 3, 3))));
     wr::WrClipId clipId =
-      aBuilder.DefineClip(Nothing(), Nothing(), rect, &roundedClip);
+      aBuilder.DefineClip(Nothing(), rect, &roundedClip);
     aBuilder.PushClip(clipId);
     aBuilder.PushClearRect(rect);
     aBuilder.PopClip();
   }
 }
 
 uint32_t
 nsWindow::GetMaxTouchPoints() const