doc splitting megapatch draft
authorDoug Thayer <dothayer@mozilla.com>
Sat, 24 Nov 2018 11:16:11 -0800
changeset 1774429 7dd0d6a0ed41
parent 1770276 6c10213a8924
child 1774430 895786dc36c9
push id318916
push userdothayer@mozilla.com
push dateThu, 29 Nov 2018 19:55:14 +0000
treeherdertry@895786dc36c9 [default view] [failures only]
milestone65.0a1
doc splitting megapatch
browser/base/content/browser.xul
browser/components/extensions/ExtensionPopups.jsm
gfx/layers/AnimationHelper.cpp
gfx/layers/AnimationHelper.h
gfx/layers/ShareableCanvasRenderer.cpp
gfx/layers/ShareableCanvasRenderer.h
gfx/layers/UpdateImageHelper.h
gfx/layers/apz/public/APZUpdater.h
gfx/layers/apz/public/CompositorController.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/APZUpdater.cpp
gfx/layers/client/CanvasClient.cpp
gfx/layers/client/CanvasClient.h
gfx/layers/client/ClientImageLayer.cpp
gfx/layers/client/CompositableClient.cpp
gfx/layers/client/CompositableClient.h
gfx/layers/client/ContentClient.cpp
gfx/layers/client/ImageClient.cpp
gfx/layers/client/ImageClient.h
gfx/layers/ipc/CompositableForwarder.h
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/layers/ipc/PWebRenderBridge.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/ipc/SharedSurfacesChild.cpp
gfx/layers/ipc/SharedSurfacesChild.h
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/AsyncImagePipelineManager.h
gfx/layers/wr/IpcResourceUpdateQueue.cpp
gfx/layers/wr/IpcResourceUpdateQueue.h
gfx/layers/wr/StackingContextHelper.cpp
gfx/layers/wr/StackingContextHelper.h
gfx/layers/wr/WebRenderBridgeChild.cpp
gfx/layers/wr/WebRenderBridgeChild.h
gfx/layers/wr/WebRenderBridgeParent.cpp
gfx/layers/wr/WebRenderBridgeParent.h
gfx/layers/wr/WebRenderCanvasRenderer.cpp
gfx/layers/wr/WebRenderCanvasRenderer.h
gfx/layers/wr/WebRenderCommandBuilder.cpp
gfx/layers/wr/WebRenderCommandBuilder.h
gfx/layers/wr/WebRenderLayerManager.cpp
gfx/layers/wr/WebRenderLayerManager.h
gfx/layers/wr/WebRenderMessageUtils.h
gfx/layers/wr/WebRenderScrollData.cpp
gfx/layers/wr/WebRenderScrollData.h
gfx/layers/wr/WebRenderUserData.cpp
gfx/layers/wr/WebRenderUserData.h
gfx/thebes/gfxFontMissingGlyphs.cpp
gfx/thebes/gfxPrefs.h
gfx/webrender_bindings/RenderThread.cpp
gfx/webrender_bindings/RenderThread.h
gfx/webrender_bindings/WebRenderAPI.cpp
gfx/webrender_bindings/WebRenderAPI.h
gfx/webrender_bindings/WebRenderTypes.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi_generated.h
gfx/wr/webrender/src/frame_builder.rs
gfx/wr/webrender/src/gpu_cache.rs
gfx/wr/webrender/src/render_backend.rs
gfx/wr/webrender/src/renderer.rs
gfx/wr/webrender/src/resource_cache.rs
gfx/wr/webrender/src/scene_builder.rs
gfx/wr/webrender/src/texture_cache.rs
gfx/wr/webrender_api/src/api.rs
ipc/glue/ByteBuf.h
layout/generic/TextDrawTarget.h
layout/generic/nsFrame.cpp
layout/generic/nsHTMLCanvasFrame.cpp
layout/painting/RetainedDisplayListBuilder.cpp
layout/painting/nsDisplayItemTypesList.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/tools/reftest/reftest.xul
layout/xul/nsBoxFrame.cpp
testing/marionette/reftest.xul
xpcom/ds/StaticAtoms.py
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -699,17 +699,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                 accesskey="&selectAllCmd.accesskey;"
                 cmd="cmd_selectAll"/>
       <menuseparator/>
       <menuitem label="&syncSyncNowItem.label;"
                 accesskey="&syncSyncNowItem.accesskey;"
                 id="syncedTabsRefreshFilter"/>
     </menupopup>
 
-    <hbox id="statuspanel" inactive="true" layer="true">
+    <hbox id="statuspanel" inactive="true" layer="true" webrendercontent="true">
       <hbox id="statuspanel-inner">
         <label id="statuspanel-label"
                role="status"
                aria-live="off"
                flex="1"
                crop="end"/>
       </hbox>
     </hbox>
@@ -1299,17 +1299,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                    persist="width">
         <searchbar id="searchbar" flex="1"/>
       </toolbaritem>
     </toolbarpalette>
   </toolbox>
 
   <hbox id="fullscr-toggler" hidden="true"/>
 
-  <deck id="content-deck" flex="1">
+  <deck id="content-deck" flex="1" webrendercontent="true">
     <hbox flex="1" id="browser">
       <vbox id="browser-border-start" hidden="true" layer="true"/>
       <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
         <sidebarheader id="sidebar-header" align="center">
           <toolbarbutton id="sidebar-switcher-target" flex="1" class="tabbable">
             <image id="sidebar-icon" consumeanchor="sidebar-switcher-target"/>
             <label id="sidebar-title" crop="end" flex="1" control="sidebar"/>
             <image id="sidebar-switcher-arrow"/>
@@ -1335,17 +1335,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                      flex="1" class="plain" selectedIndex="0"/>
         </tabbox>
       </vbox>
       <vbox id="browser-border-end" hidden="true" layer="true"/>
     </hbox>
 #include ../../components/customizableui/content/customizeMode.inc.xul
   </deck>
 
-  <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
+  <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true" webrendercontent="true">
     <html:div class="pointerlockfswarning-domain-text">
       &fullscreenWarning.beforeDomain.label;
       <html:span class="pointerlockfswarning-domain"/>
       &fullscreenWarning.afterDomain.label;
     </html:div>
     <html:div class="pointerlockfswarning-generic-text">
       &fullscreenWarning.generic.label;
     </html:div>
@@ -1354,24 +1354,23 @@ xmlns="http://www.w3.org/1999/xhtml"
 #ifdef XP_MACOSX
             &exitDOMFullscreenMac.button;
 #else
             &exitDOMFullscreen.button;
 #endif
     </html:button>
   </html:div>
 
-  <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
+  <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true" webrendercontent="true">
     <html:div class="pointerlockfswarning-domain-text">
       &pointerlockWarning.beforeDomain.label;
       <html:span class="pointerlockfswarning-domain"/>
       &pointerlockWarning.afterDomain.label;
     </html:div>
     <html:div class="pointerlockfswarning-generic-text">
       &pointerlockWarning.generic.label;
     </html:div>
   </html:div>
 
-  <vbox id="browser-bottombox" layer="true">
+  <vbox id="browser-bottombox" layer="true" webrendercontent="true">
     <!-- gNotificationBox will be added here lazily. -->
   </vbox>
-
 </window>
--- a/browser/components/extensions/ExtensionPopups.jsm
+++ b/browser/components/extensions/ExtensionPopups.jsm
@@ -234,16 +234,17 @@ class BasePopup {
     }
   }
 
   createBrowser(viewNode, popupURL = null) {
     let document = viewNode.ownerDocument;
 
     let stack = document.createXULElement("stack");
     stack.setAttribute("class", "webextension-popup-stack");
+    stack.setAttribute("webrendercontent", "true");
 
     let browser = document.createXULElement("browser");
     browser.setAttribute("type", "content");
     browser.setAttribute("disableglobalhistory", "true");
     browser.setAttribute("transparent", "true");
     browser.setAttribute("class", "webextension-popup-browser");
     browser.setAttribute("webextension-view-type", "popup");
     browser.setAttribute("tooltip", "aHTMLTooltip");
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -22,25 +22,27 @@ namespace layers {
 
 void
 CompositorAnimationStorage::Clear()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   mAnimatedValues.Clear();
   mAnimations.Clear();
+  mAnimationRenderRoots.Clear();
 }
 
 void
 CompositorAnimationStorage::ClearById(const uint64_t& aId)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   mAnimatedValues.Remove(aId);
   mAnimations.Remove(aId);
+  mAnimationRenderRoots.Remove(aId);
 }
 
 AnimatedValue*
 CompositorAnimationStorage::GetAnimatedValue(const uint64_t& aId) const
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mAnimatedValues.Get(aId);
 }
@@ -134,21 +136,23 @@ CompositorAnimationStorage::SetAnimatedV
 AnimationArray*
 CompositorAnimationStorage::GetAnimations(const uint64_t& aId) const
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mAnimations.Get(aId);
 }
 
 void
-CompositorAnimationStorage::SetAnimations(uint64_t aId, const AnimationArray& aValue)
+CompositorAnimationStorage::SetAnimations(uint64_t aId, const AnimationArray& aValue,
+                                          wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AnimationArray* value = new AnimationArray(aValue);
   mAnimations.Put(aId, value);
+  mAnimationRenderRoots.Put(aId, aRenderRoot);
 }
 
 
 AnimationHelper::SampleResult
 AnimationHelper::SampleAnimationForEachNode(
   TimeStamp aPreviousFrameTime,
   TimeStamp aCurrentFrameTime,
   AnimationArray& aAnimations,
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_AnimationHelper_h
 #define mozilla_layers_AnimationHelper_h
 
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/ComputedTimingFunction.h" // for ComputedTimingFunction
 #include "mozilla/layers/LayersMessages.h" // for TransformData, etc
+#include "mozilla/webrender/WebRenderTypes.h" // for RenderRoot
 #include "mozilla/TimeStamp.h"          // for TimeStamp
 #include "mozilla/TimingParams.h"
 #include "X11UndefineNone.h"
 
 namespace mozilla {
 struct AnimationValue;
 namespace layers {
 class Animation;
@@ -99,16 +100,17 @@ private:
 // that key during its lifetime. Likewise, in layers-free webrender, a display
 // item that is animated (e.g. nsDisplayTransform) gets a CompositorAnimationsId
 // key and reuses that key (it persists the key via the frame user-data
 // mechanism).
 class CompositorAnimationStorage final
 {
   typedef nsClassHashtable<nsUint64HashKey, AnimatedValue> AnimatedValueTable;
   typedef nsClassHashtable<nsUint64HashKey, AnimationArray> AnimationsTable;
+  typedef nsDataHashtable<nsUint64HashKey, wr::RenderRoot> AnimationsRenderRootsTable;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorAnimationStorage)
 public:
 
   /**
    * Set the animation transform based on the unique id and also
    * set up |aFrameTransform| and |aData| for OMTA testing
    */
@@ -146,17 +148,17 @@ public:
   uint32_t AnimatedValueCount() const
   {
     return mAnimatedValues.Count();
   }
 
   /**
    * Set the animations based on the unique id
    */
-  void SetAnimations(uint64_t aId, const AnimationArray& aAnimations);
+  void SetAnimations(uint64_t aId, const AnimationArray& aAnimations, wr::RenderRoot aRenderRoot);
 
   /**
    * Return the animations if a given id can map to its animations
    */
   AnimationArray* GetAnimations(const uint64_t& aId) const;
 
   /**
    * Return the iterator of animations table
@@ -166,28 +168,34 @@ public:
     return mAnimations.ConstIter();
   }
 
   uint32_t AnimationsCount() const
   {
     return mAnimations.Count();
   }
 
+  wr::RenderRoot AnimationRenderRoot(const uint64_t& aId) const
+  {
+    return mAnimationRenderRoots.Get(aId);
+  }
+
   /**
    * Clear AnimatedValues and Animations data
    */
   void Clear();
   void ClearById(const uint64_t& aId);
 
 private:
   ~CompositorAnimationStorage() { };
 
 private:
   AnimatedValueTable mAnimatedValues;
   AnimationsTable mAnimations;
+  AnimationsRenderRootsTable mAnimationRenderRoots;
 };
 
 /**
  * This utility class allows reusing code between the webrender and
  * non-webrender compositor-side implementations. It provides
  * utility functions for sampling animations at particular timestamps.
  */
 class AnimationHelper
--- a/gfx/layers/ShareableCanvasRenderer.cpp
+++ b/gfx/layers/ShareableCanvasRenderer.cpp
@@ -208,17 +208,17 @@ ShareableCanvasRenderer::GetCanvasClient
 
   if (mGLContext) {
     return CanvasClient::CanvasClientTypeShSurf;
   }
   return CanvasClient::CanvasClientSurface;
 }
 
 void
-ShareableCanvasRenderer::UpdateCompositableClient()
+ShareableCanvasRenderer::UpdateCompositableClient(wr::RenderRoot aRenderRoot)
 {
   if (!CreateCompositable()) {
     return;
   }
 
   if (mCanvasClient && mAsyncRenderer) {
     mCanvasClient->UpdateAsync(mAsyncRenderer);
   }
@@ -229,20 +229,20 @@ ShareableCanvasRenderer::UpdateComposita
   ResetDirty();
 
   FirePreTransactionCallback();
   if (mBufferProvider && mBufferProvider->GetTextureClient()) {
     if (!mBufferProvider->SetKnowsCompositor(GetForwarder())) {
       gfxCriticalNote << "BufferProvider::SetForwarder failed";
       return;
     }
-    mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient());
+    mCanvasClient->UpdateFromTexture(mBufferProvider->GetTextureClient(), aRenderRoot);
   } else {
-    mCanvasClient->Update(gfx::IntSize(mSize.width, mSize.height), this);
+    mCanvasClient->Update(gfx::IntSize(mSize.width, mSize.height), this, aRenderRoot);
   }
 
   FireDidTransactionCallback();
 
-  mCanvasClient->Updated();
+  mCanvasClient->Updated(aRenderRoot);
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/ShareableCanvasRenderer.h
+++ b/gfx/layers/ShareableCanvasRenderer.h
@@ -34,17 +34,17 @@ public:
 
   virtual CompositableForwarder* GetForwarder() = 0;
 
   virtual bool CreateCompositable() = 0;
 
   void ClearCachedResources() override;
   void Destroy() override;
 
-  void UpdateCompositableClient();
+  void UpdateCompositableClient(wr::RenderRoot aRenderRoot = wr::RenderRoot::Default);
 
   const TextureFlags& Flags() const { return mFlags; }
 
   CanvasClient* GetCanvasClient() { return mCanvasClient; }
 
 protected:
   bool UpdateTarget(gfx::DrawTarget* aDestTarget);
 
--- a/gfx/layers/UpdateImageHelper.h
+++ b/gfx/layers/UpdateImageHelper.h
@@ -55,31 +55,31 @@ public:
   {
     RefPtr<gfx::DrawTarget> target;
     if (mTexture) {
       target = mTexture->BorrowDrawTarget();
     }
     return target.forget();
   }
 
-  bool UpdateImage()
+  bool UpdateImage(wr::RenderRoot aRenderRoot)
   {
     if (!mTexture) {
       return false;
     }
 
     if (mIsLocked) {
       mTexture->Unlock();
       mIsLocked = false;
     }
 
     RefPtr<TextureWrapperImage> image = new TextureWrapperImage(mTexture,
                                                                 gfx::IntRect(gfx::IntPoint(0, 0), mImageSize));
     mImageContainer->SetCurrentImageInTransaction(image);
-    return mImageClient->UpdateImage(mImageContainer, /* unused */0);
+    return mImageClient->UpdateImage(mImageContainer, /* unused */0, aRenderRoot);
   }
 
 private:
   RefPtr<ImageContainer> mImageContainer;
   RefPtr<ImageClient> mImageClient;
   gfx::IntSize mImageSize;
   RefPtr<TextureClient> mTexture;
   bool mIsLocked;
--- a/gfx/layers/apz/public/APZUpdater.h
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -166,16 +166,17 @@ private:
 
   // Stores epoch state for a particular layers id. This structure is only
   // accessed on the updater thread.
   struct EpochState {
     // The epoch for the most recent scroll data sent from the content side.
     wr::Epoch mRequired;
     // The epoch for the most recent scene built and swapped in on the WR side.
     Maybe<wr::Epoch> mBuilt;
+    bool mVisible;
     // True if and only if the layers id is the root layers id for the compositor
     bool mIsRoot;
 
     EpochState();
 
     // Whether or not the state for this layers id is such that it blocks
     // processing of tasks for the layer tree. This happens if the root layers
     // id or a "visible" layers id has scroll data for an epoch newer than what
--- a/gfx/layers/apz/public/CompositorController.h
+++ b/gfx/layers/apz/public/CompositorController.h
@@ -12,17 +12,17 @@
 namespace mozilla {
 namespace layers {
 
 class CompositorController
 {
 public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
-  virtual void ScheduleRenderOnCompositorThread() = 0;
+  virtual void ScheduleRenderOnCompositorThread(uint64_t aRenderRootid = 0) = 0;
   virtual void ScheduleHideAllPluginWindows() = 0;
   virtual void ScheduleShowAllPluginWindows() = 0;
 
 protected:
   virtual ~CompositorController() {}
 };
 
 } // namespace layers
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -2557,17 +2557,17 @@ already_AddRefed<AsyncPanZoomController>
 APZCTreeManager::GetAPZCAtPointWR(const ScreenPoint& aHitTestPoint,
                                   CompositorHitTestInfo* aOutHitResult,
                                   HitTestingTreeNode** aOutScrollbarNode)
 {
   MOZ_ASSERT(aOutHitResult);
   MOZ_ASSERT(aOutScrollbarNode);
 
   RefPtr<AsyncPanZoomController> result;
-  RefPtr<wr::WebRenderAPI> wr = GetWebRenderAPI();
+  RefPtr<wr::WebRenderAPI> wr = GetWebRenderAPIAtPoint(aHitTestPoint);
   if (!wr) {
     // If WebRender isn't running, fall back to the root APZC.
     // This is mostly for the benefit of GTests which do not
     // run a WebRender instance, but gracefully falling back
     // here allows those tests which are not specifically
     // testing the hit-test algorithm to still work.
     result = FindRootApzcForLayersId(mRootLayersId);
     *aOutHitResult = CompositorHitTestFlags::eVisibleToHitTest;
@@ -3156,16 +3156,43 @@ APZCTreeManager::GetWebRenderAPI() const
     [&](LayerTreeState& aState) -> void {
       if (aState.mWrBridge) {
         api = aState.mWrBridge->GetWebRenderAPI();
       }
     });
   return api.forget();
 }
 
+already_AddRefed<wr::WebRenderAPI>
+APZCTreeManager::GetContentRectWebRenderAPI() const
+{
+  RefPtr<wr::WebRenderAPI> api;
+  CompositorBridgeParent::CallWithIndirectShadowTree(mRootLayersId,
+    [&](LayerTreeState& aState) -> void {
+      if (aState.mWrBridge) {
+        api = aState.mWrBridge->GetContentRectWebRenderAPI();
+      }
+    });
+  return api.forget();
+}
+
+already_AddRefed<wr::WebRenderAPI>
+APZCTreeManager::GetWebRenderAPIAtPoint(const ScreenPoint& aPoint) const
+{
+  RefPtr<wr::WebRenderAPI> api;
+  CompositorBridgeParent::CallWithIndirectShadowTree(mRootLayersId,
+    [&](LayerTreeState& aState) -> void {
+      if (aState.mWrBridge) {
+        IntPoint point = RoundedToInt(aPoint).ToUnknownPoint();
+        api = aState.mWrBridge->GetWebRenderAPIAtPoint(point);
+      }
+    });
+  return api.forget();
+}
+
 already_AddRefed<GeckoContentController>
 APZCTreeManager::GetContentController(LayersId aLayersId) const
 {
   RefPtr<GeckoContentController> controller;
   CompositorBridgeParent::CallWithIndirectShadowTree(aLayersId,
     [&](LayerTreeState& aState) -> void {
       controller = aState.mController;
     });
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -542,16 +542,18 @@ public:
   // Assert that the current thread is the sampler thread for this APZCTM.
   void AssertOnSamplerThread();
   // Assert that the current thread is the updater thread for this APZCTM.
   void AssertOnUpdaterThread();
 
   // Returns a pointer to the WebRenderAPI for the root layers id this APZCTreeManager
   // is for. This might be null (for example, if WebRender is not enabled).
   already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() const;
+  already_AddRefed<wr::WebRenderAPI> GetContentRectWebRenderAPI() const;
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPIAtPoint(const ScreenPoint& aPoint) const;
 
 protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
   APZSampler* GetSampler() const;
   APZUpdater* GetUpdater() const;
 
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -101,21 +101,27 @@ APZUpdater::CompleteSceneSwap(const wr::
 
   for (uintptr_t i = 0; i < aInfo.removed_pipelines.length; i++) {
     LayersId layersId = wr::AsLayersId(aInfo.removed_pipelines.data[i]);
     updater->mEpochData.erase(layersId);
   }
   // Reset the built info for all pipelines, then put it back for the ones
   // that got built in this scene swap.
   for (auto& i : updater->mEpochData) {
-    i.second.mBuilt = Nothing();
+    i.second.mVisible = false;
   }
   for (uintptr_t i = 0; i < aInfo.epochs.length; i++) {
     LayersId layersId = wr::AsLayersId(aInfo.epochs.data[i].pipeline_id);
-    updater->mEpochData[layersId].mBuilt = Some(aInfo.epochs.data[i].epoch);
+    // With two documents there can be two different epochs for a pipeline_id,
+    // but the most recent one is always the one we care about.
+    if (!updater->mEpochData[layersId].mBuilt ||
+        *updater->mEpochData[layersId].mBuilt < aInfo.epochs.data[i].epoch) {
+      updater->mEpochData[layersId].mBuilt = Some(aInfo.epochs.data[i].epoch);
+    }
+    updater->mEpochData[layersId].mVisible = true;
   }
 
   // Run any tasks that got unblocked, then unlock the tree. The order is
   // important because we want to run all the tasks up to and including the
   // UpdateHitTestingTree calls corresponding to the built epochs, and we
   // want to run those before we release the lock (i.e. atomically with the
   // scene swap). This ensures that any hit-tests always encounter a consistent
   // state between the APZ tree and the built scene in WR.
@@ -214,17 +220,21 @@ APZUpdater::UpdateScrollDataAndTreeState
     }
   ));
   RunOnUpdaterThread(aOriginatingLayersId, NS_NewRunnableFunction(
     "APZUpdater::UpdateHitTestingTree",
     [=,aScrollData=std::move(aScrollData)]() {
       self->mApz->UpdateFocusState(aRootLayerTreeId,
           aOriginatingLayersId, aScrollData.GetFocusTarget());
 
-      self->mScrollData[aOriginatingLayersId] = aScrollData;
+      if (aScrollData.SkippedContentRect()) {
+        // TODO: merge in content rect scroll data at known roots?
+      } else {
+        self->mScrollData[aOriginatingLayersId] = aScrollData;
+      }
       auto root = self->mScrollData.find(aRootLayerTreeId);
       if (root == self->mScrollData.end()) {
         return;
       }
       self->mApz->UpdateHitTestingTree(aRootLayerTreeId,
           WebRenderScrollDataWrapper(*self, &(root->second)),
           aScrollData.IsFirstPaint(), aOriginatingLayersId,
           aScrollData.GetPaintSequenceNumber());
@@ -516,16 +526,17 @@ APZUpdater::ProcessQueue()
       // Run and discard the task
       task.mRunnable->Run();
     }
   }
 }
 
 APZUpdater::EpochState::EpochState()
   : mRequired{0}
+  , mVisible(false)
   , mIsRoot(false)
 {
 }
 
 bool
 APZUpdater::EpochState::IsBlocked() const
 {
   // The root is a special case because we basically assume it is "visible"
@@ -541,20 +552,21 @@ APZUpdater::EpochState::IsBlocked() cons
   // So in the case of non-visible subtrees, we know that no hit-test will
   // actually end up hitting that subtree either before or after the scene swap,
   // because the subtree will remain non-visible. That in turns means that we
   // can apply the APZ scroll data for that subtree epoch before the scene is
   // built, because it's not going to get used anyway. And that means we don't
   // need to block the queue for non-visible subtrees. Which is a good thing,
   // because in practice it seems like we often have non-visible subtrees sent
   // to the compositor from content.
-  if (mIsRoot && !mBuilt) {
+  if (mIsRoot && !mVisible) {
     return true;
   }
-  return mBuilt && (*mBuilt < mRequired);
+
+  return mBuilt && mVisible && (*mBuilt < mRequired);
 }
 
 } // namespace layers
 } // namespace mozilla
 
 // Rust callback implementations
 
 void
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -61,17 +61,17 @@ CanvasClientBridge::UpdateAsync(AsyncCan
   }
 
   static_cast<ShadowLayerForwarder*>(GetForwarder())
     ->AttachAsyncCompositable(asyncID, mLayer);
   mAsyncHandle = asyncID;
 }
 
 void
-CanvasClient2D::UpdateFromTexture(TextureClient* aTexture)
+CanvasClient2D::UpdateFromTexture(TextureClient* aTexture, wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(aTexture);
 
   if (!aTexture->IsSharedWithCompositor()) {
     if (!AddTextureClient(aTexture)) {
       return;
     }
   }
@@ -81,26 +81,26 @@ CanvasClient2D::UpdateFromTexture(Textur
   mBufferProviderTexture = aTexture;
 
   AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
   CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
   t->mTextureClient = aTexture;
   t->mPictureRect = nsIntRect(nsIntPoint(0, 0), aTexture->GetSize());
   t->mFrameID = mFrameID;
 
-  GetForwarder()->UseTextures(this, textures);
+  GetForwarder()->UseTextures(this, textures, aRenderRoot);
   aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
 }
 
 void
-CanvasClient2D::Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer)
+CanvasClient2D::Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer, wr::RenderRoot aRenderRoot)
 {
   mBufferProviderTexture = nullptr;
 
-  AutoRemoveTexture autoRemove(this);
+  AutoRemoveTexture autoRemove(this, aRenderRoot);
   if (mBackBuffer && (mBackBuffer->IsReadLocked() || mBackBuffer->GetSize() != aSize)) {
     autoRemove.mTexture = mBackBuffer;
     mBackBuffer = nullptr;
   }
 
   bool bufferCreated = false;
   if (!mBackBuffer) {
     gfxContentType contentType =
@@ -147,17 +147,17 @@ CanvasClient2D::Update(gfx::IntSize aSiz
   }
 
   if (updated) {
     AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
     CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
     t->mTextureClient = mBackBuffer;
     t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mBackBuffer->GetSize());
     t->mFrameID = mFrameID;
-    GetForwarder()->UseTextures(this, textures);
+    GetForwarder()->UseTextures(this, textures, aRenderRoot);
     mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
   }
 
   mBackBuffer.swap(mFrontBuffer);
 }
 
 already_AddRefed<TextureClient>
 CanvasClient2D::CreateTextureClientForCanvas(gfx::SurfaceFormat aFormat,
@@ -368,17 +368,18 @@ CloneSurface(gl::SharedSurface* src, gl:
     destSurf->ProducerAcquire();
     SharedSurface::ProdCopy(src, dest->Surf(), factory);
     destSurf->ProducerRelease();
 
     return dest.forget();
 }
 
 void
-CanvasClientSharedSurface::Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer)
+CanvasClientSharedSurface::Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer,
+                                  wr::RenderRoot aRenderRoot)
 {
   Renderer renderer;
   renderer.construct<ShareableCanvasRenderer*>(aCanvasRenderer);
   UpdateRenderer(aSize, renderer);
 }
 
 void
 CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer)
@@ -484,17 +485,17 @@ CanvasClientSharedSurface::UpdateRendere
     gfxCriticalError() << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: " << aSize;
     return;
   }
 
   mNewFront = newFront;
 }
 
 void
-CanvasClientSharedSurface::Updated()
+CanvasClientSharedSurface::Updated(wr::RenderRoot aRenderRoot)
 {
   if (!mNewFront) {
     return;
   }
 
   auto forwarder = GetForwarder();
 
   mFront = mNewFront;
@@ -505,17 +506,17 @@ CanvasClientSharedSurface::Updated()
     return;
   }
 
   AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
   CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
   t->mTextureClient = mFront;
   t->mPictureRect = nsIntRect(nsIntPoint(0, 0), mFront->GetSize());
   t->mFrameID = mFrameID;
-  forwarder->UseTextures(this, textures);
+  forwarder->UseTextures(this, textures, aRenderRoot);
 }
 
 void
 CanvasClientSharedSurface::OnDetach() {
   ClearSurfaces();
 }
 
 void
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -61,29 +61,30 @@ public:
   {
     mTextureFlags = aFlags;
   }
 
   virtual ~CanvasClient() {}
 
   virtual void Clear() {};
 
-  virtual void Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer) = 0;
+  virtual void Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer,
+                      wr::RenderRoot aRenderRoot) = 0;
 
   virtual bool AddTextureClient(TextureClient* aTexture) override
   {
     ++mFrameID;
     return CompositableClient::AddTextureClient(aTexture);
   }
 
   virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) {}
 
-  virtual void UpdateFromTexture(TextureClient* aTexture) {}
+  virtual void UpdateFromTexture(TextureClient* aTexture, wr::RenderRoot aRenderRoot) {}
 
-  virtual void Updated() { }
+  virtual void Updated(wr::RenderRoot aRenderRoot) { }
 
 protected:
   int32_t mFrameID;
 };
 
 // Used for 2D canvases and WebGL canvas on non-GL systems where readback is requried.
 class CanvasClient2D : public CanvasClient
 {
@@ -99,19 +100,20 @@ public:
     return TextureInfo(CompositableType::IMAGE, mTextureFlags);
   }
 
   virtual void Clear() override
   {
     mBackBuffer = mFrontBuffer = mBufferProviderTexture = nullptr;
   }
 
-  virtual void Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer) override;
+  virtual void Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer,
+                      wr::RenderRoot aRenderRoot) override;
 
-  virtual void UpdateFromTexture(TextureClient* aBuffer) override;
+  virtual void UpdateFromTexture(TextureClient* aBuffer, wr::RenderRoot aRenderRoot) override;
 
   virtual bool AddTextureClient(TextureClient* aTexture) override
   {
     return CanvasClient::AddTextureClient(aTexture);
   }
 
   virtual void OnDetach() override
   {
@@ -157,22 +159,23 @@ public:
     return TextureInfo(CompositableType::IMAGE);
   }
 
   virtual void Clear() override {
     ClearSurfaces();
   }
 
   virtual void Update(gfx::IntSize aSize,
-                      ShareableCanvasRenderer* aCanvasRenderer) override;
+                      ShareableCanvasRenderer* aCanvasRenderer,
+                      wr::RenderRoot aRenderRoot) override;
   void UpdateRenderer(gfx::IntSize aSize, Renderer& aRenderer);
 
   virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
 
-  virtual void Updated() override;
+  virtual void Updated(wr::RenderRoot aRenderRoot) override;
 
   virtual void OnDetach() override;
 };
 
 /**
  * Used for OMT<canvas> uploads using the image bridge protocol.
  * Actual CanvasClient is on the ImageBridgeChild thread, so we
  * only forward its AsyncID here
@@ -187,17 +190,18 @@ public:
   {
   }
 
   TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(CompositableType::IMAGE);
   }
 
-  virtual void Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer) override
+  virtual void Update(gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer,
+                      wr::RenderRoot aRenderRoot) override
   {
   }
 
   virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
 
   void SetLayer(ShadowableLayer* aLayer)
   {
     mLayer = aLayer;
--- a/gfx/layers/client/ClientImageLayer.cpp
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -135,34 +135,34 @@ ClientImageLayer::RenderLayer()
 {
   RenderMaskLayers(this);
 
   if (!mContainer) {
      return;
   }
 
   if (!mImageClient ||
-      !mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+      !mImageClient->UpdateImage(mContainer, GetContentFlags(), wr::kRenderRootUnknown)) {
     CompositableType type = GetImageClientType();
     if (type == CompositableType::UNKNOWN) {
       return;
     }
     TextureFlags flags = TextureFlags::DEFAULT;
     mImageClient = ImageClient::CreateImageClient(type,
                                                   ClientManager()->AsShadowForwarder(),
                                                   flags);
     if (!mImageClient) {
       return;
     }
     mImageClient->SetLayer(this);
     if (HasShadow() && !mContainer->IsAsync()) {
       mImageClient->Connect();
       ClientManager()->AsShadowForwarder()->Attach(mImageClient, this);
     }
-    if (!mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+    if (!mImageClient->UpdateImage(mContainer, GetContentFlags(), wr::kRenderRootUnknown)) {
       return;
     }
   }
   ClientManager()->Hold(this);
 }
 
 already_AddRefed<ImageLayer>
 ClientLayerManager::CreateImageLayer()
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -157,19 +157,19 @@ void
 CompositableClient::HandleMemoryPressure()
 {
   if (mTextureClientRecycler) {
     mTextureClientRecycler->ShrinkToMinimumSize();
   }
 }
 
 void
-CompositableClient::RemoveTexture(TextureClient* aTexture)
+CompositableClient::RemoveTexture(TextureClient* aTexture, wr::RenderRoot aRenderRoot)
 {
-  mForwarder->RemoveTextureFromCompositable(this, aTexture);
+  mForwarder->RemoveTextureFromCompositable(this, aTexture, aRenderRoot);
 }
 
 TextureClientRecycleAllocator*
 CompositableClient::GetTextureClientRecycler()
 {
   if (mTextureClientRecycler) {
     return mTextureClientRecycler;
   }
@@ -232,14 +232,14 @@ CompositableClient::DumpTextureClient(st
   } else {
     aStream << gfxUtils::GetAsDataURI(dSurf).get();
   }
 }
 
 AutoRemoveTexture::~AutoRemoveTexture()
 {
   if (mCompositable && mTexture && mCompositable->IsConnected()) {
-    mCompositable->RemoveTexture(mTexture);
+    mCompositable->RemoveTexture(mTexture, mRenderRoot);
   }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -11,16 +11,17 @@
 #include <vector>                       // for vector
 #include <map>                          // for map
 #include "mozilla/Assertions.h"         // for MOZ_CRASH
 #include "mozilla/RefPtr.h"             // for already_AddRefed, RefCounted
 #include "mozilla/gfx/Types.h"          // for SurfaceFormat
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/LayersTypes.h"  // for LayersBackend, TextureDumpMode
 #include "mozilla/layers/TextureClient.h"  // for TextureClient
+#include "mozilla/webrender/WebRenderTypes.h"  // for RenderRoot
 #include "nsISupportsImpl.h"            // for MOZ_COUNT_CTOR, etc
 
 namespace mozilla {
 namespace layers {
 
 class CompositableClient;
 class ImageBridgeChild;
 class ImageContainer;
@@ -159,17 +160,17 @@ public:
   virtual void HandleMemoryPressure();
 
   /**
    * Should be called when deataching a TextureClient from a Compositable, because
    * some platforms need to do some extra book keeping when this happens.
    *
    * See AutoRemoveTexture to automatically invoke this at the end of a scope.
    */
-  virtual void RemoveTexture(TextureClient* aTexture);
+  virtual void RemoveTexture(TextureClient* aTexture, wr::RenderRoot aRenderRoot);
 
   void InitIPDL(const CompositableHandle& aHandle);
 
   TextureFlags GetTextureFlags() const { return mTextureFlags; }
 
   TextureClientRecycleAllocator* GetTextureClientRecycler();
 
   bool HasTextureClientRecycler() { return !!mTextureClientRecycler; }
@@ -191,24 +192,27 @@ protected:
 };
 
 /**
  * Helper to call RemoveTexture at the end of a scope.
  */
 struct AutoRemoveTexture
 {
   explicit AutoRemoveTexture(CompositableClient* aCompositable,
+                             wr::RenderRoot aRenderRoot,
                              TextureClient* aTexture = nullptr)
     : mTexture(aTexture)
     , mCompositable(aCompositable)
+    , mRenderRoot(aRenderRoot)
   {}
 
   ~AutoRemoveTexture();
 
   RefPtr<TextureClient> mTexture;
 private:
   CompositableClient* mCompositable;
+  wr::RenderRoot mRenderRoot;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/client/ContentClient.cpp
+++ b/gfx/layers/client/ContentClient.cpp
@@ -737,17 +737,18 @@ ContentClientRemoteBuffer::Updated(const
                                           remoteBuffer->GetClient(),
                                           remoteBuffer->GetClientOnWhite());
   } else {
     AutoTArray<CompositableForwarder::TimedTextureClient,1> textures;
     CompositableForwarder::TimedTextureClient* t = textures.AppendElement();
     t->mTextureClient = remoteBuffer->GetClient();
     IntSize size = remoteBuffer->GetClient()->GetSize();
     t->mPictureRect = nsIntRect(0, 0, size.width, size.height);
-    GetForwarder()->UseTextures(this, textures);
+
+    GetForwarder()->UseTextures(this, textures, wr::RenderRoot::Default);
   }
 
   // This forces a synchronous transaction, so we can swap buffers now
   // and know that we'll have sole ownership of the old front buffer
   // by the time we paint next.
   mForwarder->UpdateTextureRegion(this,
                                   ThebesBufferData(remoteBuffer->BufferRect(),
                                                    remoteBuffer->BufferRotation()),
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -60,19 +60,19 @@ ImageClient::CreateImageClient(Composita
   }
 
   NS_ASSERTION(result, "Failed to create ImageClient");
 
   return result.forget();
 }
 
 void
-ImageClient::RemoveTexture(TextureClient* aTexture)
+ImageClient::RemoveTexture(TextureClient* aTexture, wr::RenderRoot aRenderRoot)
 {
-  GetForwarder()->RemoveTextureFromCompositable(this, aTexture);
+  GetForwarder()->RemoveTextureFromCompositable(this, aTexture, aRenderRoot);
 }
 
 ImageClientSingle::ImageClientSingle(CompositableForwarder* aFwd,
                                      TextureFlags aFlags,
                                      CompositableType aType)
   : ImageClient(aFwd, aFlags, aType)
 {
 }
@@ -81,17 +81,17 @@ TextureInfo ImageClientSingle::GetTextur
 {
   return TextureInfo(CompositableType::IMAGE);
 }
 
 void
 ImageClientSingle::FlushAllImages()
 {
   for (auto& b : mBuffers) {
-    RemoveTexture(b.mTextureClient);
+    RemoveTexture(b.mTextureClient, wr::RenderRoot::Default);
   }
   mBuffers.Clear();
 }
 
 /* static */ already_AddRefed<TextureClient>
 ImageClient::CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder)
 {
   RefPtr<TextureClient> texture;
@@ -157,17 +157,18 @@ ImageClient::CreateTextureClientForImage
     }
 
     texture->Unlock();
   }
   return texture.forget();
 }
 
 bool
-ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
+ImageClientSingle::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags,
+                               wr::RenderRoot aRenderRoot)
 {
   AutoTArray<ImageContainer::OwningImage,4> images;
   uint32_t generationCounter;
   aContainer->GetCurrentImages(&images, &generationCounter);
 
   if (mLastUpdateGenerationCounter == generationCounter) {
     return true;
   }
@@ -182,17 +183,17 @@ ImageClientSingle::UpdateImage(ImageCont
   if (images.IsEmpty()) {
     // This can happen if a ClearAllImages raced with SetCurrentImages from
     // another thread and ClearImagesFromImageBridge ran after the
     // SetCurrentImages call but before UpdateImageClientNow.
     // This can also happen if all images in the list are invalid.
     // We return true because the caller would attempt to recreate the
     // ImageClient otherwise, and that isn't going to help.
     for (auto& b : mBuffers) {
-      RemoveTexture(b.mTextureClient);
+      RemoveTexture(b.mTextureClient, aRenderRoot);
     }
     mBuffers.Clear();
     return true;
   }
 
   nsTArray<Buffer> newBuffers;
   AutoTArray<CompositableForwarder::TimedTextureClient,4> textures;
 
@@ -245,20 +246,20 @@ ImageClientSingle::UpdateImage(ImageCont
 
     Buffer* newBuf = newBuffers.AppendElement();
     newBuf->mImageSerial = image->GetSerial();
     newBuf->mTextureClient = texture;
 
     texture->SyncWithObject(GetForwarder()->GetSyncObject());
   }
 
-  GetForwarder()->UseTextures(this, textures);
+  GetForwarder()->UseTextures(this, textures, aRenderRoot);
 
   for (auto& b : mBuffers) {
-    RemoveTexture(b.mTextureClient);
+    RemoveTexture(b.mTextureClient, aRenderRoot);
   }
   mBuffers.SwapElements(newBuffers);
 
   return true;
 }
 
 RefPtr<TextureClient>
 ImageClientSingle::GetForwardedTexture()
@@ -292,17 +293,17 @@ ImageClient::ImageClient(CompositableFor
 
 ImageClientBridge::ImageClientBridge(CompositableForwarder* aFwd,
                                      TextureFlags aFlags)
 : ImageClient(aFwd, aFlags, CompositableType::IMAGE_BRIDGE)
 {
 }
 
 bool
-ImageClientBridge::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags)
+ImageClientBridge::UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags, wr::RenderRoot aRenderRoot)
 {
   if (!GetForwarder() || !mLayer) {
     return false;
   }
   if (mAsyncContainerHandle == aContainer->GetAsyncContainerHandle()) {
     return true;
   }
 
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -49,28 +49,28 @@ public:
 
   virtual ~ImageClient() {}
 
   /**
    * Update this ImageClient from aContainer in aLayer
    * returns false if this is the wrong kind of ImageClient for aContainer.
    * Note that returning true does not necessarily imply success
    */
-  virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) = 0;
+  virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags, wr::RenderRoot aRenderRoot) = 0;
 
   void SetLayer(ClientLayer* aLayer) { mLayer = aLayer; }
   ClientLayer* GetLayer() const { return mLayer; }
 
   /**
    * asynchronously remove all the textures used by the image client.
    *
    */
   virtual void FlushAllImages() {}
 
-  virtual void RemoveTexture(TextureClient* aTexture) override;
+  virtual void RemoveTexture(TextureClient* aTexture, wr::RenderRoot aRenderRoot) override;
 
   virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
 
   static already_AddRefed<TextureClient> CreateTextureClientForImage(Image* aImage, KnowsCompositor* aForwarder);
 
   uint32_t GetLastUpdateGenerationCounter() { return mLastUpdateGenerationCounter; }
 
   virtual RefPtr<TextureClient> GetForwardedTexture() { return nullptr; }
@@ -89,17 +89,17 @@ protected:
  */
 class ImageClientSingle : public ImageClient
 {
 public:
   ImageClientSingle(CompositableForwarder* aFwd,
                     TextureFlags aFlags,
                     CompositableType aType);
 
-  virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
+  virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlag, wr::RenderRoot aRenderRoot) override;
 
   virtual void OnDetach() override;
 
   virtual bool AddTextureClient(TextureClient* aTexture) override;
 
   virtual TextureInfo GetTextureInfo() const override;
 
   virtual void FlushAllImages() override;
@@ -124,17 +124,17 @@ protected:
  * We store the ImageBridge id in the TextureClientIdentifier.
  */
 class ImageClientBridge : public ImageClient
 {
 public:
   ImageClientBridge(CompositableForwarder* aFwd,
                     TextureFlags aFlags);
 
-  virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags) override;
+  virtual bool UpdateImage(ImageContainer* aContainer, uint32_t aContentFlags, wr::RenderRoot aRenderRoot) override;
   virtual bool Connect(ImageContainer* aImageContainer) override { return false; }
 
   virtual TextureInfo GetTextureInfo() const override
   {
     return TextureInfo(mType);
   }
 
 protected:
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -76,36 +76,46 @@ public:
 
   /**
    * Tell the CompositableHost on the compositor side to remove the texture
    * from the CompositableHost.
    * This function does not delete the TextureHost corresponding to the
    * TextureClient passed in parameter.
    * When the TextureClient has TEXTURE_DEALLOCATE_CLIENT flag,
    * the transaction becomes synchronous.
+   *
+   * aRenderRoot can be ignored if not using WebRender - since webrender
+   * splits the chrome and content areas into different documents which are
+   * updated separately, we need to know which command buffer to route this into.
    */
   virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
-                                             TextureClient* aTexture) = 0;
+                                             TextureClient* aTexture,
+                                             wr::RenderRoot aRenderRoot) = 0;
 
   struct TimedTextureClient {
     TimedTextureClient()
         : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {}
 
     TextureClient* mTextureClient;
     TimeStamp mTimeStamp;
     nsIntRect mPictureRect;
     int32_t mFrameID;
     int32_t mProducerID;
   };
   /**
    * Tell the CompositableHost on the compositor side what textures to use for
    * the next composition.
+   *
+   * aRenderRoot can be ignored if not using WebRender - since webrender
+   * splits the chrome and content areas into different documents which are
+   * updated separately, we need to know which command buffer to route this into.
    */
   virtual void UseTextures(CompositableClient* aCompositable,
-                           const nsTArray<TimedTextureClient>& aTextures) = 0;
+                           const nsTArray<TimedTextureClient>& aTextures,
+                           wr::RenderRoot aRenderRoot) = 0;
   virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                          TextureClient* aClientOnBlack,
                                          TextureClient* aClientOnWhite) = 0;
 
   virtual void UpdateFwdTransactionId() = 0;
   virtual uint64_t GetFwdTransactionId() = 0;
 
   virtual bool InForwarderThread() = 0;
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -723,23 +723,24 @@ CompositorBridgeParent::ActorDestroy(Act
   mSelfRef = this;
   MessageLoop::current()->PostTask(
     NewRunnableMethod("layers::CompositorBridgeParent::DeferredDestroy",
                       this,
                       &CompositorBridgeParent::DeferredDestroy));
 }
 
 void
-CompositorBridgeParent::ScheduleRenderOnCompositorThread()
+CompositorBridgeParent::ScheduleRenderOnCompositorThread(uint64_t aRenderRootId)
 {
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(
-    NewRunnableMethod("layers::CompositorBridgeParent::ScheduleComposition",
-                      this,
-                      &CompositorBridgeParent::ScheduleComposition));
+    NewRunnableMethod<uint64_t>("layers::CompositorBridgeParent::ScheduleComposition",
+                                this,
+                                &CompositorBridgeParent::ScheduleComposition,
+                                aRenderRootId));
 }
 
 void
 CompositorBridgeParent::InvalidateOnCompositorThread()
 {
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(
     NewRunnableMethod("layers::CompositorBridgeParent::Invalidate",
@@ -947,25 +948,25 @@ CompositorBridgeParent::NotifyShadowTree
     mLayerManager->NotifyShadowTreeTransaction();
   }
   if (aScheduleComposite) {
     ScheduleComposition();
   }
 }
 
 void
-CompositorBridgeParent::ScheduleComposition()
+CompositorBridgeParent::ScheduleComposition(uint64_t aRenderRootId)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (mPaused) {
     return;
   }
 
   if (mWrBridge) {
-    mWrBridge->ScheduleGenerateFrame();
+    mWrBridge->ScheduleGenerateFrame(aRenderRootId);
   } else {
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 // Go down the composite layer tree, setting properties to match their
 // content-side counterparts.
 /* static */ void
@@ -1836,19 +1837,22 @@ CompositorBridgeParent::RecvAdoptChild(c
 
   if (scheduleComposition) {
     ScheduleComposition();
   }
 
   if (childWrBridge) {
     MOZ_ASSERT(mWrBridge);
     RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+    RefPtr<wr::WebRenderAPI> contentRectApi = mWrBridge->GetContentRectWebRenderAPI();
     api = api->Clone();
+    contentRectApi = contentRectApi ? contentRectApi->Clone() : nullptr;
     wr::Epoch newEpoch = childWrBridge->UpdateWebRender(mWrBridge->CompositorScheduler(),
                                    api,
+                                   contentRectApi,
                                    mWrBridge->AsyncImageManager(),
                                    GetAnimationStorage(),
                                    mWrBridge->GetTextureFactoryIdentifier());
     // Pretend we composited, since parent CompositorBridgeParent was replaced.
     TimeStamp now = TimeStamp::Now();
     NotifyPipelineRendered(childWrBridge->PipelineId(), newEpoch, now, now, now);
   }
 
@@ -1899,23 +1903,35 @@ CompositorBridgeParent::AllocPWebRenderB
     mApzSampler->SetWebRenderWindowId(windowId);
   }
   RefPtr<wr::WebRenderAPI> api = wr::WebRenderAPI::Create(this, std::move(widget), windowId, aSize);
   if (!api) {
     mWrBridge = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
     mWrBridge.get()->AddRef(); // IPDL reference
     return mWrBridge;
   }
-  mAsyncImageManager = new AsyncImagePipelineManager(api->Clone());
-  RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
   wr::TransactionBuilder txn;
   txn.SetRootPipeline(aPipelineId);
   api->SendTransaction(txn);
+
+  RefPtr<wr::WebRenderAPI> contentRectApi;
+  if (gfxPrefs::WebRenderSplitRenderRoots()) {
+    contentRectApi = api->CreateDocument(aSize, 1);
+    wr::TransactionBuilder contentRectTxn;
+    contentRectTxn.SetRootPipeline(aPipelineId);
+    contentRectApi->SendTransaction(contentRectTxn); 
+  }
+
+  mAsyncImageManager = new AsyncImagePipelineManager(api->Clone(),
+                                                     contentRectApi ? contentRectApi->Clone() : nullptr);
+  RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
   RefPtr<CompositorAnimationStorage> animStorage = GetAnimationStorage();
-  mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr, std::move(api), std::move(asyncMgr), std::move(animStorage), mVsyncRate);
+  mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr,
+                                        std::move(api), std::move(contentRectApi),
+                                        std::move(asyncMgr), std::move(animStorage), mVsyncRate);
   mWrBridge.get()->AddRef(); // IPDL reference
 
   mCompositorScheduler = mWrBridge->CompositorScheduler();
   MOZ_ASSERT(mCompositorScheduler);
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     MOZ_ASSERT(sIndirectLayerTrees[mRootLayerTreeID].mWrBridge == nullptr);
     sIndirectLayerTrees[mRootLayerTreeID].mWrBridge = mWrBridge;
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -305,27 +305,27 @@ public:
 
   static void SetShadowProperties(Layer* aLayer);
 
   void NotifyChildCreated(LayersId aChild);
 
   void AsyncRender();
 
   // Can be called from any thread
-  void ScheduleRenderOnCompositorThread() override;
+  void ScheduleRenderOnCompositorThread(uint64_t aRenderRootid = 0) override;
   void SchedulePauseOnCompositorThread();
   void InvalidateOnCompositorThread();
   /**
    * Returns true if a surface was obtained and the resume succeeded; false
    * otherwise.
    */
   bool ScheduleResumeOnCompositorThread();
   bool ScheduleResumeOnCompositorThread(int x, int y, int width, int height);
 
-  void ScheduleComposition();
+  void ScheduleComposition(uint64_t aRenderRootid = 0);
   void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint,
       const FocusTarget& aFocusTarget,
       bool aScheduleComposite, uint32_t aPaintSequenceNumber,
       bool aIsRepeatTransaction, bool aHitTestUpdate);
 
   void UpdatePaintTime(LayerTransactionParent* aLayerTree,
                        const TimeDuration& aPaintTime) override;
 
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
@@ -218,34 +218,39 @@ CrossProcessCompositorBridgeParent::Allo
     MOZ_ASSERT(sIndirectLayerTrees[layersId].mWrBridge == nullptr);
     cbp = sIndirectLayerTrees[layersId].mParent;
     if (cbp) {
       root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge;
     }
   }
 
   RefPtr<wr::WebRenderAPI> api;
+  RefPtr<wr::WebRenderAPI> contentRectAPI;
   if (root) {
     api = root->GetWebRenderAPI();
+    contentRectAPI = root->GetContentRectWebRenderAPI();
   }
 
   if (!root || !api) {
     // This could happen when this function is called after CompositorBridgeParent destruction.
     // This was observed during Tab move between different windows.
     NS_WARNING(nsPrintfCString("Created child without a matching parent? root %p", root.get()).get());
     WebRenderBridgeParent* parent = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
     parent->AddRef(); // IPDL reference
     return parent;
   }
 
   api = api->Clone();
+  contentRectAPI = contentRectAPI ? contentRectAPI->Clone() : nullptr;
   RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager();
   RefPtr<CompositorAnimationStorage> animStorage = cbp->GetAnimationStorage();
   WebRenderBridgeParent* parent = new WebRenderBridgeParent(
-          this, aPipelineId, nullptr, root->CompositorScheduler(), std::move(api), std::move(holder), std::move(animStorage), cbp->GetVsyncInterval());
+          this, aPipelineId, nullptr, root->CompositorScheduler(),
+          std::move(api), std::move(contentRectAPI),
+          std::move(holder), std::move(animStorage), cbp->GetVsyncInterval());
   parent->AddRef(); // IPDL reference
 
   { // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees[layersId].mCrossProcessParent = this;
     sIndirectLayerTrees[layersId].mWrBridge = parent;
   }
 
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -107,17 +107,18 @@ struct CompositableTransaction
 struct AutoEndTransaction {
   explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
   ~AutoEndTransaction() { mTxn->End(); }
   CompositableTransaction* mTxn;
 };
 
 void
 ImageBridgeChild::UseTextures(CompositableClient* aCompositable,
-                              const nsTArray<TimedTextureClient>& aTextures)
+                              const nsTArray<TimedTextureClient>& aTextures,
+                              wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->GetIPCHandle());
   MOZ_ASSERT(aCompositable->IsConnected());
 
   AutoTArray<TimedTexture,4> textures;
 
   for (auto& t : aTextures) {
@@ -366,17 +367,17 @@ ImageBridgeChild::UpdateImageClient(RefP
 
   // If the client has become disconnected before this event was dispatched,
   // early return now.
   if (!client->IsConnected()) {
     return;
   }
 
   BeginTransaction();
-  client->UpdateImage(aContainer, Layer::CONTENT_OPAQUE);
+  client->UpdateImage(aContainer, Layer::CONTENT_OPAQUE, wr::kRenderRootUnknown);
   EndTransaction();
 }
 
 void
 ImageBridgeChild::UpdateAsyncCanvasRendererSync(SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper)
 {
   AutoCompleteTask complete(aTask);
 
@@ -410,17 +411,18 @@ ImageBridgeChild::UpdateAsyncCanvasRende
 {
   MOZ_ASSERT(aWrapper);
 
   if (!CanSend()) {
     return;
   }
 
   BeginTransaction();
-  aWrapper->GetCanvasClient()->Updated();
+  // TODO wr::RenderRoot::Unknown
+  aWrapper->GetCanvasClient()->Updated(wr::RenderRoot::Default);
   EndTransaction();
 }
 
 void
 ImageBridgeChild::FlushAllImagesSync(SynchronousTask* aTask,
                                      ImageClient* aClient,
                                      ImageContainer* aContainer)
 {
@@ -1065,17 +1067,18 @@ ImageBridgeChild::DestroyInTransaction(P
 bool
 ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle)
 {
   return IBCAddOpDestroy(mTxn, OpDestroy(aHandle));
 }
 
 void
 ImageBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
-                                                TextureClient* aTexture)
+                                                TextureClient* aTexture,
+                                                wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(CanSend());
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aTexture->IsSharedWithCompositor());
   MOZ_ASSERT(aCompositable->IsConnected());
   if (!aTexture || !aTexture->IsSharedWithCompositor() || !aCompositable->IsConnected()) {
     return;
   }
--- a/gfx/layers/ipc/ImageBridgeChild.h
+++ b/gfx/layers/ipc/ImageBridgeChild.h
@@ -265,17 +265,18 @@ public:
                        ImageContainer* aImageContainer) override;
 
   virtual bool UsesImageBridge() const override { return true; }
 
   /**
    * See CompositableForwarder::UseTextures
    */
   virtual void UseTextures(CompositableClient* aCompositable,
-                           const nsTArray<TimedTextureClient>& aTextures) override;
+                           const nsTArray<TimedTextureClient>& aTextures,
+                           wr::RenderRoot aRenderRoot) override;
   virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                          TextureClient* aClientOnBlack,
                                          TextureClient* aClientOnWhite) override;
 
   void ReleaseCompositable(const CompositableHandle& aHandle) override;
 
   void ForgetImageContainer(const CompositableHandle& aHandle);
 
@@ -292,17 +293,18 @@ public:
   void NotifyNotUsed(uint64_t aTextureId, uint64_t aFwdTransactionId);
 
   virtual void CancelWaitForRecycle(uint64_t aTextureId) override;
 
   virtual bool DestroyInTransaction(PTextureChild* aTexture) override;
   bool DestroyInTransaction(const CompositableHandle& aHandle);
 
   virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
-                                             TextureClient* aTexture) override;
+                                             TextureClient* aTexture,
+                                             wr::RenderRoot aRenderRoot) override;
 
   virtual void UseTiledLayerBuffer(CompositableClient* aCompositable,
                                    const SurfaceDescriptorTiles& aTileLayerDescriptor) override
   {
     MOZ_CRASH("should not be called");
   }
 
   virtual void UpdateTextureRegion(CompositableClient* aCompositable,
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -19,48 +19,74 @@ using mozilla::layers::ScrollUpdatesMap 
 using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::wr::BuiltDisplayListDescriptor from "mozilla/webrender/webrender_ffi.h";
 using mozilla::wr::IdNamespace from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::MaybeIdNamespace from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::wr::ExternalImageKeyPair from "mozilla/webrender/WebRenderTypes.h";
+using mozilla::wr::RenderRoot from "mozilla/webrender/WebRenderTypes.h";
 using mozilla::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
 using mozilla::layers::FocusTarget from "mozilla/layers/FocusTarget.h";
 using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
 
 namespace mozilla {
 namespace layers {
 
 sync protocol PWebRenderBridge
 {
   manager PCompositorBridge;
 
 parent:
-  sync EnsureConnected()
+  sync EnsureConnected(RenderRoot aRenderRoot)
     returns (TextureFactoryIdentifier textureFactoryIdentifier, MaybeIdNamespace maybeIdNamespace);
 
   async NewCompositable(CompositableHandle handle, TextureInfo info);
   async ReleaseCompositable(CompositableHandle compositable);
 
   async DeleteCompositorAnimations(uint64_t[] aIds);
-  async SetDisplayList(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
-                       LayoutSize aContentSize, ByteBuf aDL, BuiltDisplayListDescriptor aDLDesc,
-                       WebRenderScrollData aScrollData,
-                       OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems,
-                       IdNamespace aIdNamespace, bool containsSVGGroup, TimeStamp refreshStartTime, TimeStamp txnStartTime, nsCString txnURL, TimeStamp fwdTime);
+  async SetDisplayList(IntRect aDefaultRect,
+                       WebRenderParentCommand[] aDefaultCommands,
+                       LayoutSize aDefaultContentSize,
+                       ByteBuf aDefaultDL,
+                       BuiltDisplayListDescriptor aDefaultDLDesc,
+                       OpUpdateResource[] aDefaultResourceUpdates,
+                       RefCountedShmem[] aDefaultSmallShmems,
+                       Shmem[] aDefaultLargeShmems,
+                       bool aProcessContentData,
+                       IntRect aContentRect,
+                       WebRenderParentCommand[] aContentCommands,
+                       LayoutSize aContentContentSize,
+                       ByteBuf aContentDL,
+                       BuiltDisplayListDescriptor aContentDLDesc,
+                       OpUpdateResource[] aContentResourceUpdates,
+                       RefCountedShmem[] aContentSmallShmems,
+                       Shmem[] aContentLargeShmems,
+                       OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+                       WebRenderScrollData aScrollData, IdNamespace aIdNamespace, bool containsSVGGroup,
+                       TimeStamp refreshStartTime, TimeStamp txnStartTime, nsCString txnURL, TimeStamp fwdTime);
   async EmptyTransaction(FocusTarget focusTarget, ScrollUpdatesMap scrollUpdates, uint32_t aPaintSequenceNumber,
-                         WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
-                         OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems,
-                         IdNamespace aIdNamespace, TimeStamp refreshStartTime, TimeStamp txnStartTime, nsCString txnURL, TimeStamp fwdTime);
+                         WebRenderParentCommand[] aDefaultCommands,
+                         OpUpdateResource[] aDefaultResourceUpdates,
+                         RefCountedShmem[] aDefaultSmallShmems,
+                         Shmem[] aDefaultLargeShmems,
+                         WebRenderParentCommand[] aContentCommands,
+                         OpUpdateResource[] aContentResourceUpdates,
+                         RefCountedShmem[] aContentSmallShmems,
+                         Shmem[] aContentLargeShmems,
+                         OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+                         IdNamespace aIdNamespace, TimeStamp refreshStartTime, TimeStamp txnStartTime,
+                         nsCString txnURL, TimeStamp fwdTime);
   async SetFocusTarget(FocusTarget focusTarget);
-  async UpdateResources(OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems);
-  async ParentCommands(WebRenderParentCommand[] commands);
+  async UpdateResources(OpUpdateResource[] aResourceUpdates,
+                        RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems,
+                        RenderRoot aRenderRoot);
+  async ParentCommands(WebRenderParentCommand[] commands, RenderRoot aRenderRoot);
   sync GetSnapshot(PTexture texture);
   async SetLayersObserverEpoch(LayersObserverEpoch childEpoch);
   async ClearCachedResources();
   // Schedule a composite if one isn't already scheduled.
   async ScheduleComposite();
   // Save the frame capture to disk
   async Capture();
 
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -457,17 +457,18 @@ ShadowLayerForwarder::UpdateTextureRegio
   mTxn->AddNoSwapPaint(
     CompositableOperation(
       aCompositable->GetIPCHandle(),
       OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion)));
 }
 
 void
 ShadowLayerForwarder::UseTextures(CompositableClient* aCompositable,
-                                  const nsTArray<TimedTextureClient>& aTextures)
+                                  const nsTArray<TimedTextureClient>& aTextures,
+                                  wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
   AutoTArray<TimedTexture,4> textures;
@@ -544,17 +545,18 @@ ShadowLayerForwarder::DestroyInTransacti
 bool
 ShadowLayerForwarder::DestroyInTransaction(const CompositableHandle& aHandle)
 {
   return AddOpDestroy(mTxn, OpDestroy(aHandle));
 }
 
 void
 ShadowLayerForwarder::RemoveTextureFromCompositable(CompositableClient* aCompositable,
-                                                    TextureClient* aTexture)
+                                                    TextureClient* aTexture,
+                                                    wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aTexture->GetIPDLActor());
   MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() == mShadowManager->GetIPCChannel());
   if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
     // We don't have an actor anymore, don't try to use it!
     return;
--- a/gfx/layers/ipc/ShadowLayers.h
+++ b/gfx/layers/ipc/ShadowLayers.h
@@ -218,31 +218,33 @@ public:
   void UseTiledLayerBuffer(CompositableClient* aCompositable,
                                    const SurfaceDescriptorTiles& aTileLayerDescriptor) override;
 
   void ReleaseCompositable(const CompositableHandle& aHandle) override;
   bool DestroyInTransaction(PTextureChild* aTexture) override;
   bool DestroyInTransaction(const CompositableHandle& aHandle);
 
   virtual void RemoveTextureFromCompositable(CompositableClient* aCompositable,
-                                             TextureClient* aTexture) override;
+                                             TextureClient* aTexture,
+                                             wr::RenderRoot aRenderRoot) override;
 
   /**
    * Communicate to the compositor that aRegion in the texture identified by aLayer
    * and aIdentifier has been updated to aThebesBuffer.
    */
   virtual void UpdateTextureRegion(CompositableClient* aCompositable,
                                    const ThebesBufferData& aThebesBufferData,
                                    const nsIntRegion& aUpdatedRegion) override;
 
   /**
    * See CompositableForwarder::UseTextures
    */
   virtual void UseTextures(CompositableClient* aCompositable,
-                           const nsTArray<TimedTextureClient>& aTextures) override;
+                           const nsTArray<TimedTextureClient>& aTextures,
+                           wr::RenderRoot aRenderRoot) override;
   virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                          TextureClient* aClientOnBlack,
                                          TextureClient* aClientOnWhite) override;
 
   /**
    * Used for debugging to tell the compositor how long this frame took to paint.
    */
   void SendPaintTime(TransactionId aId, TimeDuration aPaintTime);
--- a/gfx/layers/ipc/SharedSurfacesChild.cpp
+++ b/gfx/layers/ipc/SharedSurfacesChild.cpp
@@ -17,33 +17,37 @@
 namespace mozilla {
 namespace layers {
 
 using namespace mozilla::gfx;
 
 /* static */ UserDataKey SharedSurfacesChild::sSharedKey;
 
 SharedSurfacesChild::ImageKeyData::ImageKeyData(WebRenderLayerManager* aManager,
-                                                const wr::ImageKey& aImageKey)
+                                                const wr::ImageKey& aImageKey,
+                                                wr::RenderRoot aRenderRoot)
   : mManager(aManager)
   , mImageKey(aImageKey)
+  , mRenderRoot(aRenderRoot)
 { }
 
 SharedSurfacesChild::ImageKeyData::ImageKeyData(SharedSurfacesChild::ImageKeyData&& aOther)
   : mManager(std::move(aOther.mManager))
   , mDirtyRect(std::move(aOther.mDirtyRect))
   , mImageKey(aOther.mImageKey)
+  , mRenderRoot(aOther.mRenderRoot)
 { }
 
 SharedSurfacesChild::ImageKeyData&
 SharedSurfacesChild::ImageKeyData::operator=(SharedSurfacesChild::ImageKeyData&& aOther)
 {
   mManager = std::move(aOther.mManager);
   mDirtyRect = std::move(aOther.mDirtyRect);
   mImageKey = aOther.mImageKey;
+  mRenderRoot = aOther.mRenderRoot;
   return *this;
 }
 
 SharedSurfacesChild::ImageKeyData::~ImageKeyData()
 { }
 
 void
 SharedSurfacesChild::ImageKeyData::MergeDirtyRect(const Maybe<IntRect>& aDirtyRect)
@@ -144,17 +148,17 @@ SharedSurfacesChild::SharedUserData::Upd
       // We don't have the resource update queue for this manager, so just
       // accumulate the dirty rects until it is requested.
       entry.MergeDirtyRect(aDirtyRect);
     }
   }
 
   if (!found) {
     key = aManager->WrBridge()->GetNextImageKey();
-    ImageKeyData data(aManager, key);
+    ImageKeyData data(aManager, key, aResources.GetRenderRoot());
     mKeys.AppendElement(std::move(data));
     aResources.AddExternalImage(mId, key);
   }
 
   return key;
 }
 
 /* static */ SourceSurfaceSharedData*
@@ -399,17 +403,17 @@ SharedSurfacesChild::Share(SourceSurface
 SharedSurfacesChild::Unshare(const wr::ExternalImageId& aId,
                              bool aReleaseId,
                              nsTArray<ImageKeyData>& aKeys)
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   for (const auto& entry : aKeys) {
     if (!entry.mManager->IsDestroyed()) {
-      entry.mManager->AddImageKeyForDiscard(entry.mImageKey);
+      entry.mManager->AddImageKeyForDiscard(entry.mImageKey, entry.mRenderRoot);
     }
   }
 
   if (!aReleaseId) {
     // We don't own the external image ID itself.
     return;
   }
 
@@ -471,18 +475,19 @@ SharedSurfacesChild::UpdateAnimation(Ima
   SharedSurfacesAnimation* anim =
     aContainer->EnsureSharedSurfacesAnimation();
   MOZ_ASSERT(anim);
 
   return anim->SetCurrentFrame(aSurface, sharedSurface, aDirtyRect);
 }
 
 AnimationImageKeyData::AnimationImageKeyData(WebRenderLayerManager* aManager,
-                                             const wr::ImageKey& aImageKey)
-  : SharedSurfacesChild::ImageKeyData(aManager, aImageKey)
+                                             const wr::ImageKey& aImageKey,
+                                             wr::RenderRoot aRenderRoot)
+  : SharedSurfacesChild::ImageKeyData(aManager, aImageKey, aRenderRoot)
   , mRecycling(false)
 { }
 
 AnimationImageKeyData::AnimationImageKeyData(AnimationImageKeyData&& aOther)
   : SharedSurfacesChild::ImageKeyData(std::move(aOther))
   , mPendingRelease(std::move(aOther.mPendingRelease))
   , mRecycling(aOther.mRecycling)
 { }
@@ -518,17 +523,17 @@ SharedSurfacesAnimation::Destroy()
     return;
   }
 
   for (const auto& entry : mKeys) {
     MOZ_ASSERT(!entry.mManager->IsDestroyed());
     if (entry.mRecycling) {
       entry.mManager->DeregisterAsyncAnimation(entry.mImageKey);
     }
-    entry.mManager->AddImageKeyForDiscard(entry.mImageKey);
+    entry.mManager->AddImageKeyForDiscard(entry.mImageKey, entry.mRenderRoot);
   }
 
   mKeys.Clear();
 }
 
 void
 SharedSurfacesAnimation::HoldSurfaceForRecycling(AnimationImageKeyData& aEntry,
                                                  SourceSurface* aParentSurface,
@@ -567,17 +572,19 @@ SharedSurfacesAnimation::SetCurrentFrame
     --i;
     AnimationImageKeyData& entry = mKeys[i];
     MOZ_ASSERT(!entry.mManager->IsDestroyed());
 
     entry.MergeDirtyRect(Some(aDirtyRect));
     Maybe<IntRect> dirtyRect = entry.TakeDirtyRect();
     if (dirtyRect) {
       HoldSurfaceForRecycling(entry, aParentSurface, aSurface);
-      auto& resourceUpdates = entry.mManager->AsyncResourceUpdates();
+      auto& resourceUpdates = entry.mRenderRoot == wr::RenderRoot::Content ?
+        entry.mManager->AsyncResourceUpdates().SubQueue() :
+        entry.mManager->AsyncResourceUpdates();
       resourceUpdates.UpdateExternalImage(mId, entry.mImageKey,
                                           ViewAs<ImagePixel>(dirtyRect.ref()));
     }
   }
 
   return NS_OK;
 }
 
@@ -631,17 +638,17 @@ SharedSurfacesAnimation::UpdateKey(Sourc
       aKey = entry.mImageKey;
       found = true;
       break;
     }
   }
 
   if (!found) {
     aKey = aManager->WrBridge()->GetNextImageKey();
-    AnimationImageKeyData data(aManager, aKey);
+    AnimationImageKeyData data(aManager, aKey, aResources.GetRenderRoot());
     HoldSurfaceForRecycling(data, aParentSurface, aSurface);
     mKeys.AppendElement(std::move(data));
     aResources.AddExternalImage(mId, aKey);
   }
 
   return NS_OK;
 }
 
--- a/gfx/layers/ipc/SharedSurfacesChild.h
+++ b/gfx/layers/ipc/SharedSurfacesChild.h
@@ -101,17 +101,18 @@ public:
 
   static nsresult UpdateAnimation(ImageContainer* aContainer,
                                   gfx::SourceSurface* aSurface,
                                   const gfx::IntRect& aDirtyRect);
 
   class ImageKeyData {
   public:
     ImageKeyData(WebRenderLayerManager* aManager,
-                 const wr::ImageKey& aImageKey);
+                 const wr::ImageKey& aImageKey,
+                 wr::RenderRoot aRenderRoot);
     ~ImageKeyData();
 
     ImageKeyData(ImageKeyData&& aOther);
     ImageKeyData& operator=(ImageKeyData&& aOther);
     ImageKeyData(const ImageKeyData&) = delete;
     ImageKeyData& operator=(const ImageKeyData&) = delete;
 
     void MergeDirtyRect(const Maybe<gfx::IntRect>& aDirtyRect);
@@ -119,16 +120,17 @@ public:
     Maybe<gfx::IntRect> TakeDirtyRect()
     {
       return std::move(mDirtyRect);
     }
 
     RefPtr<WebRenderLayerManager> mManager;
     Maybe<gfx::IntRect> mDirtyRect;
     wr::ImageKey mImageKey;
+    wr::RenderRoot mRenderRoot;
   };
 
 private:
   SharedSurfacesChild() = delete;
   ~SharedSurfacesChild() = delete;
 
   friend class SharedSurfacesAnimation;
 
@@ -195,17 +197,18 @@ private:
 
   static gfx::UserDataKey sSharedKey;
 };
 
 class AnimationImageKeyData final : public SharedSurfacesChild::ImageKeyData
 {
 public:
   AnimationImageKeyData(WebRenderLayerManager* aManager,
-               const wr::ImageKey& aImageKey);
+                        const wr::ImageKey& aImageKey,
+                        wr::RenderRoot aRenderRoot);
 
   ~AnimationImageKeyData();
 
   AnimationImageKeyData(AnimationImageKeyData&& aOther);
   AnimationImageKeyData& operator=(AnimationImageKeyData&& aOther);
 
   AutoTArray<RefPtr<gfx::SourceSurface>, 2> mPendingRelease;
   bool mRecycling;
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -38,18 +38,20 @@ AsyncImagePipelineManager::AsyncImagePip
 AsyncImagePipelineManager::PipelineUpdates::PipelineUpdates(RefPtr<wr::WebRenderPipelineInfo> aPipelineInfo,
                                                             const uint64_t aUpdatesCount,
                                                             const bool aRendered)
   : mPipelineInfo(aPipelineInfo)
   , mUpdatesCount(aUpdatesCount)
   , mRendered(aRendered)
 {}
 
-AsyncImagePipelineManager::AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi)
+AsyncImagePipelineManager::AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi,
+                                                     already_AddRefed<wr::WebRenderAPI>&& aContentRectApi)
  : mApi(aApi)
+ , mContentRectApi(aContentRectApi)
  , mIdNamespace(mApi->GetNamespace())
  , mUseTripleBuffering(mApi->GetUseTripleBuffering())
  , mResourceId(0)
  , mAsyncImageEpoch{0}
  , mWillGenerateFrame(false)
  , mDestroyed(false)
  , mUpdatesLock("UpdatesLock")
  , mUpdatesCount(0)
@@ -63,35 +65,46 @@ AsyncImagePipelineManager::~AsyncImagePi
 }
 
 void
 AsyncImagePipelineManager::Destroy()
 {
   MOZ_ASSERT(!mDestroyed);
   mApi = nullptr;
   mPipelineTexturesHolders.Clear();
+  mContentRectApi = nullptr;
   mDestroyed = true;
 }
 
 void
-AsyncImagePipelineManager::SetWillGenerateFrame()
+AsyncImagePipelineManager::SetWillGenerateFrame(wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
-  mWillGenerateFrame = true;
+  if (aRenderRoot == wr::RenderRoot::Content) {
+    mWillGenerateFrameContentRect = true;
+  } else {
+    mWillGenerateFrame = true;
+  }
 }
 
 bool
-AsyncImagePipelineManager::GetAndResetWillGenerateFrame()
+AsyncImagePipelineManager::GetAndResetWillGenerateFrame(wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
-  bool ret = mWillGenerateFrame;
-  mWillGenerateFrame = false;
-  return ret;
+  if (aRenderRoot == wr::RenderRoot::Content) {
+    bool ret = mWillGenerateFrameContentRect;
+    mWillGenerateFrameContentRect = false;
+    return ret;
+  } else {
+    bool ret = mWillGenerateFrame;
+    mWillGenerateFrame = false;
+    return ret;
+  }
 }
 
 wr::ExternalImageId
 AsyncImagePipelineManager::GetNextExternalImageId()
 {
   static uint32_t sNextId = 0;
   ++sNextId;
   MOZ_RELEASE_ASSERT(sNextId != UINT32_MAX);
@@ -153,26 +166,29 @@ AsyncImagePipelineManager::GetWrBridge(c
     MOZ_ASSERT(holder->mDestroyedEpoch.isNothing());
     return holder->mWrBridge;
   }
 
   return nullptr;
 }
 
 void
-AsyncImagePipelineManager::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost)
+AsyncImagePipelineManager::AddAsyncImagePipeline(const wr::PipelineId& aPipelineId,
+                                                 WebRenderImageHost* aImageHost,
+                                                 wr::RenderRoot aRenderRoot)
 {
   if (mDestroyed) {
     return;
   }
   MOZ_ASSERT(aImageHost);
   uint64_t id = wr::AsUint64(aPipelineId);
 
   MOZ_ASSERT(!mAsyncImagePipelines.Get(id));
   AsyncImagePipeline* holder = new AsyncImagePipeline();
+  holder->mRenderRoot = aRenderRoot;
   holder->mImageHost = aImageHost;
   mAsyncImagePipelines.Put(id, holder);
   AddPipeline(aPipelineId, /* aWrBridge */ nullptr);
 }
 
 void
 AsyncImagePipelineManager::RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn)
 {
@@ -302,17 +318,17 @@ AsyncImagePipelineManager::UpdateImageKe
   }
 
   if (useWrTextureWrapper && aPipeline->mWrTextureWrapper) {
     MOZ_ASSERT(canUpdate);
     // Reuse WebRenderTextureHostWrapper. With it, rendered frame could be updated
     // without batch re-creation.
     aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(wrTexture);
     // Ensure frame generation.
-    SetWillGenerateFrame();
+    SetWillGenerateFrame(aPipeline->mRenderRoot);
   } else {
     if (useWrTextureWrapper) {
       aPipeline->mWrTextureWrapper = new WebRenderTextureHostWrapper(this);
       aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(wrTexture);
     }
     Range<wr::ImageKey> keys(&aKeys[0], aKeys.Length());
     auto externalImageKey =
       aPipeline->mWrTextureWrapper ? aPipeline->mWrTextureWrapper->GetExternalImageKey() : wrTexture->GetExternalImageKey();
@@ -362,34 +378,40 @@ AsyncImagePipelineManager::UpdateWithout
 
   dSurf->Unmap();
 
   return Some(aOp);
 }
 
 void
 AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(wr::TransactionBuilder& aSceneBuilderTxn,
-                                                         wr::TransactionBuilder& aFastTxn)
+                                                         wr::TransactionBuilder& aFastTxn,
+                                                         wr::TransactionBuilder& aContentRectSceneBuilderTxn,
+                                                         wr::TransactionBuilder& aContentRectFastTxn)
 {
   if (mDestroyed || mAsyncImagePipelines.Count() == 0) {
     return;
   }
 
   wr::Epoch epoch = GetNextImageEpoch();
 
   // We use a pipeline with a very small display list for each video element.
   // Update each of them if needed.
   for (auto iter = mAsyncImagePipelines.Iter(); !iter.Done(); iter.Next()) {
     wr::PipelineId pipelineId = wr::AsPipelineId(iter.Key());
     AsyncImagePipeline* pipeline = iter.Data();
     // If aync image pipeline does not use ImageBridge, do not need to apply.
     if (!pipeline->mImageHost->GetAsyncRef()) {
       continue;
     }
-    ApplyAsyncImageForPipeline(epoch, pipelineId, pipeline, aSceneBuilderTxn, aFastTxn);
+    ApplyAsyncImageForPipeline(epoch, pipelineId, pipeline,
+                               pipeline->mRenderRoot == wr::RenderRoot::Content ?
+                                 aContentRectSceneBuilderTxn : aSceneBuilderTxn,
+                               pipeline->mRenderRoot == wr::RenderRoot::Content ?
+                                 aContentRectFastTxn : aFastTxn);
   }
 }
 
 void
 AsyncImagePipelineManager::ApplyAsyncImageForPipeline(const wr::Epoch& aEpoch,
                                                       const wr::PipelineId& aPipelineId,
                                                       AsyncImagePipeline* aPipeline,
                                                       wr::TransactionBuilder& aSceneBuilderTxn,
@@ -445,16 +467,17 @@ AsyncImagePipelineManager::ApplyAsyncIma
     if (aPipeline->mUseExternalImage) {
       MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
       Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
       aPipeline->mCurrentTexture->PushDisplayItems(builder,
                                                   wr::ToRoundedLayoutRect(rect),
                                                   wr::ToRoundedLayoutRect(rect),
                                                   aPipeline->mFilter,
                                                   range_keys);
+
       HoldExternalImage(aPipelineId, aEpoch, aPipeline->mCurrentTexture->AsWebRenderTextureHost());
     } else {
       MOZ_ASSERT(keys.Length() == 1);
       builder.PushImage(wr::ToRoundedLayoutRect(rect),
                         wr::ToRoundedLayoutRect(rect),
                         true,
                         aPipeline->mFilter,
                         keys[0]);
@@ -472,24 +495,26 @@ AsyncImagePipelineManager::ApplyAsyncIma
     LayerSize(aPipeline->mScBounds.Width(), aPipeline->mScBounds.Height()),
     aPipelineId, builderContentSize,
     dl.dl_desc, dl.dl);
 }
 
 void
 AsyncImagePipelineManager::ApplyAsyncImageForPipeline(const wr::PipelineId& aPipelineId,
                                                       wr::TransactionBuilder& aTxn,
-                                                      wr::TransactionBuilder& aTxnForImageBridge)
+                                                      wr::TransactionBuilder& aTxnForImageBridge,
+                                                      wr::RenderRoot aRenderRoot)
 {
   AsyncImagePipeline* pipeline = mAsyncImagePipelines.Get(wr::AsUint64(aPipelineId));
   if (!pipeline) {
     return;
   }
+  wr::WebRenderAPI* api = pipeline->mRenderRoot == wr::RenderRoot::Content ? mContentRectApi : mApi;
   wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
-  wr::AutoTransactionSender sender(mApi, &fastTxn);
+  wr::AutoTransactionSender sender(api, &fastTxn);
 
   // Transaction for async image pipeline that uses ImageBridge always need to be non low priority.
   auto& sceneBuilderTxn = pipeline->mImageHost->GetAsyncRef() ? aTxnForImageBridge : aTxn;
 
   // Use transaction of using non scene builder thread when ImageHost uses ImageBridge.
   // ApplyAsyncImagesOfImageBridge() handles transaction of adding and updating
   // wr::ImageKeys of ImageHosts that uses ImageBridge. Then AsyncImagePipelineManager
   // always needs to use non scene builder thread transaction for adding and updating
--- a/gfx/layers/wr/AsyncImagePipelineManager.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -34,17 +34,18 @@ class WebRenderImageHost;
 class WebRenderTextureHost;
 class WebRenderTextureHostWrapper;
 
 class AsyncImagePipelineManager final
 {
 public:
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncImagePipelineManager)
 
-  explicit AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi);
+  explicit AsyncImagePipelineManager(already_AddRefed<wr::WebRenderAPI>&& aApi,
+                                     already_AddRefed<wr::WebRenderAPI>&& aContentRectApi);
 
 protected:
   ~AsyncImagePipelineManager();
 
 public:
   void Destroy();
 
   void AddPipeline(const wr::PipelineId& aPipelineId, WebRenderBridgeParent* aWrBridge);
@@ -79,55 +80,65 @@ public:
         mCompositeUntilTime < aTimeStamp) {
       mCompositeUntilTime = aTimeStamp;
     }
   }
   TimeStamp GetCompositeUntilTime() const {
     return mCompositeUntilTime;
   }
 
-  void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost);
+  void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost,
+                             wr::RenderRoot aRenderRoot);
   void RemoveAsyncImagePipeline(const wr::PipelineId& aPipelineId,
                                 wr::TransactionBuilder& aTxn);
 
   void UpdateAsyncImagePipeline(const wr::PipelineId& aPipelineId,
                                 const LayoutDeviceRect& aScBounds,
                                 const gfx::Matrix4x4& aScTransform,
                                 const gfx::MaybeIntSize& aScaleToSize,
                                 const wr::ImageRendering& aFilter,
                                 const wr::MixBlendMode& aMixBlendMode);
-  void ApplyAsyncImagesOfImageBridge(wr::TransactionBuilder& aSceneBuilderTxn, wr::TransactionBuilder& aFastTxn);
-  void ApplyAsyncImageForPipeline(const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn, wr::TransactionBuilder& aTxnForImageBridge);
+  void ApplyAsyncImagesOfImageBridge(wr::TransactionBuilder& aSceneBuilderTxn,
+                                     wr::TransactionBuilder& aFastTxn,
+                                     wr::TransactionBuilder& aContentRectSceneBuilderTxn,
+                                     wr::TransactionBuilder& aContentRectFastTxn);
+  void ApplyAsyncImageForPipeline(const wr::PipelineId& aPipelineId,
+                                  wr::TransactionBuilder& aTxn,
+                                  wr::TransactionBuilder& aTxnForImageBridge,
+                                  wr::RenderRoot aRenderRoot);
 
   void SetEmptyDisplayList(const wr::PipelineId& aPipelineId,
                            wr::TransactionBuilder& aTxn,
                            wr::TransactionBuilder& aTxnForImageBridge);
 
   void AppendImageCompositeNotification(const ImageCompositeNotificationInfo& aNotification)
   {
     mImageCompositeNotifications.AppendElement(aNotification);
   }
 
   void FlushImageNotifications(nsTArray<ImageCompositeNotificationInfo>* aNotifications)
   {
     aNotifications->AppendElements(std::move(mImageCompositeNotifications));
   }
 
-  void SetWillGenerateFrame();
-  bool GetAndResetWillGenerateFrame();
+  void SetWillGenerateFrame(wr::RenderRoot aRenderRoot);
+  bool GetAndResetWillGenerateFrame(wr::RenderRoot aRenderRoot);
 
   wr::ExternalImageId GetNextExternalImageId();
 
 private:
   void ProcessPipelineRendered(const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch, const uint64_t aUpdatesCount);
   void ProcessPipelineRemoved(const wr::PipelineId& aPipelineId, const uint64_t aUpdatesCount);
 
   wr::Epoch GetNextImageEpoch();
   uint32_t GetNextResourceId() { return ++mResourceId; }
-  wr::IdNamespace GetNamespace() { return mIdNamespace; }
+  wr::IdNamespace GetNamespace()
+  {
+    return mIdNamespace;
+  }
   wr::ImageKey GenerateImageKey()
   {
     wr::ImageKey key;
     key.mNamespace = GetNamespace();
     key.mHandle = GetNextResourceId();
     return key;
   }
 
@@ -184,16 +195,17 @@ private:
       mScBounds = aScBounds;
       mScTransform = aScTransform;
       mScaleToSize = aScaleToSize;
       mFilter = aFilter;
       mMixBlendMode = aMixBlendMode;
     }
 
     bool mInitialised;
+    wr::RenderRoot mRenderRoot;
     bool mIsChanged;
     bool mUseExternalImage;
     LayoutDeviceRect mScBounds;
     gfx::Matrix4x4 mScTransform;
     gfx::MaybeIntSize mScaleToSize;
     wr::ImageRendering mFilter;
     wr::MixBlendMode mMixBlendMode;
     RefPtr<WebRenderImageHost> mImageHost;
@@ -220,24 +232,26 @@ private:
                              TextureHost::ResourceUpdateOp,
                              wr::TransactionBuilder& aTxn);
 
   // If texture is direct binding texture, keep it until it is not used by GPU.
   void HoldUntilNotUsedByGPU(const CompositableTextureHostRef& aTextureHost, uint64_t aUpdatesCount);
   void CheckForTextureHostsNotUsedByGPU();
 
   RefPtr<wr::WebRenderAPI> mApi;
+  RefPtr<wr::WebRenderAPI> mContentRectApi;
   const wr::IdNamespace mIdNamespace;
   const bool mUseTripleBuffering;
   uint32_t mResourceId;
 
   nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder> mPipelineTexturesHolders;
   nsClassHashtable<nsUint64HashKey, AsyncImagePipeline> mAsyncImagePipelines;
   wr::Epoch mAsyncImageEpoch;
   bool mWillGenerateFrame;
+  bool mWillGenerateFrameContentRect;
   bool mDestroyed;
 
   // Render time for the current composition.
   TimeStamp mCompositionTime;
 
   // When nonnull, during rendering, some compositable indicated that it will
   // change its rendering at this time. In order not to miss it, we composite
   // on every vsync until this time occurs (this is the latest such time).
--- a/gfx/layers/wr/IpcResourceUpdateQueue.cpp
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.cpp
@@ -264,31 +264,37 @@ ShmSegmentsReader::Read(const layers::Of
     srcCursor += copyRange;
     remainingBytesToCopy -= copyRange;
   }
 
   return aInto.Length() - initialLength == aRange.length();
 }
 
 IpcResourceUpdateQueue::IpcResourceUpdateQueue(layers::WebRenderBridgeChild* aAllocator,
-                                               size_t aChunkSize)
+                                               size_t aChunkSize,
+                                               wr::RenderRoot aRenderRoot)
 : mWriter(aAllocator, aChunkSize)
+, mRenderRoot(aRenderRoot)
 {}
 
 IpcResourceUpdateQueue::IpcResourceUpdateQueue(IpcResourceUpdateQueue&& aOther) noexcept
   : mWriter(std::move(aOther.mWriter))
   , mUpdates(std::move(aOther.mUpdates))
+  , mSubQueue(std::move(aOther.mSubQueue))
+  , mRenderRoot(aOther.mRenderRoot)
 { }
 
 IpcResourceUpdateQueue&
 IpcResourceUpdateQueue::operator=(IpcResourceUpdateQueue&& aOther) noexcept
 {
   MOZ_ASSERT(IsEmpty(), "Will forget existing updates!");
   mWriter = std::move(aOther.mWriter);
   mUpdates = std::move(aOther.mUpdates);
+  mSubQueue = std::move(aOther.mSubQueue);
+  mRenderRoot = aOther.mRenderRoot;
   return *this;
 }
 
 bool
 IpcResourceUpdateQueue::AddImage(ImageKey key, const ImageDescriptor& aDescriptor,
                                  Range<uint8_t> aBytes)
 {
   auto bytes = mWriter.Write(aBytes);
@@ -456,16 +462,19 @@ IpcResourceUpdateQueue::IsEmpty() const
   return false;
 }
 
 void
 IpcResourceUpdateQueue::Clear()
 {
   mWriter.Clear();
   mUpdates.Clear();
+  if (mSubQueue) {
+    mSubQueue->Clear();
+  }
 }
 
 //static
 void
 IpcResourceUpdateQueue::ReleaseShmems(ipc::IProtocol* aShmAllocator, nsTArray<layers::RefCountedShmem>& aShms)
 {
   for (auto& shm : aShms) {
     if (RefCountedShm::IsValid(shm) && RefCountedShm::Release(shm) == 0) {
--- a/gfx/layers/wr/IpcResourceUpdateQueue.h
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.h
@@ -39,16 +39,17 @@ public:
   }
 
   void Flush(nsTArray<layers::RefCountedShmem>& aSmallAllocs, nsTArray<mozilla::ipc::Shmem>& aLargeAllocs);
 
   void Clear();
   bool IsEmpty() const;
 
   layers::WebRenderBridgeChild* WrBridge() const { return mShmAllocator; }
+  size_t ChunkSize() const { return mChunkSize; }
 
 protected:
   bool AllocChunk();
   layers::OffsetRange AllocLargeChunk(size_t aSize);
 
   nsTArray<layers::RefCountedShmem> mSmallAllocs;
   nsTArray<mozilla::ipc::Shmem> mLargeAllocs;
   layers::WebRenderBridgeChild* mShmAllocator;
@@ -74,17 +75,40 @@ protected:
 class IpcResourceUpdateQueue {
 public:
   // Because we are using shmems, the size should be a multiple of the page size.
   // Each shmem has two guard pages, and the minimum shmem size (at least one Windows)
   // is 64k which is already quite large for a lot of the resources we use here.
   // The RefCountedShmem type used to allocate the chunks keeps a 16 bytes header
   // in the buffer which we account for here as well.
   // So we pick 64k - 2 * 4k - 16 = 57328 bytes as the default alloc size.
-  explicit IpcResourceUpdateQueue(layers::WebRenderBridgeChild* aAllocator, size_t aChunkSize = 57328);
+  explicit IpcResourceUpdateQueue(layers::WebRenderBridgeChild* aAllocator,
+                                  size_t aChunkSize = 57328,
+                                  wr::RenderRoot aRenderRoot = wr::RenderRoot::Default);
+
+  // Although resource updates don't belong to a particular document/render root
+  // in any concrete way, they still end up being tied to a render root because
+  // we need to know which WR document to generate a frame for when they change.
+  IpcResourceUpdateQueue& SubQueue() {
+    MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default);
+    if (!mSubQueue) {
+      mSubQueue = MakeUnique<IpcResourceUpdateQueue>(mWriter.WrBridge(),
+                                                     mWriter.ChunkSize(),
+                                                     wr::RenderRoot::Content);
+    }
+    return *mSubQueue;
+  }
+
+  bool HasSubQueue() {
+    return !!mSubQueue;
+  }
+
+  wr::RenderRoot GetRenderRoot() {
+    return mRenderRoot;
+  }
 
   IpcResourceUpdateQueue(IpcResourceUpdateQueue&& aOther) noexcept;
   IpcResourceUpdateQueue& operator=(IpcResourceUpdateQueue&& aOther) noexcept;
 
   IpcResourceUpdateQueue(const IpcResourceUpdateQueue& aOther) = delete;
   IpcResourceUpdateQueue& operator=(const IpcResourceUpdateQueue& aOther) = delete;
 
   bool AddImage(wr::ImageKey aKey,
@@ -144,14 +168,16 @@ public:
 
   bool IsEmpty() const;
 
   static void ReleaseShmems(mozilla::ipc::IProtocol*, nsTArray<layers::RefCountedShmem>& aShms);
   static void ReleaseShmems(mozilla::ipc::IProtocol*, nsTArray<mozilla::ipc::Shmem>& aShms);
 protected:
   ShmSegmentsWriter mWriter;
   nsTArray<layers::OpUpdateResource> mUpdates;
+  UniquePtr<IpcResourceUpdateQueue> mSubQueue;
+  wr::RenderRoot mRenderRoot;
 };
 
 } // namespace
 } // namespace
 
 #endif
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -94,16 +94,35 @@ StackingContextHelper::StackingContextHe
       mDeferredAncestorTransform = aParentSC.GetDeferredTransformMatrix();
     } else {
       // We are not deferring another transform, so we can just inherit the
       // parent stacking context's deferred data without any modification.
       mDeferredTransformItem = aParentSC.mDeferredTransformItem;
       mDeferredAncestorTransform = aParentSC.mDeferredAncestorTransform;
     }
   }
+
+  if (aParentSC.mSubContextHelper) {
+    mSubContextHelper = MakeUnique<StackingContextHelper>(*aParentSC.mSubContextHelper,
+                                                           aAsr,
+                                                           *aParentSC.mSubContextHelper->mBuilder,
+                                                           aFilters,
+                                                           aBounds,
+                                                           aBoundTransform,
+                                                           aAnimation,
+                                                           aOpacityPtr,
+                                                           aTransformPtr,
+                                                           aPerspectivePtr,
+                                                           aMixBlendMode,
+                                                           aBackfaceVisible,
+                                                           aIsPreserve3D,
+                                                           aDeferredTransformItem,
+                                                           aClipNodeId,
+                                                           aAnimated);
+  }
 }
 
 StackingContextHelper::~StackingContextHelper()
 {
   if (mBuilder) {
     mBuilder->PopStackingContext(mReferenceFrameId.isSome());
   }
 }
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -20,17 +20,17 @@ namespace mozilla {
 struct ActiveScrolledRoot;
 
 namespace layers {
 
 /**
  * This is a helper class that pushes/pops a stacking context, and manages
  * some of the coordinate space transformations needed.
  */
-class MOZ_RAII StackingContextHelper
+class StackingContextHelper
 {
 public:
   StackingContextHelper(const StackingContextHelper& aParentSC,
                         const ActiveScrolledRoot* aAsr,
                         wr::DisplayListBuilder& aBuilder,
                         const nsTArray<wr::WrFilterOp>& aFilters = nsTArray<wr::WrFilterOp>(),
                         const LayoutDeviceRect& aBounds = LayoutDeviceRect(),
                         const gfx::Matrix4x4* aBoundTransform = nullptr,
@@ -51,16 +51,35 @@ public:
   StackingContextHelper();
 
   // Pops the stacking context, if one was pushed during the constructor.
   ~StackingContextHelper();
 
   // Export the inherited scale
   gfx::Size GetInheritedScale() const { return mScale; }
 
+  // We create a StackingContextHelper for the content render root to parallel
+  // the StackingContextHelper for the chrome render root. This results in an
+  // equivalent chain of SCs when we actually transition to the content render root
+  // in an nsDisplayRenderRoot.
+  void CreateSubContextHelper(const StackingContextHelper& aParentSC,
+                               const nsTArray<wr::WrFilterOp>& aFilters)
+  {
+    MOZ_ASSERT(!mSubContextHelper);
+    mSubContextHelper = MakeUnique<StackingContextHelper>(aParentSC,
+                                                           nullptr,
+                                                           mBuilder->SubBuilder(),
+                                                           aFilters);
+  }
+
+  const StackingContextHelper& SubContextHelper() const
+  {
+    return *mSubContextHelper;
+  }
+
   const gfx::Matrix& GetInheritedTransform() const
   {
     return mInheritedTransform;
   }
 
   const gfx::Matrix& GetSnappingSurfaceTransform() const
   {
     return mSnappingSurfaceTransform;
@@ -123,14 +142,15 @@ private:
   // in the face of async scrolling). This behaviour of forcing a
   // WebRenderLayerScrollData item to be generated when the ASR changes is
   // implemented in WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList.
   Maybe<nsDisplayTransform*> mDeferredTransformItem;
   Maybe<gfx::Matrix4x4> mDeferredAncestorTransform;
 
   bool mIsPreserve3D;
   bool mRasterizeLocally;
+  UniquePtr<StackingContextHelper> mSubContextHelper;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif /* GFX_STACKINGCONTEXTHELPER_H */
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -27,17 +27,18 @@ WebRenderBridgeChild::WebRenderBridgeChi
   , mIsInClearCachedResources(false)
   , mIdNamespace{0}
   , mResourceId(0)
   , mPipelineId(aPipelineId)
   , mManager(nullptr)
   , mIPCOpen(false)
   , mDestroyed(false)
   , mFontKeysDeleted(0)
-  , mFontInstanceKeysDeleted(0)
+  , mContentRectFontKeysDeleted(0)
+  , mContentRectFontInstanceKeysDeleted(0)
 {
 }
 
 WebRenderBridgeChild::~WebRenderBridgeChild()
 {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mDestroyed);
 }
@@ -76,169 +77,236 @@ WebRenderBridgeChild::DoDestroy()
   // When this function is called from CompositorBridgeChild::Destroy().
   // mActiveResourceTracker is not cleared here, since it is
   // used by PersistentBufferProviderShared.
   mDestroyed = true;
   mManager = nullptr;
 }
 
 void
-WebRenderBridgeChild::AddWebRenderParentCommand(const WebRenderParentCommand& aCmd)
+WebRenderBridgeChild::AddWebRenderParentCommand(const WebRenderParentCommand& aCmd,
+                                                wr::RenderRoot aRenderRoot)
 {
-  mParentCommands.AppendElement(aCmd);
+  if (aRenderRoot == wr::RenderRoot::Content) {
+    mContentRectParentCommands.AppendElement(aCmd);
+  } else {
+    mParentCommands.AppendElement(aCmd);
+  }
 }
 
 void
 WebRenderBridgeChild::BeginTransaction()
 {
   MOZ_ASSERT(!mDestroyed);
 
   UpdateFwdTransactionId();
   mIsInTransaction = true;
 }
 
 void
-WebRenderBridgeChild::UpdateResources(wr::IpcResourceUpdateQueue& aResources)
+WebRenderBridgeChild::UpdateResources(wr::IpcResourceUpdateQueue& aResources,
+                                      wr::RenderRoot aRenderRoot)
 {
   if (!IPCOpen()) {
     aResources.Clear();
     return;
   }
 
   if (aResources.IsEmpty()) {
     return;
   }
 
   nsTArray<OpUpdateResource> resourceUpdates;
   nsTArray<RefCountedShmem> smallShmems;
   nsTArray<ipc::Shmem> largeShmems;
   aResources.Flush(resourceUpdates, smallShmems, largeShmems);
 
   this->SendUpdateResources(resourceUpdates, smallShmems,
-                            largeShmems);
+                            largeShmems, aRenderRoot);
 }
 
 void
-WebRenderBridgeChild::EndTransaction(const wr::LayoutSize& aContentSize,
-                                     wr::BuiltDisplayList& aDL,
-                                     wr::IpcResourceUpdateQueue& aResources,
-                                     const gfx::IntSize& aSize,
+WebRenderBridgeChild::EndTransaction(const wr::LayoutSize& aChromeContentSize,
+                                     wr::BuiltDisplayList& aChromeDL,
+                                     wr::IpcResourceUpdateQueue& aChromeResources,
+                                     const gfx::IntRect& aChromeRect,
+                                     bool aProcessContentData,
+                                     const wr::LayoutSize& aContentContentSize,
+                                     wr::BuiltDisplayList& aContentDL,
+                                     wr::IpcResourceUpdateQueue& aContentResources,
+                                     const gfx::IntRect& aContentRect,
                                      TransactionId aTransactionId,
                                      const WebRenderScrollData& aScrollData,
                                      bool aContainsSVGGroup,
                                      const mozilla::TimeStamp& aRefreshStartTime,
                                      const mozilla::TimeStamp& aTxnStartTime,
                                      const nsCString& aTxnURL)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mIsInTransaction);
 
-  ByteBuf dlData(aDL.dl.inner.data, aDL.dl.inner.length, aDL.dl.inner.capacity);
-  aDL.dl.inner.capacity = 0;
-  aDL.dl.inner.data = nullptr;
+  ByteBuf chromeDLData(aChromeDL.dl.inner.data, aChromeDL.dl.inner.length, aChromeDL.dl.inner.capacity);
+  aChromeDL.dl.inner.capacity = 0;
+  aChromeDL.dl.inner.data = nullptr;
+  ByteBuf contentDLData(aContentDL.dl.inner.data, aContentDL.dl.inner.length, aContentDL.dl.inner.capacity);
+  aContentDL.dl.inner.capacity = 0;
+  aContentDL.dl.inner.data = nullptr;
 
   TimeStamp fwdTime = TimeStamp::Now();
 
-  nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<RefCountedShmem> smallShmems;
-  nsTArray<ipc::Shmem> largeShmems;
-  aResources.Flush(resourceUpdates, smallShmems, largeShmems);
+
+  nsTArray<OpUpdateResource> chromeResourceUpdates;
+  nsTArray<RefCountedShmem> chromeSmallShmems;
+  nsTArray<ipc::Shmem> chromeLargeShmems;
+  aChromeResources.Flush(chromeResourceUpdates, chromeSmallShmems, chromeLargeShmems);
+  nsTArray<OpUpdateResource> contentResourceUpdates;
+  nsTArray<RefCountedShmem> contentSmallShmems;
+  nsTArray<ipc::Shmem> contentLargeShmems;
+  aContentResources.Flush(contentResourceUpdates, contentSmallShmems, contentLargeShmems);
 
-  this->SendSetDisplayList(aSize, mParentCommands, mDestroyedActors,
-                           GetFwdTransactionId(), aTransactionId,
-                           aContentSize, dlData, aDL.dl_desc, aScrollData,
-                           resourceUpdates, smallShmems, largeShmems,
-                           mIdNamespace, aContainsSVGGroup, aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime);
+  this->SendSetDisplayList(aChromeRect,
+                           mParentCommands,
+                           aChromeContentSize,
+                           chromeDLData,
+                           aChromeDL.dl_desc,
+                           chromeResourceUpdates,
+                           chromeSmallShmems,
+                           chromeLargeShmems,
+                           aProcessContentData,
+                           aContentRect,
+                           mContentRectParentCommands,
+                           aContentContentSize,
+                           contentDLData,
+                           aContentDL.dl_desc,
+                           contentResourceUpdates,
+                           contentSmallShmems,
+                           contentLargeShmems,
+                           mDestroyedActors,
+                           GetFwdTransactionId(),
+                           aTransactionId,
+                           aScrollData,
+                           mIdNamespace,
+                           aContainsSVGGroup,
+                           aRefreshStartTime,
+                           aTxnStartTime,
+                           aTxnURL,
+                           fwdTime);
 
   mParentCommands.Clear();
+  mContentRectParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 void
 WebRenderBridgeChild::EndEmptyTransaction(const FocusTarget& aFocusTarget,
                                           const ScrollUpdatesMap& aUpdates,
-                                          Maybe<wr::IpcResourceUpdateQueue>& aResources,
+                                          Maybe<wr::IpcResourceUpdateQueue>& aChromeResources,
                                           uint32_t aPaintSequenceNumber,
                                           TransactionId aTransactionId,
                                           const mozilla::TimeStamp& aRefreshStartTime,
                                           const mozilla::TimeStamp& aTxnStartTime,
                                           const nsCString& aTxnURL)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(mIsInTransaction);
 
   TimeStamp fwdTime = TimeStamp::Now();
 
-  nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<RefCountedShmem> smallShmems;
-  nsTArray<ipc::Shmem> largeShmems;
-  if (aResources) {
-    aResources->Flush(resourceUpdates, smallShmems, largeShmems);
-    aResources.reset();
+  nsTArray<OpUpdateResource> chromeResourceUpdates;
+  nsTArray<RefCountedShmem> chromeSmallShmems;
+  nsTArray<ipc::Shmem> chromeLargeShmems;
+  if (aChromeResources) {
+    aChromeResources->Flush(chromeResourceUpdates, chromeSmallShmems, chromeLargeShmems);
+  }
+
+  nsTArray<OpUpdateResource> contentResourceUpdates;
+  nsTArray<RefCountedShmem> contentSmallShmems;
+  nsTArray<ipc::Shmem> contentLargeShmems;
+  if (aChromeResources && aChromeResources->HasSubQueue()) {
+    aChromeResources->SubQueue().Flush(contentResourceUpdates, contentSmallShmems, contentLargeShmems);
+  }
+
+  if (aChromeResources) {
+    aChromeResources.reset();
   }
 
   this->SendEmptyTransaction(aFocusTarget, aUpdates, aPaintSequenceNumber,
-                             mParentCommands, mDestroyedActors,
+                             mParentCommands,
+                             chromeResourceUpdates, chromeSmallShmems, chromeLargeShmems,
+                             mContentRectParentCommands,
+                             contentResourceUpdates, contentSmallShmems, contentLargeShmems,
+                             mDestroyedActors,
                              GetFwdTransactionId(), aTransactionId,
-                             resourceUpdates, smallShmems, largeShmems,
                              mIdNamespace, aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime);
   mParentCommands.Clear();
+  mContentRectParentCommands.Clear();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 void
 WebRenderBridgeChild::ProcessWebRenderParentCommands()
 {
   MOZ_ASSERT(!mDestroyed);
 
-  if (mParentCommands.IsEmpty()) {
-    return;
+  if (!mParentCommands.IsEmpty()) {
+    this->SendParentCommands(mParentCommands, wr::RenderRoot::Default);
+    mParentCommands.Clear();
   }
-  this->SendParentCommands(mParentCommands);
-  mParentCommands.Clear();
+
+  if (!mContentRectParentCommands.IsEmpty()) {
+    this->SendParentCommands(mContentRectParentCommands, wr::RenderRoot::Content);
+    mContentRectParentCommands.Clear();
+  }
 }
 
 void
 WebRenderBridgeChild::AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
-                                                        const CompositableHandle& aHandle)
+                                                        const CompositableHandle& aHandle,
+                                                        wr::RenderRoot aRenderRoot)
 {
   AddWebRenderParentCommand(
-    OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ true));
+    OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ true),
+    aRenderRoot);
 }
 
 void
 WebRenderBridgeChild::AddPipelineIdForCompositable(const wr::PipelineId& aPipelineId,
-                                                   const CompositableHandle& aHandle)
+                                                   const CompositableHandle& aHandle,
+                                                   wr::RenderRoot aRenderRoot)
 {
   AddWebRenderParentCommand(
-    OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ false));
+    OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ false),
+    aRenderRoot);
 }
 
 void
-WebRenderBridgeChild::RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId)
+WebRenderBridgeChild::RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId,
+                                                      wr::RenderRoot aRenderRoot)
 {
   AddWebRenderParentCommand(
-    OpRemovePipelineIdForCompositable(aPipelineId));
+    OpRemovePipelineIdForCompositable(aPipelineId),
+    aRenderRoot);
 }
 
 wr::ExternalImageId
 WebRenderBridgeChild::GetNextExternalImageId()
 {
   wr::MaybeExternalImageId id = GetCompositorBridgeChild()->GetNextExternalImageId();
   MOZ_RELEASE_ASSERT(id.isSome());
   return id.value();
 }
 
 void
-WebRenderBridgeChild::ReleaseTextureOfImage(const wr::ImageKey& aKey)
+WebRenderBridgeChild::ReleaseTextureOfImage(const wr::ImageKey& aKey, wr::RenderRoot aRenderRoot)
 {
   AddWebRenderParentCommand(
-    OpReleaseTextureOfImage(aKey));
+    OpReleaseTextureOfImage(aKey),
+    aRenderRoot);
 }
 
 struct FontFileDataSink
 {
   wr::FontKey* mFontKey;
   WebRenderBridgeChild* mWrBridge;
   wr::IpcResourceUpdateQueue* mResources;
 };
@@ -268,119 +336,134 @@ WriteFontDescriptor(const uint8_t* aData
 void
 WebRenderBridgeChild::PushGlyphs(wr::DisplayListBuilder& aBuilder, Range<const wr::GlyphInstance> aGlyphs,
                                  gfx::ScaledFont* aFont, const wr::ColorF& aColor, const StackingContextHelper& aSc,
                                  const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip, bool aBackfaceVisible,
                                  const wr::GlyphOptions* aGlyphOptions)
 {
   MOZ_ASSERT(aFont);
 
-  wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont);
+  wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont,
+                                                      aBuilder.GetRenderRoot());
   MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle);
 
   aBuilder.PushText(aBounds,
                     aClip,
                     aBackfaceVisible,
                     aColor,
                     key,
                     aGlyphs,
                     aGlyphOptions);
 }
 
 wr::FontInstanceKey
 WebRenderBridgeChild::GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont,
+                                              wr::RenderRoot aRenderRoot,
                                               wr::IpcResourceUpdateQueue* aResources)
 {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aScaledFont);
   MOZ_ASSERT(aScaledFont->CanSerialize());
 
+  auto& fontInstanceKeys = aRenderRoot == wr::RenderRoot::Content ? mContentRectFontInstanceKeys : mFontInstanceKeys;
   wr::FontInstanceKey instanceKey = { wr::IdNamespace { 0 }, 0 };
-  if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) {
+  if (fontInstanceKeys.Get(aScaledFont, &instanceKey)) {
     return instanceKey;
   }
 
   Maybe<wr::IpcResourceUpdateQueue> resources =
     aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this));
   aResources = resources.ptrOr(aResources);
 
-  wr::FontKey fontKey = GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont(), aResources);
+  wr::FontKey fontKey = GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont(),
+                                                  aRenderRoot, aResources);
   wr::FontKey nullKey = { wr::IdNamespace { 0 }, 0};
   if (fontKey == nullKey) {
     return instanceKey;
   }
 
   instanceKey = GetNextFontInstanceKey();
 
   Maybe<wr::FontInstanceOptions> options;
   Maybe<wr::FontInstancePlatformOptions> platformOptions;
   std::vector<FontVariation> variations;
   aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions, &variations);
 
   aResources->AddFontInstance(instanceKey, fontKey, aScaledFont->GetSize(),
                               options.ptrOr(nullptr), platformOptions.ptrOr(nullptr),
                               Range<const FontVariation>(variations.data(), variations.size()));
   if (resources.isSome()) {
-    UpdateResources(resources.ref());
+    UpdateResources(resources.ref(), aRenderRoot);
   }
 
-  mFontInstanceKeys.Put(aScaledFont, instanceKey);
+  fontInstanceKeys.Put(aScaledFont, instanceKey);
 
   return instanceKey;
 
 }
 
 wr::FontKey
 WebRenderBridgeChild::GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaled,
+                                                wr::RenderRoot aRenderRoot,
                                                 wr::IpcResourceUpdateQueue* aResources)
 {
   MOZ_ASSERT(!mDestroyed);
 
+  auto& fontKeys = aRenderRoot == wr::RenderRoot::Content ? mContentRectFontKeys : mFontKeys;
   wr::FontKey fontKey = { wr::IdNamespace { 0 }, 0};
-  if (!mFontKeys.Get(aUnscaled, &fontKey)) {
+  if (!fontKeys.Get(aUnscaled, &fontKey)) {
     Maybe<wr::IpcResourceUpdateQueue> resources =
       aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this));
 
     FontFileDataSink sink = { &fontKey, this, resources.ptrOr(aResources) };
     // First try to retrieve a descriptor for the font, as this is much cheaper
     // to send over IPC than the full raw font data. If this is not possible, then
     // and only then fall back to getting the raw font file data. If that fails,
     // then the only thing left to do is signal failure by returning a null font key.
     if (!aUnscaled->GetWRFontDescriptor(WriteFontDescriptor, &sink) &&
         !aUnscaled->GetFontFileData(WriteFontFileData, &sink)) {
       return fontKey;
     }
 
     if (resources.isSome()) {
-      UpdateResources(resources.ref());
+      UpdateResources(resources.ref(), aRenderRoot);
     }
 
-    mFontKeys.Put(aUnscaled, fontKey);
+    fontKeys.Put(aUnscaled, fontKey);
   }
 
   return fontKey;
 }
 
 void
 WebRenderBridgeChild::RemoveExpiredFontKeys(wr::IpcResourceUpdateQueue& aResourceUpdates)
 {
+  auto& fontInstanceKeys = aResourceUpdates.GetRenderRoot() == wr::RenderRoot::Content ?
+    mContentRectFontInstanceKeys : mFontInstanceKeys;
+  auto& fontKeys = aResourceUpdates.GetRenderRoot() == wr::RenderRoot::Content ?
+    mContentRectFontKeys : mFontKeys;
+  auto& fontInstanceKeysDeleted = aResourceUpdates.GetRenderRoot() == wr::RenderRoot::Content ?
+    mContentRectFontInstanceKeysDeleted : mFontInstanceKeysDeleted;
+  auto& fontKeysDeleted = aResourceUpdates.GetRenderRoot() == wr::RenderRoot::Content ?
+    mContentRectFontKeysDeleted : mFontKeysDeleted;
+
   uint32_t counter = gfx::ScaledFont::DeletionCounter();
-  if (mFontInstanceKeysDeleted != counter) {
-    mFontInstanceKeysDeleted = counter;
-    for (auto iter = mFontInstanceKeys.Iter(); !iter.Done(); iter.Next()) {
+  if (fontInstanceKeysDeleted != counter) {
+    fontInstanceKeysDeleted = counter;
+    for (auto iter = fontInstanceKeys.Iter(); !iter.Done(); iter.Next()) {
       if (!iter.Key()) {
         aResourceUpdates.DeleteFontInstance(iter.Data());
         iter.Remove();
       }
     }
   }
   counter = gfx::UnscaledFont::DeletionCounter();
-  if (mFontKeysDeleted != counter) {
-    mFontKeysDeleted = counter;
-    for (auto iter = mFontKeys.Iter(); !iter.Done(); iter.Next()) {
+  if (fontKeysDeleted != counter) {
+    fontKeysDeleted = counter;
+    for (auto iter = fontKeys.Iter(); !iter.Done(); iter.Next()) {
       if (!iter.Key()) {
         aResourceUpdates.DeleteFont(iter.Data());
         iter.Remove();
       }
     }
   }
 }
 
@@ -477,36 +560,39 @@ WebRenderBridgeChild::DestroyInTransacti
 bool
 WebRenderBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle)
 {
   return AddOpDestroy(OpDestroy(aHandle));
 }
 
 void
 WebRenderBridgeChild::RemoveTextureFromCompositable(CompositableClient* aCompositable,
-                                                    TextureClient* aTexture)
+                                                    TextureClient* aTexture,
+                                                    wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aTexture->GetIPDLActor());
   MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() == GetIPCChannel());
   if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
     // We don't have an actor anymore, don't try to use it!
     return;
   }
 
   AddWebRenderParentCommand(
     CompositableOperation(
       aCompositable->GetIPCHandle(),
-      OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
+      OpRemoveTexture(nullptr, aTexture->GetIPDLActor())),
+    aRenderRoot);
 }
 
 void
 WebRenderBridgeChild::UseTextures(CompositableClient* aCompositable,
-                                  const nsTArray<TimedTextureClient>& aTextures)
+                                  const nsTArray<TimedTextureClient>& aTextures,
+                                  wr::RenderRoot aRenderRoot)
 {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
   AutoTArray<TimedTexture,4> textures;
@@ -519,17 +605,18 @@ WebRenderBridgeChild::UseTextures(Compos
 
     textures.AppendElement(TimedTexture(nullptr, t.mTextureClient->GetIPDLActor(),
                                         t.mTimeStamp, t.mPictureRect,
                                         t.mFrameID, t.mProducerID,
                                         readLocked));
     GetCompositorBridgeChild()->HoldUntilCompositableRefReleasedIfNecessary(t.mTextureClient);
   }
   AddWebRenderParentCommand(CompositableOperation(aCompositable->GetIPCHandle(),
-                                            OpUseTexture(textures)));
+                                                  OpUseTexture(textures)),
+                            aRenderRoot);
 }
 
 void
 WebRenderBridgeChild::UseComponentAlphaTextures(CompositableClient* aCompositable,
                                                 TextureClient* aClientOnBlack,
                                                 TextureClient* aClientOnWhite)
 {
 
@@ -562,16 +649,18 @@ WebRenderBridgeChild::RecvWrUpdated(cons
   }
   IdentifyTextureHost(textureFactoryIdentifier);
   // Update mIdNamespace to identify obsolete keys and messages by WebRenderBridgeParent.
   // Since usage of invalid keys could cause crash in webrender.
   mIdNamespace = aNewIdNamespace;
   // Just clear FontInstaceKeys/FontKeys, they are removed during WebRenderAPI destruction.
   mFontInstanceKeys.Clear();
   mFontKeys.Clear();
+  mContentRectFontInstanceKeys.Clear();
+  mContentRectFontKeys.Clear();
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeChild::RecvWrReleasedImages(nsTArray<wr::ExternalImageKeyPair>&& aPairs)
 {
   if (mManager) {
     mManager->WrReleasedImages(aPairs);
--- a/gfx/layers/wr/WebRenderBridgeChild.h
+++ b/gfx/layers/wr/WebRenderBridgeChild.h
@@ -59,24 +59,31 @@ typedef ThreadSafeWeakPtrHashKey<gfx::Sc
 class WebRenderBridgeChild final : public PWebRenderBridgeChild
                                  , public CompositableForwarder
 {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderBridgeChild, override)
 
 public:
   explicit WebRenderBridgeChild(const wr::PipelineId& aPipelineId);
 
-  void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd);
+  void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd,
+                                 wr::RenderRoot aRenderRoot);
 
-  void UpdateResources(wr::IpcResourceUpdateQueue& aResources);
+  void UpdateResources(wr::IpcResourceUpdateQueue& aResources,
+                       wr::RenderRoot aRenderRoot);
   void BeginTransaction();
-  void EndTransaction(const wr::LayoutSize& aContentSize,
-                      wr::BuiltDisplayList& dl,
-                      wr::IpcResourceUpdateQueue& aResources,
-                      const gfx::IntSize& aSize,
+  void EndTransaction(const wr::LayoutSize& aChromeContentSize,
+                      wr::BuiltDisplayList& aChromeDL,
+                      wr::IpcResourceUpdateQueue& aChromeResources,
+                      const gfx::IntRect& aChromeRect,
+                      bool aProcessContentData,
+                      const wr::LayoutSize& aContentContentSize,
+                      wr::BuiltDisplayList& aContentDL,
+                      wr::IpcResourceUpdateQueue& aContentResources,
+                      const gfx::IntRect& aContentRect,
                       TransactionId aTransactionId,
                       const WebRenderScrollData& aScrollData,
                       bool aContainsSVGroup,
                       const mozilla::TimeStamp& aRefreshStartTime,
                       const mozilla::TimeStamp& aTxnStartTime,
                       const nsCString& aTxtURL);
   void EndEmptyTransaction(const FocusTarget& aFocusTarget,
                            const ScrollUpdatesMap& aUpdates,
@@ -94,24 +101,27 @@ public:
 
   // KnowsCompositor
   TextureForwarder* GetTextureForwarder() override;
   LayersIPCActor* GetLayersIPCActor() override;
   void SyncWithCompositor() override;
   ActiveResourceTracker* GetActiveResourceTracker() override { return mActiveResourceTracker.get(); }
 
   void AddPipelineIdForAsyncCompositable(const wr::PipelineId& aPipelineId,
-                                         const CompositableHandle& aHandlee);
+                                         const CompositableHandle& aHandlee,
+                                         wr::RenderRoot aRenderRoot);
   void AddPipelineIdForCompositable(const wr::PipelineId& aPipelineId,
-                                    const CompositableHandle& aHandlee);
-  void RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId);
+                                    const CompositableHandle& aHandlee,
+                                    wr::RenderRoot aRenderRoot);
+  void RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId,
+                                       wr::RenderRoot aRenderRoot);
 
   /// Release TextureClient that is bounded to ImageKey.
   /// It is used for recycling TextureClient.
-  void ReleaseTextureOfImage(const wr::ImageKey& aKey);
+  void ReleaseTextureOfImage(const wr::ImageKey& aKey, wr::RenderRoot aRenderRoot);
 
   /**
    * Clean this up, finishing with SendShutDown() which will cause __delete__
    * to be sent from the parent side.
    */
   void Destroy(bool aIsSync);
   bool IPCOpen() const { return mIPCOpen && !mDestroyed; }
   bool IsDestroyed() const { return mDestroyed; }
@@ -141,18 +151,20 @@ public:
   void PushGlyphs(wr::DisplayListBuilder& aBuilder, Range<const wr::GlyphInstance> aGlyphs,
                   gfx::ScaledFont* aFont, const wr::ColorF& aColor,
                   const StackingContextHelper& aSc,
                   const wr::LayoutRect& aBounds, const wr::LayoutRect& aClip,
                   bool aBackfaceVisible,
                   const wr::GlyphOptions* aGlyphOptions = nullptr);
 
   wr::FontInstanceKey GetFontKeyForScaledFont(gfx::ScaledFont* aScaledFont,
+                                              wr::RenderRoot aRenderRoot,
                                               wr::IpcResourceUpdateQueue* aResources = nullptr);
   wr::FontKey GetFontKeyForUnscaledFont(gfx::UnscaledFont* aUnscaledFont,
+                                        wr::RenderRoot aRenderRoot,
                                         wr::IpcResourceUpdateQueue* aResources = nullptr);
   void RemoveExpiredFontKeys(wr::IpcResourceUpdateQueue& aResources);
 
   void BeginClearCachedResources();
   void EndClearCachedResources();
 
   void SetWebRenderLayerManager(WebRenderLayerManager* aManager);
 
@@ -189,19 +201,21 @@ private:
                            const SurfaceDescriptorTiles& aTiledDescriptor) override;
   void UpdateTextureRegion(CompositableClient* aCompositable,
                            const ThebesBufferData& aThebesBufferData,
                            const nsIntRegion& aUpdatedRegion) override;
   void ReleaseCompositable(const CompositableHandle& aHandle) override;
   bool DestroyInTransaction(PTextureChild* aTexture) override;
   bool DestroyInTransaction(const CompositableHandle& aHandle);
   void RemoveTextureFromCompositable(CompositableClient* aCompositable,
-                                     TextureClient* aTexture) override;
+                                     TextureClient* aTexture,
+                                     wr::RenderRoot aRenderRoot) override;
   void UseTextures(CompositableClient* aCompositable,
-                   const nsTArray<TimedTextureClient>& aTextures) override;
+                   const nsTArray<TimedTextureClient>& aTextures,
+                   wr::RenderRoot aRenderRoot) override;
   void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                  TextureClient* aClientOnBlack,
                                  TextureClient* aClientOnWhite) override;
   void UpdateFwdTransactionId() override;
   uint64_t GetFwdTransactionId() override;
   bool InForwarderThread() override;
 
   void ActorDestroy(ActorDestroyReason why) override;
@@ -221,33 +235,38 @@ private:
     MOZ_ASSERT(mIPCOpen == true);
     mIPCOpen = false;
     Release();
   }
 
   bool AddOpDestroy(const OpDestroy& aOp);
 
   nsTArray<WebRenderParentCommand> mParentCommands;
+  nsTArray<WebRenderParentCommand> mContentRectParentCommands;
   nsTArray<OpDestroy> mDestroyedActors;
   nsDataHashtable<nsUint64HashKey, CompositableClient*> mCompositables;
   bool mIsInTransaction;
   bool mIsInClearCachedResources;
   wr::IdNamespace mIdNamespace;
   uint32_t mResourceId;
   wr::PipelineId mPipelineId;
   WebRenderLayerManager* mManager;
 
   bool mIPCOpen;
   bool mDestroyed;
 
   uint32_t mFontKeysDeleted;
+  uint32_t mContentRectFontKeysDeleted;
   nsDataHashtable<UnscaledFontHashKey, wr::FontKey> mFontKeys;
+  nsDataHashtable<UnscaledFontHashKey, wr::FontKey> mContentRectFontKeys;
 
   uint32_t mFontInstanceKeysDeleted;
+  uint32_t mContentRectFontInstanceKeysDeleted;
   nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey> mFontInstanceKeys;
+  nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey> mContentRectFontInstanceKeys;
 
   UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
 
   RefCountedShmem mResourceShm;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -309,31 +309,35 @@ private:
   InfallibleTArray<OpDestroy>* mActorsToDestroy;
 };
 
 WebRenderBridgeParent::WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                                              const wr::PipelineId& aPipelineId,
                                              widget::CompositorWidget* aWidget,
                                              CompositorVsyncScheduler* aScheduler,
                                              RefPtr<wr::WebRenderAPI>&& aApi,
+                                             RefPtr<wr::WebRenderAPI>&& aContentRectApi,
                                              RefPtr<AsyncImagePipelineManager>&& aImageMgr,
                                              RefPtr<CompositorAnimationStorage>&& aAnimStorage,
                                              TimeDuration aVsyncRate)
   : mCompositorBridge(aCompositorBridge)
   , mPipelineId(aPipelineId)
   , mWidget(aWidget)
   , mApi(aApi)
+  , mContentRectApi(aContentRectApi)
   , mAsyncImageManager(aImageMgr)
   , mCompositorScheduler(aScheduler)
   , mAnimStorage(aAnimStorage)
   , mVsyncRate(aVsyncRate)
   , mChildLayersObserverEpoch{0}
   , mParentLayersObserverEpoch{0}
   , mWrEpoch{0}
   , mIdNamespace(aApi->GetNamespace())
+  , mContentRectMutex("WebRenderBridgeParent::mContentRectMutex")
+  , mRenderRoot(wr::kRenderRootUnknown)
   , mPaused(false)
   , mDestroyed(false)
   , mReceivedDisplayList(false)
   , mIsFirstPaint(true)
 {
   MOZ_ASSERT(mAsyncImageManager);
   MOZ_ASSERT(mAnimStorage);
   mAsyncImageManager->AddPipeline(mPipelineId, this);
@@ -345,16 +349,18 @@ WebRenderBridgeParent::WebRenderBridgePa
 
 WebRenderBridgeParent::WebRenderBridgeParent(const wr::PipelineId& aPipelineId)
   : mCompositorBridge(nullptr)
   , mPipelineId(aPipelineId)
   , mChildLayersObserverEpoch{0}
   , mParentLayersObserverEpoch{0}
   , mWrEpoch{0}
   , mIdNamespace{0}
+  , mContentRectMutex(nullptr)
+  , mRenderRoot(wr::kRenderRootUnknown)
   , mPaused(false)
   , mDestroyed(true)
   , mReceivedDisplayList(false)
   , mIsFirstPaint(false)
 {
 }
 
 /* static */ WebRenderBridgeParent*
@@ -363,26 +369,29 @@ WebRenderBridgeParent::CreateDestroyed(c
   return new WebRenderBridgeParent(aPipelineId);
 }
 
 WebRenderBridgeParent::~WebRenderBridgeParent()
 {
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvEnsureConnected(TextureFactoryIdentifier* aTextureFactoryIdentifier,
+WebRenderBridgeParent::RecvEnsureConnected(const wr::RenderRoot& aRenderRoot,
+                                           TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                            MaybeIdNamespace* aMaybeIdNamespace)
 {
   if (mDestroyed) {
     *aTextureFactoryIdentifier = TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     *aMaybeIdNamespace = Nothing();
     return IPC_OK();
   }
 
+  MOZ_ASSERT(mRenderRoot == wr::kRenderRootUnknown);
   MOZ_ASSERT(mIdNamespace.mHandle != 0);
+  mRenderRoot = aRenderRoot;
   *aTextureFactoryIdentifier = GetTextureFactoryIdentifier();
   *aMaybeIdNamespace = Some(mIdNamespace);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvShutdown()
@@ -749,17 +758,18 @@ WebRenderBridgeParent::ObserveSharedSurf
   for (const auto& pair : aPairs) {
     SharedSurfacesParent::Release(pair.id);
   }
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvUpdateResources(nsTArray<OpUpdateResource>&& aResourceUpdates,
                                            nsTArray<RefCountedShmem>&& aSmallShmems,
-                                           nsTArray<ipc::Shmem>&& aLargeShmems)
+                                           nsTArray<ipc::Shmem>&& aLargeShmems,
+                                           const wr::RenderRoot& aRenderRoot)
 {
   if (mDestroyed) {
     wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
     wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
     return IPC_OK();
   }
 
   wr::TransactionBuilder txn;
@@ -769,17 +779,21 @@ WebRenderBridgeParent::RecvUpdateResourc
     UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn);
   wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
   wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
 
   if (!success) {
     return IPC_FAIL(this, "Invalid WebRender resource data shmem or address.");
   }
 
-  mApi->SendTransaction(txn);
+  if (aRenderRoot == wr::RenderRoot::Content) {
+    ContentApi()->SendTransaction(txn);
+  } else {
+    DefaultApi()->SendTransaction(txn);
+  }
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds)
 {
   if (mDestroyed) {
@@ -906,29 +920,118 @@ WebRenderBridgeParent::SetAPZSampleTime(
     // use the timestamp for the next vsync when advancing animations.
     if (frameInterval != TimeDuration::Forever()) {
       animationTime += frameInterval;
     }
     apz->SetSampleTime(animationTime);
   }
 }
 
+bool
+WebRenderBridgeParent::SetDisplayList(wr::RenderRoot aRenderRoot,
+                                      const gfx::IntRect& aRect,
+                                      const nsTArray<WebRenderParentCommand>& aCommands,
+                                      const wr::LayoutSize& aContentSize,
+                                      ipc::ByteBuf&& aDL,
+                                      const wr::BuiltDisplayListDescriptor& aDLDesc,
+                                      const nsTArray<OpUpdateResource>& aResourceUpdates,
+                                      const nsTArray<RefCountedShmem>& aSmallShmems,
+                                      const nsTArray<ipc::Shmem>& aLargeShmems,
+                                      const TimeStamp& aTxnStartTime,
+                                      wr::TransactionBuilder& aTxn,
+                                      Maybe<wr::AutoTransactionSender>& aTxnSender,
+                                      wr::Epoch aWrEpoch,
+                                      bool aValidTransaction,
+                                      bool aObserveLayersUpdate)
+{
+  wr::WebRenderAPI* api = aRenderRoot == wr::RenderRoot::Content ? ContentApi() : DefaultApi();
+  aTxn.SetLowPriority(!IsRootWebRenderBridgeParent());
+  if (aValidTransaction) {
+    aTxnSender.emplace(api, &aTxn);
+  }
+
+  if (NS_WARN_IF(!ProcessWebRenderParentCommands(aCommands, aTxn, aRenderRoot))) {
+    return false;
+  }
+
+  if (NS_WARN_IF(!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, aTxn))) {
+    return false;
+  }
+
+  wr::Vec<uint8_t> dlData(std::move(aDL));
+
+  if (aValidTransaction) {
+    if (IsRootWebRenderBridgeParent()) {
+      if (aRenderRoot == wr::RenderRoot::Content) {
+        MutexAutoLock lock(mContentRectMutex);
+        mContentRect = aRect;
+      }
+      LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
+      LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromUnknownRect(aRect);
+      rect.SetWidth(std::max(0, std::min(widgetSize.width - rect.X(), rect.Width())));
+      rect.SetHeight(std::max(0, std::min(widgetSize.height - rect.Y(), rect.Height())));
+      aTxn.SetWindowParameters(widgetSize, rect);
+    }
+    gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
+    aTxn.SetDisplayList(clearColor, aWrEpoch, LayerSize(aRect.width, aRect.height),
+                        mPipelineId, aContentSize,
+                        aDLDesc, dlData);
+
+    if (aObserveLayersUpdate) {
+      aTxn.Notify(
+        wr::Checkpoint::SceneBuilt,
+        MakeUnique<ScheduleObserveLayersUpdate>(
+          mCompositorBridge,
+          GetLayersId(),
+          mChildLayersObserverEpoch,
+          true
+        )
+      );
+    }
+
+    aTxn.Notify(
+      wr::Checkpoint::SceneBuilt,
+      MakeUnique<SceneBuiltNotification>(
+        this,
+        aWrEpoch,
+        aTxnStartTime
+      )
+    );
+
+    api->SendTransaction(aTxn);
+
+    // We will schedule generating a frame after the scene
+    // build is done, so we don't need to do it here.
+  }
+
+  return true;
+}
+
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvSetDisplayList(const gfx::IntSize& aSize,
-                                          InfallibleTArray<WebRenderParentCommand>&& aCommands,
+WebRenderBridgeParent::RecvSetDisplayList(const gfx::IntRect& aDefaultRect,
+                                          InfallibleTArray<WebRenderParentCommand>&& aDefaultCommands,
+                                          const wr::LayoutSize& aDefaultContentSize,
+                                          ipc::ByteBuf&& defaultDL,
+                                          const wr::BuiltDisplayListDescriptor& defaultDLDesc,
+                                          nsTArray<OpUpdateResource>&& aDefaultResourceUpdates,
+                                          nsTArray<RefCountedShmem>&& aDefaultSmallShmems,
+                                          nsTArray<ipc::Shmem>&& aDefaultLargeShmems,
+                                          const bool& aProcessContentData,
+                                          const gfx::IntRect& aContentRect,
+                                          InfallibleTArray<WebRenderParentCommand>&& aContentCommands,
+                                          const wr::LayoutSize& aContentContentSize,
+                                          ipc::ByteBuf&& contentDL,
+                                          const wr::BuiltDisplayListDescriptor& contentDLDesc,
+                                          nsTArray<OpUpdateResource>&& aContentResourceUpdates,
+                                          nsTArray<RefCountedShmem>&& aContentSmallShmems,
+                                          nsTArray<ipc::Shmem>&& aContentLargeShmems,
                                           InfallibleTArray<OpDestroy>&& aToDestroy,
                                           const uint64_t& aFwdTransactionId,
                                           const TransactionId& aTransactionId,
-                                          const wr::LayoutSize& aContentSize,
-                                          ipc::ByteBuf&& dl,
-                                          const wr::BuiltDisplayListDescriptor& dlDesc,
                                           const WebRenderScrollData& aScrollData,
-                                          nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                          nsTArray<RefCountedShmem>&& aSmallShmems,
-                                          nsTArray<ipc::Shmem>&& aLargeShmems,
                                           const wr::IdNamespace& aIdNamespace,
                                           const bool& aContainsSVGGroup,
                                           const TimeStamp& aRefreshStartTime,
                                           const TimeStamp& aTxnStartTime,
                                           const nsCString& aTxnURL,
                                           const TimeStamp& aFwdTime)
 {
   if (mDestroyed) {
@@ -944,123 +1047,116 @@ WebRenderBridgeParent::RecvSetDisplayLis
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return from RecvDPEnd without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
   wr::Epoch wrEpoch = GetNextWrEpoch();
 
   mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
 
-  // If id namespaces do not match, it means the command is obsolete, probably
-  // because the tab just moved to a new window.
-  // In that case do not send the commands to webrender.
-  bool validTransaction = aIdNamespace == mIdNamespace;
-  wr::TransactionBuilder txn;
-  txn.SetLowPriority(!IsRootWebRenderBridgeParent());
-  Maybe<wr::AutoTransactionSender> sender;
-  if (validTransaction) {
-    sender.emplace(mApi, &txn);
-  }
-
-  if (!ProcessWebRenderParentCommands(aCommands, txn)) {
-    return IPC_FAIL(this, "Invalid parent command found");
-  }
-
-  if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
-    return IPC_FAIL(this, "Failed to deserialize resource updates");
-  }
-
   mReceivedDisplayList = true;
+  bool observeLayersUpdate = ShouldParentObserveEpoch();
 
   if (aScrollData.IsFirstPaint()) {
     mIsFirstPaint = true;
   }
 
   // aScrollData is moved into this function but that is not reflected by the
   // function signature due to the way the IPDL generator works. We remove the
   // const so that we can move this structure all the way to the desired
   // destination.
   // Also note that this needs to happen before the display list transaction is
   // sent to WebRender, so that the UpdateHitTestingTree call is guaranteed to
   // be in the updater queue at the time that the scene swap completes.
   UpdateAPZScrollData(wrEpoch, std::move(const_cast<WebRenderScrollData&>(aScrollData)));
 
-  wr::Vec<uint8_t> dlData(std::move(dl));
-
-  bool observeLayersUpdate = ShouldParentObserveEpoch();
+  bool validTransaction = aIdNamespace == mIdNamespace;
 
-  if (validTransaction) {
-    if (IsRootWebRenderBridgeParent()) {
-      LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
-      LayoutDeviceIntRect docRect(LayoutDeviceIntPoint(), widgetSize);
-      txn.SetWindowParameters(widgetSize, docRect);
-    }
-    gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
-    txn.SetDisplayList(clearColor, wrEpoch, LayerSize(aSize.width, aSize.height),
-                       mPipelineId, aContentSize,
-                       dlDesc, dlData);
+  wr::TransactionBuilder txn;
+  Maybe<wr::AutoTransactionSender> sender;
+  if (!SetDisplayList(wr::RenderRoot::Default,
+                      aDefaultRect,
+                      aDefaultCommands,
+                      aDefaultContentSize,
+                      std::move(defaultDL),
+                      defaultDLDesc,
+                      aDefaultResourceUpdates,
+                      aDefaultSmallShmems,
+                      aDefaultLargeShmems,
+                      aTxnStartTime,
+                      txn,
+                      sender,
+                      wrEpoch,
+                      validTransaction,
+                      observeLayersUpdate)) {
+    return IPC_FAIL(this, "Failed call to SetDisplayList");
+  }
 
-    if (observeLayersUpdate) {
-      txn.Notify(
-        wr::Checkpoint::SceneBuilt,
-        MakeUnique<ScheduleObserveLayersUpdate>(
-          mCompositorBridge,
-          GetLayersId(),
-          mChildLayersObserverEpoch,
-          true
-        )
-      );
+  wr::TransactionBuilder contentTxn;
+  Maybe<wr::AutoTransactionSender> contentSender;
+  if (aProcessContentData) {
+    if (!SetDisplayList(wr::RenderRoot::Content,
+                        aContentRect,
+                        aContentCommands,
+                        aContentContentSize,
+                        std::move(contentDL),
+                        contentDLDesc,
+                        aContentResourceUpdates,
+                        aContentSmallShmems,
+                        aContentLargeShmems,
+                        aTxnStartTime,
+                        contentTxn,
+                        contentSender,
+                        wrEpoch,
+                        validTransaction,
+                        observeLayersUpdate)) {
+      return IPC_FAIL(this, "Failed call to SetDisplayList");
     }
+  }
 
-    txn.Notify(
-      wr::Checkpoint::SceneBuilt,
-      MakeUnique<SceneBuiltNotification>(
-        this,
-        wrEpoch,
-        aTxnStartTime
-      )
-    );
-
-    mApi->SendTransaction(txn);
-
-    // We will schedule generating a frame after the scene
-    // build is done, so we don't need to do it here.
-  } else if (observeLayersUpdate) {
+  if (!validTransaction && observeLayersUpdate) {
     mCompositorBridge->ObserveLayersUpdate(GetLayersId(), mChildLayersObserverEpoch, true);
   }
 
   HoldPendingTransactionId(wrEpoch, aTransactionId, aContainsSVGGroup,
                            aRefreshStartTime, aTxnStartTime, aTxnURL, aFwdTime, mIsFirstPaint);
   mIsFirstPaint = false;
 
   if (!validTransaction) {
     // Pretend we composited since someone is wating for this event,
     // though DisplayList was not pushed to webrender.
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
       TimeStamp now = TimeStamp::Now();
       cbp->NotifyPipelineRendered(mPipelineId, wrEpoch, now, now, now);
     }
   }
 
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aDefaultSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aDefaultLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aContentSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aContentLargeShmems);
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                             const ScrollUpdatesMap& aUpdates,
                                             const uint32_t& aPaintSequenceNumber,
-                                            InfallibleTArray<WebRenderParentCommand>&& aCommands,
+                                            InfallibleTArray<WebRenderParentCommand>&& aDefaultCommands,
+                                            nsTArray<OpUpdateResource>&& aDefaultResourceUpdates,
+                                            nsTArray<RefCountedShmem>&& aDefaultSmallShmems,
+                                            nsTArray<ipc::Shmem>&& aDefaultLargeShmems,
+                                            InfallibleTArray<WebRenderParentCommand>&& aContentCommands,
+                                            nsTArray<OpUpdateResource>&& aContentResourceUpdates,
+                                            nsTArray<RefCountedShmem>&& aContentSmallShmems,
+                                            nsTArray<ipc::Shmem>&& aContentLargeShmems,
                                             InfallibleTArray<OpDestroy>&& aToDestroy,
                                             const uint64_t& aFwdTransactionId,
                                             const TransactionId& aTransactionId,
-                                            nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                            nsTArray<RefCountedShmem>&& aSmallShmems,
-                                            nsTArray<ipc::Shmem>&& aLargeShmems,
                                             const wr::IdNamespace& aIdNamespace,
                                             const TimeStamp& aRefreshStartTime,
                                             const TimeStamp& aTxnStartTime,
                                             const nsCString& aTxnURL,
                                             const TimeStamp& aFwdTime)
 {
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
@@ -1071,17 +1167,18 @@ WebRenderBridgeParent::RecvEmptyTransact
 
   AUTO_PROFILER_TRACING("Paint", "EmptyTransaction");
   UpdateFwdTransactionId(aFwdTransactionId);
 
   // This ensures that destroy operations are always processed. It is not safe
   // to early-return without doing so.
   AutoWebRenderBridgeParentAsyncMessageSender autoAsyncMessageSender(this, &aToDestroy);
 
-  bool scheduleComposite = false;
+  bool scheduleCompositeDefault = false;
+  bool scheduleCompositeContent = false;
 
   UpdateAPZFocusState(aFocusTarget);
   if (!aUpdates.empty()) {
     // aUpdates is moved into this function but that is not reflected by the
     // function signature due to the way the IPDL generator works. We remove the
     // const so that we can move this structure all the way to the desired
     // destination.
     UpdateAPZScrollOffsets(std::move(const_cast<ScrollUpdatesMap&>(aUpdates)), aPaintSequenceNumber);
@@ -1089,57 +1186,95 @@ WebRenderBridgeParent::RecvEmptyTransact
 
   wr::TransactionBuilder txn;
   txn.SetLowPriority(!IsRootWebRenderBridgeParent());
 
   // Update WrEpoch for UpdateResources() and ProcessWebRenderParentCommands().
   // WrEpoch is used to manage ExternalImages lifetimes in AsyncImagePipelineManager.
   Unused << GetNextWrEpoch();
 
-  if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
+  if (!UpdateResources(aDefaultResourceUpdates, aDefaultSmallShmems, aDefaultLargeShmems, txn)) {
+    return IPC_FAIL(this, "Failed to deserialize resource updates");
+  }
+
+  wr::TransactionBuilder contentTxn;
+  contentTxn.SetLowPriority(!IsRootWebRenderBridgeParent());
+
+  if (!UpdateResources(aContentResourceUpdates, aContentSmallShmems, aContentLargeShmems, contentTxn)) {
     return IPC_FAIL(this, "Failed to deserialize resource updates");
   }
 
-  if (!aCommands.IsEmpty()) {
+  if (!aDefaultCommands.IsEmpty() || !aContentCommands.IsEmpty()) {
     mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
-    if (!ProcessWebRenderParentCommands(aCommands, txn)) {
+  }
+
+  if (!aDefaultCommands.IsEmpty()) {
+    if (!ProcessWebRenderParentCommands(aDefaultCommands, txn, wr::RenderRoot::Default)) {
+      return IPC_FAIL(this, "Invalid parent command found");
+    }
+  }
+
+  if (!aContentCommands.IsEmpty()) {
+    if (!ProcessWebRenderParentCommands(aContentCommands, contentTxn, wr::RenderRoot::Content)) {
       return IPC_FAIL(this, "Invalid parent command found");
     }
   }
 
   if (ShouldParentObserveEpoch()) {
     txn.Notify(
       wr::Checkpoint::SceneBuilt,
       MakeUnique<ScheduleObserveLayersUpdate>(
         mCompositorBridge,
         GetLayersId(),
         mChildLayersObserverEpoch,
         true
       )
     );
+
+    contentTxn.Notify(
+      wr::Checkpoint::SceneBuilt,
+      MakeUnique<ScheduleObserveLayersUpdate>(
+        mCompositorBridge,
+        GetLayersId(),
+        mChildLayersObserverEpoch,
+        true
+      )
+    );
   }
 
-  if (txn.IsResourceUpdatesEmpty()) {
+  if (txn.IsResourceUpdatesEmpty() && contentTxn.IsResourceUpdatesEmpty()) {
     // If TransactionBuilder does not have resource updates nor display list,
     // ScheduleGenerateFrame is not triggered via SceneBuilder and there is no
     // need to update WrEpoch.
     // Then we want to rollback WrEpoch. See Bug 1490117.
     RollbackWrEpoch();
-  } else {
+  }
+
+  if (!txn.IsResourceUpdatesEmpty()) {
     // There are resource updates, then we update Epoch of transaction.
     txn.UpdateEpoch(mPipelineId, mWrEpoch);
-    scheduleComposite = true;
+    scheduleCompositeDefault = true;
+  }
+
+  if (!contentTxn.IsResourceUpdatesEmpty()) {
+    // There are resource updates, then we update Epoch of transaction.
+    contentTxn.UpdateEpoch(mPipelineId, mWrEpoch);
+    scheduleCompositeContent = true;
   }
 
   if (!txn.IsEmpty()) {
-    mApi->SendTransaction(txn);
+    DefaultApi()->SendTransaction(txn);
+  }
+
+  if (!contentTxn.IsEmpty()) {
+    ContentApi()->SendTransaction(contentTxn);
   }
 
   bool sendDidComposite = true;
-  if (scheduleComposite || !mPendingTransactionIds.empty()) {
+  if (scheduleCompositeDefault || scheduleCompositeContent || !mPendingTransactionIds.empty()) {
     // If we are going to kick off a new composite as a result of this
     // transaction, or if there are already composite-triggering pending
     // transactions inflight, then set sendDidComposite to false because we will
     // send the DidComposite message after the composite occurs.
     // If there are no pending transactions and we're not going to do a
     // composite, then we leave sendDidComposite as true so we just send
     // the DidComposite notification now.
     sendDidComposite = false;
@@ -1150,105 +1285,124 @@ WebRenderBridgeParent::RecvEmptyTransact
   HoldPendingTransactionId(mWrEpoch,
                            aTransactionId,
                            false,
                            aRefreshStartTime,
                            aTxnStartTime,
                            aTxnURL,
                            aFwdTime,
                            /* aIsFirstPaint */false,
-                           /* aUseForTelemetry */scheduleComposite);
+                           /* aUseForTelemetry */scheduleCompositeDefault || scheduleCompositeContent);
 
-  if (scheduleComposite) {
+  if (scheduleCompositeDefault && scheduleCompositeContent) {
     // This is actually not necessary, since ScheduleGenerateFrame() is triggered
     // via SceneBuilder thread. But if we remove it, it causes talos regression.
     // The SceneBuilder thread seems not trigger next vsync right away.
     // For now, we call ScheduleGenerateFrame() here.
-    ScheduleGenerateFrame();
+    ScheduleGenerateFrame(kRenderRootIdAll);
+  } else if (scheduleCompositeDefault) {
+    ScheduleGenerateFrame(DefaultApi()->GetDocumentId().mHandle);
+  } else if (scheduleCompositeContent) {
+    ScheduleGenerateFrame(ContentApi()->GetDocumentId().mHandle);
   } else if (sendDidComposite) {
     // The only thing in the pending transaction id queue should be the entry
     // we just added, and now we're going to pretend we rendered it
     MOZ_ASSERT(mPendingTransactionIds.size() == 1);
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
       TimeStamp now = TimeStamp::Now();
       cbp->NotifyPipelineRendered(mPipelineId, mWrEpoch, now, now, now);
     }
   }
 
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aDefaultSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aDefaultLargeShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aContentSmallShmems);
+  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aContentLargeShmems);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSetFocusTarget(const FocusTarget& aFocusTarget)
 {
   UpdateAPZFocusState(aFocusTarget);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
-WebRenderBridgeParent::RecvParentCommands(nsTArray<WebRenderParentCommand>&& aCommands)
+WebRenderBridgeParent::RecvParentCommands(nsTArray<WebRenderParentCommand>&& aCommands,
+                                          const wr::RenderRoot& aRenderRoot)
 {
   if (mDestroyed) {
     return IPC_OK();
   }
   wr::TransactionBuilder txn;
   txn.SetLowPriority(!IsRootWebRenderBridgeParent());
-  if (!ProcessWebRenderParentCommands(aCommands, txn)) {
+  if (!ProcessWebRenderParentCommands(aCommands, txn, aRenderRoot)) {
     return IPC_FAIL(this, "Invalid parent command found");
   }
-  mApi->SendTransaction(txn);
+  if (aRenderRoot == wr::RenderRoot::Content) {
+    ContentApi()->SendTransaction(txn);
+  } else {
+    DefaultApi()->SendTransaction(txn);
+  }
   return IPC_OK();
 }
 
 bool
 WebRenderBridgeParent::ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands,
-                                                      wr::TransactionBuilder& aTxn)
+                                                      wr::TransactionBuilder& aTxn,
+                                                      wr::RenderRoot aRenderRoot)
 {
   // Transaction for async image pipeline that uses ImageBridge always need to be non low priority.
   wr::TransactionBuilder txnForImageBridge;
-  wr::AutoTransactionSender sender(mApi, &txnForImageBridge);
+  wr::AutoTransactionSender sender(aRenderRoot == wr::RenderRoot::Content ? ContentApi() : DefaultApi(), &txnForImageBridge);
 
   for (InfallibleTArray<WebRenderParentCommand>::index_type i = 0; i < aCommands.Length(); ++i) {
     const WebRenderParentCommand& cmd = aCommands[i];
     switch (cmd.type()) {
       case WebRenderParentCommand::TOpAddPipelineIdForCompositable: {
         const OpAddPipelineIdForCompositable& op = cmd.get_OpAddPipelineIdForCompositable();
         AddPipelineIdForCompositable(op.pipelineId(),
                                      op.handle(),
                                      op.isAsync(),
                                      aTxn,
-                                     txnForImageBridge);
+                                     txnForImageBridge,
+                                     aRenderRoot);
         break;
       }
       case WebRenderParentCommand::TOpRemovePipelineIdForCompositable: {
         const OpRemovePipelineIdForCompositable& op = cmd.get_OpRemovePipelineIdForCompositable();
-        RemovePipelineIdForCompositable(op.pipelineId(), aTxn);
+        RemovePipelineIdForCompositable(op.pipelineId(), aTxn, aRenderRoot);
         break;
       }
       case WebRenderParentCommand::TOpReleaseTextureOfImage: {
         const OpReleaseTextureOfImage& op = cmd.get_OpReleaseTextureOfImage();
         ReleaseTextureOfImage(op.key());
         break;
       }
       case WebRenderParentCommand::TOpUpdateAsyncImagePipeline: {
         const OpUpdateAsyncImagePipeline& op = cmd.get_OpUpdateAsyncImagePipeline();
         mAsyncImageManager->UpdateAsyncImagePipeline(op.pipelineId(),
                                                      op.scBounds(),
                                                      op.scTransform(),
                                                      op.scaleToSize(),
                                                      op.filter(),
                                                      op.mixBlendMode());
-        mAsyncImageManager->ApplyAsyncImageForPipeline(op.pipelineId(), aTxn, txnForImageBridge);
+        mAsyncImageManager->ApplyAsyncImageForPipeline(op.pipelineId(),
+                                                       aTxn,
+                                                       txnForImageBridge,
+                                                       RenderRootForExternal(aRenderRoot));
         break;
       }
       case WebRenderParentCommand::TOpUpdatedAsyncImagePipeline: {
         const OpUpdatedAsyncImagePipeline& op = cmd.get_OpUpdatedAsyncImagePipeline();
-        mAsyncImageManager->ApplyAsyncImageForPipeline(op.pipelineId(), aTxn, txnForImageBridge);
+        mAsyncImageManager->ApplyAsyncImageForPipeline(op.pipelineId(),
+                                                       aTxn,
+                                                       txnForImageBridge,
+                                                       RenderRootForExternal(aRenderRoot));
         break;
       }
       case WebRenderParentCommand::TCompositableOperation: {
         if (!ReceiveCompositableUpdate(cmd.get_CompositableOperation())) {
           NS_ERROR("ReceiveCompositableUpdate failed");
         }
         break;
       }
@@ -1256,17 +1410,17 @@ WebRenderBridgeParent::ProcessWebRenderP
         const OpAddCompositorAnimations& op = cmd.get_OpAddCompositorAnimations();
         CompositorAnimations data(std::move(op.data()));
         // AnimationHelper::GetNextCompositorAnimationsId() encodes the child process PID
         // in the upper 32 bits of the id, verify that this is as expected.
         if ((data.id() >> 32) != (uint64_t)OtherPid()) {
           return false;
         }
         if (data.animations().Length()) {
-          mAnimStorage->SetAnimations(data.id(), data.animations());
+          mAnimStorage->SetAnimations(data.id(), data.animations(), RenderRootForExternal(aRenderRoot));
           mActiveAnimations.insert(data.id());
         }
         break;
       }
       default: {
         // other commands are handle on the child
         break;
       }
@@ -1293,17 +1447,17 @@ WebRenderBridgeParent::FlushSceneBuilds(
   // could spin a nested event loop, but that's more messy). Instead, we
   // simulate it ourselves by calling ScheduleGenerateFrame() directly.
   // Note also that the post-swap hook will run and do another
   // ScheduleGenerateFrame() after we unwind here, so we will end up with an
   // extra render/composite that is probably avoidable, but in practice we
   // shouldn't be calling this function all that much in production so this
   // is probably fine. If it becomes an issue we can add more state tracking
   // machinery to optimize it away.
-  ScheduleGenerateFrame();
+  ScheduleGenerateFrame(kRenderRootIdAll);
 }
 
 void
 WebRenderBridgeParent::FlushFrameGeneration()
 {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(IsRootWebRenderBridgeParent()); // This function is only useful on the root WRBP
 
@@ -1384,23 +1538,27 @@ WebRenderBridgeParent::RecvGetSnapshot(P
   return IPC_OK();
 }
 
 void
 WebRenderBridgeParent::AddPipelineIdForCompositable(const wr::PipelineId& aPipelineId,
                                                     const CompositableHandle& aHandle,
                                                     const bool& aAsync,
                                                     wr::TransactionBuilder& aTxn,
-                                                    wr::TransactionBuilder& aTxnForImageBridge)
+                                                    wr::TransactionBuilder& aTxnForImageBridge,
+                                                    const wr::RenderRoot& aRenderRoot)
 {
   if (mDestroyed) {
     return;
   }
 
-  MOZ_ASSERT(mAsyncCompositables.find(wr::AsUint64(aPipelineId)) == mAsyncCompositables.end());
+  auto& asyncCompositables = aRenderRoot == wr::RenderRoot::Content ?
+    mContentRectAsyncCompositables : mAsyncCompositables;
+
+  MOZ_ASSERT(asyncCompositables.find(wr::AsUint64(aPipelineId)) == asyncCompositables.end());
 
   RefPtr<CompositableHost> host;
   if (aAsync) {
     RefPtr<ImageBridgeParent> imageBridge = ImageBridgeParent::GetInstance(OtherPid());
     if (!imageBridge) {
        return;
     }
     host = imageBridge->FindCompositable(aHandle);
@@ -1418,47 +1576,51 @@ WebRenderBridgeParent::AddPipelineIdForC
   }
 
   if (!wrHost) {
     return;
   }
 
   wrHost->SetWrBridge(this);
   wrHost->EnableUseAsyncImagePipeline();
-  mAsyncCompositables.emplace(wr::AsUint64(aPipelineId), wrHost);
-  mAsyncImageManager->AddAsyncImagePipeline(aPipelineId, wrHost);
+  asyncCompositables.emplace(wr::AsUint64(aPipelineId), wrHost);
+  mAsyncImageManager->AddAsyncImagePipeline(aPipelineId, wrHost, RenderRootForExternal(aRenderRoot));
 
   // If this is being called from WebRenderBridgeParent::RecvSetDisplayList,
   // then aTxn might contain a display list that references pipelines that
   // we just added to the async image manager.
   // If we send the display list alone then WR will not yet have the content for
   // the pipelines and so it will emit errors; the SetEmptyDisplayList call
   // below ensure that we provide its content to WR as part of the same transaction.
   mAsyncImageManager->SetEmptyDisplayList(aPipelineId, aTxn, aTxnForImageBridge);
   return;
 }
 
 void
 WebRenderBridgeParent::RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId,
-                                                       wr::TransactionBuilder& aTxn)
+                                                       wr::TransactionBuilder& aTxn,
+                                                       wr::RenderRoot aRenderRoot)
 {
   if (mDestroyed) {
     return;
   }
 
-  auto it = mAsyncCompositables.find(wr::AsUint64(aPipelineId));
-  if (it == mAsyncCompositables.end()) {
+  auto& asyncCompositables = aRenderRoot == wr::RenderRoot::Content ?
+    mContentRectAsyncCompositables : mAsyncCompositables;
+
+  auto it = asyncCompositables.find(wr::AsUint64(aPipelineId));
+  if (it == asyncCompositables.end()) {
     return;
   }
   RefPtr<WebRenderImageHost>& wrHost = it->second;
 
   wrHost->ClearWrBridge();
   mAsyncImageManager->RemoveAsyncImagePipeline(aPipelineId, aTxn);
   aTxn.RemovePipeline(aPipelineId);
-  mAsyncCompositables.erase(wr::AsUint64(aPipelineId));
+  asyncCompositables.erase(wr::AsUint64(aPipelineId));
   return;
 }
 
 void
 WebRenderBridgeParent::DeleteImage(const ImageKey& aKey,
                                    wr::TransactionBuilder& aUpdates)
 {
   if (mDestroyed) {
@@ -1521,31 +1683,49 @@ WebRenderBridgeParent::RecvClearCachedRe
     MakeUnique<ScheduleObserveLayersUpdate>(
       mCompositorBridge,
       GetLayersId(),
       mChildLayersObserverEpoch,
       false
     )
   );
 
-  mApi->SendTransaction(txn);
+  DefaultApi()->SendTransaction(txn);
+
+  if (mRenderRoot == wr::RenderRoot::Default && gfxPrefs::WebRenderSplitRenderRoots()) {
+    wr::TransactionBuilder contentTxn;
+    contentTxn.SetLowPriority(true);
+    contentTxn.ClearDisplayList(GetNextWrEpoch(), mPipelineId);
+    contentTxn.Notify(
+      wr::Checkpoint::SceneBuilt,
+      MakeUnique<ScheduleObserveLayersUpdate>(
+        mCompositorBridge,
+        GetLayersId(),
+        mChildLayersObserverEpoch,
+        false
+      )
+    );
+
+    ContentApi()->SendTransaction(contentTxn);
+  }
   // Schedule generate frame to clean up Pipeline
-  ScheduleGenerateFrame();
+  ScheduleGenerateFrame(kRenderRootIdAll);
   // Remove animations.
   for (const auto& id : mActiveAnimations) {
     mAnimStorage->ClearById(id);
   }
   mActiveAnimations.clear();
   std::queue<CompositorAnimationIdsForEpoch>().swap(mCompositorAnimationsToDelete); // clear queue
   return IPC_OK();
 }
 
 wr::Epoch
 WebRenderBridgeParent::UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                                        wr::WebRenderAPI* aApi,
+                                       wr::WebRenderAPI* aContentRectApi,
                                        AsyncImagePipelineManager* aImageMgr,
                                        CompositorAnimationStorage* aAnimStorage,
                                        const TextureFactoryIdentifier& aTextureFactoryIdentifier)
 {
   MOZ_ASSERT(!IsRootWebRenderBridgeParent());
   MOZ_ASSERT(aScheduler);
   MOZ_ASSERT(aApi);
   MOZ_ASSERT(aImageMgr);
@@ -1568,51 +1748,60 @@ WebRenderBridgeParent::UpdateWebRender(C
   // before new layer/webrender keys allocation. In future, we could address the problem.
   Unused << SendWrUpdated(mIdNamespace, aTextureFactoryIdentifier);
   CompositorBridgeParentBase* cBridge = mCompositorBridge;
   // XXX Stop to clear resources if webreder supports resources sharing between different webrender instances.
   ClearResources();
   mCompositorBridge = cBridge;
   mCompositorScheduler = aScheduler;
   mApi = aApi;
+  mContentRectApi = aContentRectApi;
   mAsyncImageManager = aImageMgr;
   mAnimStorage = aAnimStorage;
 
   // Register pipeline to updated AsyncImageManager.
   mAsyncImageManager->AddPipeline(mPipelineId, this);
 
   return GetNextWrEpoch(); // Update webrender epoch
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvScheduleComposite()
 {
-  ScheduleGenerateFrame();
+  ScheduleGenerateFrame(kRenderRootIdAll);
   return IPC_OK();
 }
 
 void
 WebRenderBridgeParent::ScheduleForcedGenerateFrame()
 {
   if (mDestroyed) {
     return;
   }
 
   wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
   fastTxn.InvalidateRenderedFrame();
-  mApi->SendTransaction(fastTxn);
+  DefaultApi()->SendTransaction(fastTxn);
+  if (mRenderRoot == wr::RenderRoot::Default && gfxPrefs::WebRenderSplitRenderRoots()) {
+    wr::TransactionBuilder contentFastTxn(/* aUseSceneBuilderThread */ false);
+    contentFastTxn.InvalidateRenderedFrame();
+    ContentApi()->SendTransaction(contentFastTxn);
+  }
 
-  ScheduleGenerateFrame();
+  ScheduleGenerateFrame(kRenderRootIdAll);
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvCapture()
 {
   if (!mDestroyed) {
     mApi->Capture();
+    if (mContentRectApi) {
+      mContentRectApi->Capture();
+    }
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult
 WebRenderBridgeParent::RecvSyncWithCompositor()
 {
   FlushSceneBuilds();
@@ -1749,30 +1938,37 @@ WebRenderBridgeParent::AdvanceAnimations
   // started animations.
   mPreviousFrameTimeStamp = isAnimating ? lastComposeTime : TimeStamp();
 
   return isAnimating;
 }
 
 bool
 WebRenderBridgeParent::SampleAnimations(nsTArray<wr::WrOpacityProperty>& aOpacityArray,
-                                        nsTArray<wr::WrTransformProperty>& aTransformArray)
+                                        nsTArray<wr::WrTransformProperty>& aTransformArray,
+                                        nsTArray<wr::WrOpacityProperty>& aContentOpacityArray,
+                                        nsTArray<wr::WrTransformProperty>& aContentTransformArray)
 {
   const bool isAnimating = AdvanceAnimations();
 
   // return the animated data if has
   if (mAnimStorage->AnimatedValueCount()) {
     for(auto iter = mAnimStorage->ConstAnimatedValueTableIter();
         !iter.Done(); iter.Next()) {
       AnimatedValue * value = iter.UserData();
+      wr::RenderRoot renderRoot = mAnimStorage->AnimationRenderRoot(iter.Key());
+      auto& transformArray = renderRoot == wr::RenderRoot::Content ?
+        aContentTransformArray : aTransformArray;
+      auto& opacityArray = renderRoot == wr::RenderRoot::Content ?
+        aContentOpacityArray : aOpacityArray;
       if (value->mType == AnimatedValue::TRANSFORM) {
-        aTransformArray.AppendElement(
+        transformArray.AppendElement(
           wr::ToWrTransformProperty(iter.Key(), value->mTransform.mTransformInDevSpace));
       } else if (value->mType == AnimatedValue::OPACITY) {
-        aOpacityArray.AppendElement(
+        opacityArray.AppendElement(
           wr::ToWrOpacityProperty(iter.Key(), value->mOpacity));
       }
     }
   }
 
   return isAnimating;
 }
 
@@ -1830,63 +2026,90 @@ WebRenderBridgeParent::MaybeGenerateFram
   TimeStamp start = TimeStamp::Now();
   mAsyncImageManager->SetCompositionTime(start);
 
   // Ensure GenerateFrame is handled on the render backend thread rather
   // than going through the scene builder thread. That way we continue generating
   // frames with the old scene even during slow scene builds.
   bool useSceneBuilderThread = false;
   wr::TransactionBuilder fastTxn(useSceneBuilderThread);
+  wr::TransactionBuilder contentFastTxn(useSceneBuilderThread);
 
   // Handle transaction that is related to DisplayList.
   wr::TransactionBuilder sceneBuilderTxn;
   wr::AutoTransactionSender sender(mApi, &sceneBuilderTxn);
+  wr::TransactionBuilder contentSceneBuilderTxn;
+  Maybe<wr::AutoTransactionSender> contentSender;
+  if (mContentRectApi) {
+    contentSender.emplace(mContentRectApi, &contentSceneBuilderTxn);
+  }
 
   // Adding and updating wr::ImageKeys of ImageHosts that uses ImageBridge are
   // done without using transaction of scene builder thread. With it, updating of
   // video frame becomes faster.
-  mAsyncImageManager->ApplyAsyncImagesOfImageBridge(sceneBuilderTxn, fastTxn);
+  mAsyncImageManager->ApplyAsyncImagesOfImageBridge(sceneBuilderTxn,
+                                                    fastTxn,
+                                                    contentSceneBuilderTxn,
+                                                    contentFastTxn);
 
   if (!mAsyncImageManager->GetCompositeUntilTime().IsNull()) {
     // Trigger another CompositeToTarget() call because there might be another
     // frame that we want to generate after this one.
     // It will check if we actually want to generate the frame or not.
     mCompositorScheduler->ScheduleComposition();
   }
 
-  if (!mAsyncImageManager->GetAndResetWillGenerateFrame() &&
-      fastTxn.IsEmpty() &&
-      !aForceGenerateFrame) {
+  bool generateChromeFrame = mAsyncImageManager->GetAndResetWillGenerateFrame(wr::RenderRoot::Default) ||
+    !fastTxn.IsEmpty() ||
+    aForceGenerateFrame;
+  bool generateContentFrame = mContentRectApi &&
+    (mAsyncImageManager->GetAndResetWillGenerateFrame(wr::RenderRoot::Content) ||
+     !contentFastTxn.IsEmpty() ||
+     aForceGenerateFrame);
+
+  if (!generateChromeFrame && !generateContentFrame) {
     // Could skip generating frame now.
     mPreviousFrameTimeStamp = TimeStamp();
     return;
   }
 
   nsTArray<wr::WrOpacityProperty> opacityArray;
   nsTArray<wr::WrTransformProperty> transformArray;
+  nsTArray<wr::WrOpacityProperty> contentOpacityArray;
+  nsTArray<wr::WrTransformProperty> contentTransformArray;
 
-  if (SampleAnimations(opacityArray, transformArray)) {
-    ScheduleGenerateFrame();
+  if (SampleAnimations(opacityArray, transformArray, contentOpacityArray, contentTransformArray)) {
+    // TODO we should have a better way of assessing whether we need a content or a chrome
+    // frame generation.
+    ScheduleGenerateFrame(kRenderRootIdAll);
   }
   // We do this even if the arrays are empty, because it will clear out any
   // previous properties store on the WR side, which is desirable.
   fastTxn.UpdateDynamicProperties(opacityArray, transformArray);
+  contentFastTxn.UpdateDynamicProperties(contentOpacityArray, contentTransformArray);
 
   SetAPZSampleTime();
 
-  wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId(), start);
+  wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId(),
+                                                start,
+                                                (generateContentFrame && generateChromeFrame) ? 2 : 1);
 
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   auto startTime = TimeStamp::Now();
   mApi->SetFrameStartTime(startTime);
 #endif
 
-  fastTxn.GenerateFrame();
-
-  mApi->SendTransaction(fastTxn);
+  if (generateContentFrame) {
+    contentFastTxn.GenerateFrame();
+    mContentRectApi->SendTransaction(contentFastTxn);
+  }
+  if (generateChromeFrame) {
+    fastTxn.GenerateFrame();
+    mApi->SendTransaction(fastTxn);
+  }
 }
 
 void
 WebRenderBridgeParent::HoldPendingTransactionId(const wr::Epoch& aWrEpoch,
                                                 TransactionId aTransactionId,
                                                 bool aContainsSVGGroup,
                                                 const TimeStamp& aRefreshStartTime,
                                                 const TimeStamp& aTxnStartTime,
@@ -1902,16 +2125,27 @@ WebRenderBridgeParent::HoldPendingTransa
                                                         aRefreshStartTime,
                                                         aTxnStartTime,
                                                         aTxnURL,
                                                         aFwdTime,
                                                         aIsFirstPaint,
                                                         aUseForTelemetry));
 }
 
+already_AddRefed<wr::WebRenderAPI>
+WebRenderBridgeParent::GetWebRenderAPIAtPoint(const gfx::IntPoint& aPoint)
+{
+  MutexAutoLock lock(mContentRectMutex);
+  if (mContentRect.Contains(aPoint)) {
+    return do_AddRef(mContentRectApi);
+  } else {
+    return do_AddRef(mApi);
+  }
+}
+
 TransactionId
 WebRenderBridgeParent::LastPendingTransactionId()
 {
   TransactionId id{0};
   if (!mPendingTransactionIds.empty()) {
     id = mPendingTransactionIds.back().mId;
   }
   return id;
@@ -2028,20 +2262,28 @@ WebRenderBridgeParent::FlushTransactionI
 
 LayersId
 WebRenderBridgeParent::GetLayersId() const
 {
   return wr::AsLayersId(mPipelineId);
 }
 
 void
-WebRenderBridgeParent::ScheduleGenerateFrame()
+WebRenderBridgeParent::ScheduleGenerateFrame(uint64_t aRenderRootId)
 {
   if (mCompositorScheduler) {
-    mAsyncImageManager->SetWillGenerateFrame();
+    if (aRenderRootId == kRenderRootIdAll ||
+        aRenderRootId == mApi->GetDocumentId().mHandle) {
+      mAsyncImageManager->SetWillGenerateFrame(wr::RenderRoot::Default);
+    }
+    if (mContentRectApi &&
+        (aRenderRootId == kRenderRootIdAll ||
+         aRenderRootId == mContentRectApi->GetDocumentId().mHandle)) {
+      mAsyncImageManager->SetWillGenerateFrame(wr::RenderRoot::Content);
+    }
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 void
 WebRenderBridgeParent::FlushRendering(bool aWaitForPresent)
 {
   if (mDestroyed) {
@@ -2061,94 +2303,119 @@ void
 WebRenderBridgeParent::Pause()
 {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 #ifdef MOZ_WIDGET_ANDROID
   if (!IsRootWebRenderBridgeParent() || mDestroyed) {
     return;
   }
   mApi->Pause();
+  if (mContentRectApi) {
+    mContentRectApi->Pause();
+  }
 #endif
   mPaused = true;
 }
 
 bool
 WebRenderBridgeParent::Resume()
 {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 #ifdef MOZ_WIDGET_ANDROID
   if (!IsRootWebRenderBridgeParent() || mDestroyed) {
     return false;
   }
 
   if (!mApi->Resume()) {
     return false;
   }
+  if (mContentRectApi && !mContentRectApi->Resume()) {
+    return false;
+  }
 #endif
   mPaused = false;
   return true;
 }
 
 void
 WebRenderBridgeParent::ClearResources()
 {
   if (!mApi) {
     return;
   }
 
   wr::Epoch wrEpoch = GetNextWrEpoch();
+  mReceivedDisplayList = false;
+  // Schedule generate frame to clean up Pipeline
+  ScheduleGenerateFrame(kRenderRootIdAll);
 
-  wr::TransactionBuilder txn;
-  txn.SetLowPriority(true);
-  txn.ClearDisplayList(wrEpoch, mPipelineId);
-  mReceivedDisplayList = false;
-
-  // Schedule generate frame to clean up Pipeline
-  ScheduleGenerateFrame();
   // WrFontKeys and WrImageKeys are deleted during WebRenderAPI destruction.
   for (const auto& entry : mTextureHosts) {
     WebRenderTextureHost* wrTexture = entry.second->AsWebRenderTextureHost();
     MOZ_ASSERT(wrTexture);
     if (wrTexture) {
       mAsyncImageManager->HoldExternalImage(mPipelineId, wrEpoch, wrTexture);
     }
   }
   mTextureHosts.clear();
+
+  for (const auto& entry : mSharedSurfaceIds) {
+    mAsyncImageManager->HoldExternalImage(mPipelineId, mWrEpoch, entry.second);
+  }
+  mSharedSurfaceIds.clear();
+
+  mAsyncImageManager->RemovePipeline(mPipelineId, wrEpoch);
+
+  wr::TransactionBuilder txn;
+  txn.SetLowPriority(true);
+  txn.ClearDisplayList(wrEpoch, mPipelineId);
+
   for (const auto& entry : mAsyncCompositables) {
     wr::PipelineId pipelineId = wr::AsPipelineId(entry.first);
     RefPtr<WebRenderImageHost> host = entry.second;
     host->ClearWrBridge();
     mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId, txn);
     txn.RemovePipeline(pipelineId);
   }
   mAsyncCompositables.clear();
-  for (const auto& entry : mSharedSurfaceIds) {
-    mAsyncImageManager->HoldExternalImage(mPipelineId, mWrEpoch, entry.second);
-  }
-  mSharedSurfaceIds.clear();
+  txn.RemovePipeline(mPipelineId);
+  DefaultApi()->SendTransaction(txn);
+
+  if (mRenderRoot == wr::RenderRoot::Default && gfxPrefs::WebRenderSplitRenderRoots()) {
+    wr::TransactionBuilder contentTxn;
+    contentTxn.SetLowPriority(true);
+    contentTxn.ClearDisplayList(wrEpoch, mPipelineId);
 
-  mAsyncImageManager->RemovePipeline(mPipelineId, wrEpoch);
-  txn.RemovePipeline(mPipelineId);
-
-  mApi->SendTransaction(txn);
+    for (const auto& entry : mContentRectAsyncCompositables) {
+      wr::PipelineId pipelineId = wr::AsPipelineId(entry.first);
+      RefPtr<WebRenderImageHost> host = entry.second;
+      host->ClearWrBridge();
+      mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId, contentTxn);
+      contentTxn.RemovePipeline(pipelineId);
+    }
+    mContentRectAsyncCompositables.clear();
+    contentTxn.RemovePipeline(mPipelineId);
+    ContentApi()->SendTransaction(contentTxn);
+  }
 
   for (const auto& id : mActiveAnimations) {
     mAnimStorage->ClearById(id);
   }
   mActiveAnimations.clear();
   std::queue<CompositorAnimationIdsForEpoch>().swap(mCompositorAnimationsToDelete); // clear queue
 
   if (IsRootWebRenderBridgeParent()) {
     mCompositorScheduler->Destroy();
   }
 
   mAnimStorage = nullptr;
   mCompositorScheduler = nullptr;
   mAsyncImageManager = nullptr;
   mApi = nullptr;
+  mContentRectApi = nullptr;
   mCompositorBridge = nullptr;
 }
 
 bool
 WebRenderBridgeParent::ShouldParentObserveEpoch()
 {
   if (mParentLayersObserverEpoch == mChildLayersObserverEpoch) {
     return false;
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -33,16 +33,18 @@ class CompositorWidget;
 }
 
 namespace wr {
 class WebRenderAPI;
 }
 
 namespace layers {
 
+const uint64_t kRenderRootIdAll = 0;
+
 class Compositor;
 class CompositorAnimationStorage;
 class CompositorBridgeParentBase;
 class CompositorVsyncScheduler;
 class AsyncImagePipelineManager;
 class WebRenderImageHost;
 
 class WebRenderBridgeParent final : public PWebRenderBridgeParent
@@ -51,76 +53,95 @@ class WebRenderBridgeParent final : publ
                                   , public layers::FrameRecorder
 {
 public:
   WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                         const wr::PipelineId& aPipelineId,
                         widget::CompositorWidget* aWidget,
                         CompositorVsyncScheduler* aScheduler,
                         RefPtr<wr::WebRenderAPI>&& aApi,
+                        RefPtr<wr::WebRenderAPI>&& aContentRectApi,
                         RefPtr<AsyncImagePipelineManager>&& aImageMgr,
                         RefPtr<CompositorAnimationStorage>&& aAnimStorage,
                         TimeDuration aVsyncRate);
 
   static WebRenderBridgeParent* CreateDestroyed(const wr::PipelineId& aPipelineId);
 
   wr::PipelineId PipelineId() { return mPipelineId; }
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPIAtPoint(const gfx::IntPoint& aPoint);
   already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() { return do_AddRef(mApi); }
+  already_AddRefed<wr::WebRenderAPI> GetContentRectWebRenderAPI() { return do_AddRef(mContentRectApi); }
   AsyncImagePipelineManager* AsyncImageManager() { return mAsyncImageManager; }
   CompositorVsyncScheduler* CompositorScheduler() { return mCompositorScheduler.get(); }
   CompositorBridgeParentBase* GetCompositorBridge() { return mCompositorBridge; }
 
-  mozilla::ipc::IPCResult RecvEnsureConnected(TextureFactoryIdentifier* aTextureFactoryIdentifier,
+  mozilla::ipc::IPCResult RecvEnsureConnected(const wr::RenderRoot& aRenderRoot,
+                                              TextureFactoryIdentifier* aTextureFactoryIdentifier,
                                               MaybeIdNamespace* aMaybeIdNamespace) override;
 
   mozilla::ipc::IPCResult RecvNewCompositable(const CompositableHandle& aHandle,
                                               const TextureInfo& aInfo) override;
   mozilla::ipc::IPCResult RecvReleaseCompositable(const CompositableHandle& aHandle) override;
 
   mozilla::ipc::IPCResult RecvShutdown() override;
   mozilla::ipc::IPCResult RecvShutdownSync() override;
   mozilla::ipc::IPCResult RecvDeleteCompositorAnimations(InfallibleTArray<uint64_t>&& aIds) override;
   mozilla::ipc::IPCResult RecvUpdateResources(nsTArray<OpUpdateResource>&& aUpdates,
                                               nsTArray<RefCountedShmem>&& aSmallShmems,
-                                              nsTArray<ipc::Shmem>&& aLargeShmems) override;
-  mozilla::ipc::IPCResult RecvSetDisplayList(const gfx::IntSize& aSize,
-                                             InfallibleTArray<WebRenderParentCommand>&& aCommands,
+                                              nsTArray<ipc::Shmem>&& aLargeShmems,
+                                              const wr::RenderRoot& aRenderRoot) override;
+  mozilla::ipc::IPCResult RecvSetDisplayList(const gfx::IntRect& aChromeRect,
+                                             InfallibleTArray<WebRenderParentCommand>&& aChromeCommands,
+                                             const wr::LayoutSize& aChromeContentSize,
+                                             ipc::ByteBuf&& chromeDL,
+                                             const wr::BuiltDisplayListDescriptor& chromeDLDesc,
+                                             nsTArray<OpUpdateResource>&& aChromeResourceUpdates,
+                                             nsTArray<RefCountedShmem>&& aChromeSmallShmems,
+                                             nsTArray<ipc::Shmem>&& aChromeLargeShmems,
+                                             const bool& aProcessContentData,
+                                             const gfx::IntRect& aContentRect,
+                                             InfallibleTArray<WebRenderParentCommand>&& aContentCommands,
+                                             const wr::LayoutSize& aContentContentSize,
+                                             ipc::ByteBuf&& contentDL,
+                                             const wr::BuiltDisplayListDescriptor& contentDLDesc,
+                                             nsTArray<OpUpdateResource>&& aContentResourceUpdates,
+                                             nsTArray<RefCountedShmem>&& aContentSmallShmems,
+                                             nsTArray<ipc::Shmem>&& aContentLargeShmems,
                                              InfallibleTArray<OpDestroy>&& aToDestroy,
                                              const uint64_t& aFwdTransactionId,
                                              const TransactionId& aTransactionId,
-                                             const wr::LayoutSize& aContentSize,
-                                             ipc::ByteBuf&& dl,
-                                             const wr::BuiltDisplayListDescriptor& dlDesc,
                                              const WebRenderScrollData& aScrollData,
-                                             nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                             nsTArray<RefCountedShmem>&& aSmallShmems,
-                                             nsTArray<ipc::Shmem>&& aLargeShmems,
                                              const wr::IdNamespace& aIdNamespace,
                                              const bool& aContainsSVGGroup,
                                              const TimeStamp& aRefreshStartTime,
                                              const TimeStamp& aTxnStartTime,
                                              const nsCString& aTxnURL,
                                              const TimeStamp& aFwdTime) override;
   mozilla::ipc::IPCResult RecvEmptyTransaction(const FocusTarget& aFocusTarget,
                                                const ScrollUpdatesMap& aUpdates,
                                                const uint32_t& aPaintSequenceNumber,
-                                               InfallibleTArray<WebRenderParentCommand>&& aCommands,
+                                               InfallibleTArray<WebRenderParentCommand>&& aChromeCommands,
+                                               nsTArray<OpUpdateResource>&& aChromeResourceUpdates,
+                                               nsTArray<RefCountedShmem>&& aChromeSmallShmems,
+                                               nsTArray<ipc::Shmem>&& aChromeLargeShmems,
+                                               InfallibleTArray<WebRenderParentCommand>&& aContentCommands,
+                                               nsTArray<OpUpdateResource>&& aContentResourceUpdates,
+                                               nsTArray<RefCountedShmem>&& aContentSmallShmems,
+                                               nsTArray<ipc::Shmem>&& aContentLargeShmems,
                                                InfallibleTArray<OpDestroy>&& aToDestroy,
                                                const uint64_t& aFwdTransactionId,
                                                const TransactionId& aTransactionId,
-                                               nsTArray<OpUpdateResource>&& aResourceUpdates,
-                                               nsTArray<RefCountedShmem>&& aSmallShmems,
-                                               nsTArray<ipc::Shmem>&& aLargeShmems,
                                                const wr::IdNamespace& aIdNamespace,
                                                const TimeStamp& aRefreshStartTime,
                                                const TimeStamp& aTxnStartTime,
                                                const nsCString& aTxnURL,
                                                const TimeStamp& aFwdTime) override;
   mozilla::ipc::IPCResult RecvSetFocusTarget(const FocusTarget& aFocusTarget) override;
-  mozilla::ipc::IPCResult RecvParentCommands(nsTArray<WebRenderParentCommand>&& commands) override;
+  mozilla::ipc::IPCResult RecvParentCommands(nsTArray<WebRenderParentCommand>&& commands,
+                                             const wr::RenderRoot& aRenderRoot) override;
   mozilla::ipc::IPCResult RecvGetSnapshot(PTextureParent* aTexture) override;
 
   mozilla::ipc::IPCResult RecvSetLayersObserverEpoch(const LayersObserverEpoch& aChildEpoch) override;
 
   mozilla::ipc::IPCResult RecvClearCachedResources() override;
   mozilla::ipc::IPCResult RecvScheduleComposite() override;
   mozilla::ipc::IPCResult RecvCapture() override;
   mozilla::ipc::IPCResult RecvSyncWithCompositor() override;
@@ -198,28 +219,29 @@ public:
    * WebRenderBridgeParent uses composite timing to check if there is an update
    * to AsyncImagePipelines. If there is no update, WebRenderBridgeParent skips
    * to generate frame. If we need to generate new frame at next composite timing,
    * call this method.
    *
    * Call CompositorVsyncScheduler::ScheduleComposition() directly, if we just
    * want to trigger AsyncImagePipelines update checks.
    */
-  void ScheduleGenerateFrame();
+  void ScheduleGenerateFrame(uint64_t aRenderRootId);
 
   /**
    * Schedule forced frame rendering at next composite timing.
    *
    * WebRender could skip frame rendering if there is no update.
    * This function is used to force rendering even when there is not update.
    */
   void ScheduleForcedGenerateFrame();
 
   wr::Epoch UpdateWebRender(CompositorVsyncScheduler* aScheduler,
                             wr::WebRenderAPI* aApi,
+                            wr::WebRenderAPI* aContentRectApi,
                             AsyncImagePipelineManager* aImageMgr,
                             CompositorAnimationStorage* aAnimStorage,
                             const TextureFactoryIdentifier& aTextureFactoryIdentifier);
 
   void RemoveEpochDataPriorTo(const wr::Epoch& aRenderedEpoch);
 
   /**
    * This sets the is-first-paint flag to true for the next received
@@ -234,16 +256,60 @@ public:
   bool IsRootWebRenderBridgeParent() const;
   LayersId GetLayersId() const;
 private:
   class ScheduleSharedSurfaceRelease;
 
   explicit WebRenderBridgeParent(const wr::PipelineId& aPipelineId);
   virtual ~WebRenderBridgeParent();
 
+  wr::WebRenderAPI* DefaultApi()
+  {
+    MOZ_ASSERT(mRenderRoot != wr::kRenderRootUnknown);
+    return mRenderRoot == wr::RenderRoot::Content ? mContentRectApi : mApi;
+  }
+
+  wr::WebRenderAPI* ContentApi()
+  {
+    MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default);
+    MOZ_ASSERT(gfxPrefs::WebRenderSplitRenderRoots());
+    return mContentRectApi;
+  }
+
+  // Within WebRenderBridgeParent, we can use wr::RenderRoot::Default to
+  // refer to DefaultApi(), which can be the content API if this
+  // WebRenderBridgeParent is for a content WebRenderBridgeChild. However,
+  // different WebRenderBridgeParents use the same AsyncImagePipelineManager,
+  // for example, which doesn't have this distinction, so we need to
+  // convert out our RenderRoot.
+  wr::RenderRoot RenderRootForExternal(wr::RenderRoot aRenderRoot)
+  {
+    if (aRenderRoot == wr::RenderRoot::Default) {
+      return mRenderRoot;
+    } else {
+      return aRenderRoot;
+    }
+  }
+
+  bool SetDisplayList(wr::RenderRoot aRenderRoot,
+                      const gfx::IntRect& aRect,
+                      const nsTArray<WebRenderParentCommand>& aCommands,
+                      const wr::LayoutSize& aContentSize,
+                      ipc::ByteBuf&& aDL,
+                      const wr::BuiltDisplayListDescriptor& aDLDesc,
+                      const nsTArray<OpUpdateResource>& aResourceUpdates,
+                      const nsTArray<RefCountedShmem>& aSmallShmems,
+                      const nsTArray<ipc::Shmem>& aLargeShmems,
+                      const TimeStamp& aTxnStartTime,
+                      wr::TransactionBuilder& aTxn,
+                      Maybe<wr::AutoTransactionSender>& aTxnSender,
+                      wr::Epoch aWrEpoch,
+                      bool aValidTransaction,
+                      bool aObserveLayersUpdate);
+
   void UpdateAPZFocusState(const FocusTarget& aFocus);
   void UpdateAPZScrollData(const wr::Epoch& aEpoch, WebRenderScrollData&& aData);
   void UpdateAPZScrollOffsets(ScrollUpdatesMap&& aUpdates,
                               uint32_t aPaintSequenceNumber);
 
   bool UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
                        const nsTArray<RefCountedShmem>& aSmallShmems,
                        const nsTArray<ipc::Shmem>& aLargeShmems,
@@ -261,36 +327,41 @@ private:
                                    TextureHost* aTexture,
                                    bool aIsUpdate,
                                    wr::TransactionBuilder& aResources);
 
   void AddPipelineIdForCompositable(const wr::PipelineId& aPipelineIds,
                                     const CompositableHandle& aHandle,
                                     const bool& aAsync,
                                     wr::TransactionBuilder& aTxn,
-                                    wr::TransactionBuilder& aTxnForImageBridge);
+                                    wr::TransactionBuilder& aTxnForImageBridge,
+                                    const wr::RenderRoot& aRenderRoot);
   void RemovePipelineIdForCompositable(const wr::PipelineId& aPipelineId,
-                                       wr::TransactionBuilder& aTxn);
+                                       wr::TransactionBuilder& aTxn,
+                                       wr::RenderRoot aRenderRoot);
 
   void DeleteImage(const wr::ImageKey& aKey,
                    wr::TransactionBuilder& aUpdates);
   void ReleaseTextureOfImage(const wr::ImageKey& aKey);
 
   bool ProcessWebRenderParentCommands(const InfallibleTArray<WebRenderParentCommand>& aCommands,
-                                      wr::TransactionBuilder& aTxn);
+                                      wr::TransactionBuilder& aTxn,
+                                      wr::RenderRoot aRenderRoot);
 
   void ClearResources();
   bool ShouldParentObserveEpoch();
   mozilla::ipc::IPCResult HandleShutdown();
 
   // Returns true if there is any animation (including animations in delay
   // phase).
   bool AdvanceAnimations();
   bool SampleAnimations(nsTArray<wr::WrOpacityProperty>& aOpacityArray,
-                        nsTArray<wr::WrTransformProperty>& aTransformArray);
+                        nsTArray<wr::WrTransformProperty>& aTransformArray,
+                        nsTArray<wr::WrOpacityProperty>& aContentOpacityArray,
+                        nsTArray<wr::WrTransformProperty>& aContentTransformArray);
 
   CompositorBridgeParent* GetRootCompositorBridgeParent() const;
 
   RefPtr<WebRenderBridgeParent> GetRootWebRenderBridgeParent() const;
 
   // Tell APZ what the subsequent sampling's timestamp should be.
   void SetAPZSampleTime();
 
@@ -352,23 +423,25 @@ private:
     wr::Epoch mEpoch;
     InfallibleTArray<uint64_t> mIds;
   };
 
   CompositorBridgeParentBase* MOZ_NON_OWNING_REF mCompositorBridge;
   wr::PipelineId mPipelineId;
   RefPtr<widget::CompositorWidget> mWidget;
   RefPtr<wr::WebRenderAPI> mApi;
+  RefPtr<wr::WebRenderAPI> mContentRectApi;
   RefPtr<AsyncImagePipelineManager> mAsyncImageManager;
   RefPtr<CompositorVsyncScheduler> mCompositorScheduler;
   RefPtr<CompositorAnimationStorage> mAnimStorage;
   // mActiveAnimations is used to avoid leaking animations when WebRenderBridgeParent is
   // destroyed abnormally and Tab move between different windows.
   std::unordered_set<uint64_t> mActiveAnimations;
   std::unordered_map<uint64_t, RefPtr<WebRenderImageHost>> mAsyncCompositables;
+  std::unordered_map<uint64_t, RefPtr<WebRenderImageHost>> mContentRectAsyncCompositables;
   std::unordered_map<uint64_t, CompositableTextureHostRef> mTextureHosts;
   std::unordered_map<uint64_t, wr::ExternalImageId> mSharedSurfaceIds;
 
   TimeDuration mVsyncRate;
   TimeStamp mPreviousFrameTimeStamp;
   // These fields keep track of the latest layer observer epoch values in the child and the
   // parent. mChildLayersObserverEpoch is the latest epoch value received from the child.
   // mParentLayersObserverEpoch is the latest epoch value that we have told TabParent about
@@ -376,16 +449,21 @@ private:
   LayersObserverEpoch mChildLayersObserverEpoch;
   LayersObserverEpoch mParentLayersObserverEpoch;
 
   std::deque<PendingTransactionId> mPendingTransactionIds;
   std::queue<CompositorAnimationIdsForEpoch> mCompositorAnimationsToDelete;
   wr::Epoch mWrEpoch;
   wr::IdNamespace mIdNamespace;
 
+  // Kind of clunky, but I can't sort out a more elegant way of getting this to work.
+  Mutex mContentRectMutex;
+  IntRect mContentRect;
+
+  wr::RenderRoot mRenderRoot;
   bool mPaused;
   bool mDestroyed;
   bool mReceivedDisplayList;
   bool mIsFirstPaint;
 };
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderCanvasRenderer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasRenderer.cpp
@@ -34,17 +34,18 @@ WebRenderCanvasRendererAsync::~WebRender
 }
 
 void
 WebRenderCanvasRendererAsync::Initialize(const CanvasInitializeData& aData)
 {
   WebRenderCanvasRenderer::Initialize(aData);
 
   if (mPipelineId.isSome()) {
-    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(),
+                                                          mRenderRoot);
     mPipelineId.reset();
   }
 }
 
 bool
 WebRenderCanvasRendererAsync::CreateCompositable()
 {
   if (!mCanvasClient) {
@@ -66,47 +67,51 @@ WebRenderCanvasRendererAsync::CreateComp
 
     mCanvasClient->Connect();
   }
 
   if (!mPipelineId) {
     // Alloc async image pipeline id.
     mPipelineId = Some(mManager->WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
     mManager->WrBridge()->AddPipelineIdForCompositable(mPipelineId.ref(),
-                                                       mCanvasClient->GetIPCHandle());
+                                                       mCanvasClient->GetIPCHandle(),
+                                                       mRenderRoot);
   }
 
   return true;
 }
 
 void
 WebRenderCanvasRendererAsync::ClearCachedResources()
 {
   if (mPipelineId.isSome()) {
-    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(),
+                                                          mRenderRoot);
     mPipelineId.reset();
   }
 }
 
 void
 WebRenderCanvasRendererAsync::Destroy()
 {
   if (mPipelineId.isSome()) {
-    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    mManager->WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(),
+                                                          mRenderRoot);
     mPipelineId.reset();
   }
 }
 
 void
 WebRenderCanvasRendererAsync::UpdateCompositableClientForEmptyTransaction()
 {
-  UpdateCompositableClient();
+  UpdateCompositableClient(mRenderRoot);
   if (mPipelineId.isSome()) {
     // Notify an update of async image pipeline during empty transaction.
     // During non empty transaction, WebRenderBridgeParent receives OpUpdateAsyncImagePipeline message,
     // but during empty transaction, the message is not sent to WebRenderBridgeParent.
     // Then OpUpdatedAsyncImagePipeline is used to notify the update.
-    mManager->WrBridge()->AddWebRenderParentCommand(OpUpdatedAsyncImagePipeline(mPipelineId.ref()));
+    mManager->WrBridge()->AddWebRenderParentCommand(OpUpdatedAsyncImagePipeline(mPipelineId.ref()),
+                                                    mRenderRoot);
   }
 }
 
 } // namespace layers
 } // namespace mozilla
--- a/gfx/layers/wr/WebRenderCanvasRenderer.h
+++ b/gfx/layers/wr/WebRenderCanvasRenderer.h
@@ -27,32 +27,35 @@ public:
 
 protected:
   WebRenderLayerManager* mManager;
 };
 
 class WebRenderCanvasRendererAsync : public WebRenderCanvasRenderer
 {
 public:
-  explicit WebRenderCanvasRendererAsync(WebRenderLayerManager* aManager)
+  explicit WebRenderCanvasRendererAsync(WebRenderLayerManager* aManager,
+                                        wr::RenderRoot aRenderRoot)
     : WebRenderCanvasRenderer(aManager)
+    , mRenderRoot(aRenderRoot)
   { }
   virtual ~WebRenderCanvasRendererAsync();
 
   WebRenderCanvasRendererAsync* AsWebRenderCanvasRendererAsync() override { return this; }
 
   void Initialize(const CanvasInitializeData& aData) override;
   bool CreateCompositable() override;
 
   void ClearCachedResources() override;
   void Destroy() override;
 
   void UpdateCompositableClientForEmptyTransaction();
 
   Maybe<wr::PipelineId> GetPipelineId() { return mPipelineId; }
 protected:
   Maybe<wr::PipelineId> mPipelineId;
+  wr::RenderRoot mRenderRoot;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 #endif
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -336,17 +336,17 @@ struct DIGroup
   int32_t mAppUnitsPerDevPixel;
   gfx::Size mScale;
   ScrollableLayerGuid::ViewID mScrollId;
   LayerPoint mResidualOffset;
   LayerIntRect mLayerBounds;
   // The current bounds of the blob image, relative to
   // the top-left of the mLayerBounds.
   IntRect mImageBounds;
-  Maybe<wr::BlobImageKey> mKey;
+  Maybe<mozilla::Pair<wr::RenderRoot, wr::BlobImageKey>> mKey;
   std::vector<RefPtr<SourceSurface>> mExternalSurfaces;
   std::vector<RefPtr<ScaledFont>> mFonts;
 
   DIGroup()
     : mAppUnitsPerDevPixel(0)
     , mScrollId(ScrollableLayerGuid::NULL_SCROLL_ID)
   {
   }
@@ -373,17 +373,17 @@ struct DIGroup
       delete data;
     }
   }
 
   void ClearImageKey(WebRenderLayerManager* aManager, bool aForce = false)
   {
     if (mKey) {
       MOZ_RELEASE_ASSERT(aForce || mInvalidRect.IsEmpty());
-      aManager->AddBlobImageKeyForDiscard(mKey.value());
+      aManager->AddBlobImageKeyForDiscard(mKey.value().second(), mKey.value().first());
       mKey = Nothing();
     }
     mFonts.clear();
   }
 
   static IntRect
   ToDeviceSpace(nsRect aBounds, Matrix& aMatrix, int32_t aAppUnitsPerDevPixel, LayerIntPoint aOffset)
   {
@@ -630,33 +630,35 @@ struct DIGroup
     IntSize dtSize = layerBounds.Size().ToUnknownSize();
     LayoutDeviceRect bounds = (LayerRect(layerBounds) - mResidualOffset) / scale;
 
     if (mInvalidRect.IsEmpty()) {
       GP("Not repainting group because it's empty\n");
       GP("End EndGroup\n");
       if (mKey) {
         aResources.SetBlobImageVisibleArea(
-          mKey.value(),
+          mKey.value().second(),
           ViewAs<ImagePixel>(mPaintRect, PixelCastJustification::LayerIsImage));
         PushImage(aBuilder, bounds);
       }
       return;
     }
 
     gfx::SurfaceFormat format = gfx::SurfaceFormat::B8G8R8A8;
     std::vector<RefPtr<ScaledFont>> fonts;
     RefPtr<WebRenderDrawEventRecorder> recorder =
       MakeAndAddRef<WebRenderDrawEventRecorder>(
         [&](MemStream& aStream, std::vector<RefPtr<ScaledFont>>& aScaledFonts) {
           size_t count = aScaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
           for (auto& scaled : aScaledFonts) {
             BlobFont font = {
-              aWrManager->WrBridge()->GetFontKeyForScaledFont(scaled, &aResources),
+              aWrManager->WrBridge()->GetFontKeyForScaledFont(scaled,
+                                                              aBuilder.GetRenderRoot(),
+                                                              &aResources),
               scaled
             };
             aStream.write((const char*)&font, sizeof(font));
           }
           fonts = std::move(aScaledFonts);
         });
 
     RefPtr<gfx::DrawTarget> dummyDt =
@@ -690,31 +692,31 @@ struct DIGroup
         return;
       wr::BlobImageKey key = wr::BlobImageKey { aWrManager->WrBridge()->GetNextImageKey() };
       GP("No previous key making new one %d\n", key._0.mHandle);
       wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
       MOZ_RELEASE_ASSERT(bytes.length() > sizeof(size_t));
       if (!aResources.AddBlobImage(key, descriptor, bytes)) {
         return;
       }
-      mKey = Some(key);
+      mKey = Some(MakePair(aBuilder.GetRenderRoot(), key));
     } else {
       wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
       auto bottomRight = mInvalidRect.BottomRight();
       GP("check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y, dtSize.width, dtSize.height);
       MOZ_RELEASE_ASSERT(bottomRight.x <= dtSize.width && bottomRight.y <= dtSize.height);
       GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y, mInvalidRect.width, mInvalidRect.height);
-      if (!aResources.UpdateBlobImage(mKey.value(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) {
+      if (!aResources.UpdateBlobImage(mKey.value().second(), descriptor, bytes, ViewAs<ImagePixel>(mInvalidRect))) {
         return;
       }
     }
     mFonts = std::move(fonts);
     mInvalidRect.SetEmpty();
     aResources.SetBlobImageVisibleArea(
-      mKey.value(),
+      mKey.value().second(),
       ViewAs<ImagePixel>(mPaintRect, PixelCastJustification::LayerIsImage));
     PushImage(aBuilder, bounds);
     GP("End EndGroup\n\n");
   }
 
   void PushImage(wr::DisplayListBuilder& aBuilder, const LayoutDeviceRect& bounds)
   {
     wr::LayoutRect dest = wr::ToLayoutRect(bounds);
@@ -728,17 +730,17 @@ struct DIGroup
 
     // XXX - clipping the item against the paint rect breaks some content.
     // cf. Bug 1455422.
     //wr::LayoutRect clip = wr::ToLayoutRect(bounds.Intersect(mPaintRect));
 
     aBuilder.SetHitTestInfo(mScrollId, hitInfo);
     aBuilder.PushImage(dest, dest, !backfaceHidden,
                        wr::ToImageRendering(sampleFilter),
-                       wr::AsImageKey(mKey.value()));
+                       wr::AsImageKey(mKey.value().second()));
     aBuilder.ClearHitTestInfo();
   }
 
   void PaintItemRange(Grouper* aGrouper,
                       nsDisplayItem* aStartItem,
                       nsDisplayItem* aEndItem,
                       gfxContext* aContext,
                       WebRenderDrawEventRecorder* aRecorder) {
@@ -966,17 +968,19 @@ Grouper::PaintContainerItem(DIGroup* aGr
       aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext, aRecorder);
       break;
   }
 }
 
 class WebRenderGroupData : public WebRenderUserData
 {
 public:
-  explicit WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  explicit WebRenderGroupData(WebRenderLayerManager* aWRManager,
+                              nsDisplayItem* aItem,
+                              wr::RenderRoot aRenderRoot);
   virtual ~WebRenderGroupData();
 
   virtual WebRenderGroupData* AsGroupData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eGroup; }
   static UserDataType Type() { return UserDataType::eGroup; }
 
   DIGroup mSubGroup;
   DIGroup mFollowingGroup;
@@ -1058,17 +1062,18 @@ Grouper::ConstructGroups(nsDisplayListBu
       // 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");
 
       RefPtr<WebRenderGroupData> groupData =
-        aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(item);
+        aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(item,
+                                                                              aBuilder.GetRenderRoot());
 
       // Initialize groupData->mFollowingGroup
       // TODO: compute the group bounds post-grouping, so that they can be
       // tighter for just the sublist that made it into this group.
       // We want to ensure the tight bounds are still clipped by area
       // that we're building the display list for.
       if (!groupData->mFollowingGroup.mGroupBounds.IsEqualEdges(currentGroup->mGroupBounds) ||
           groupData->mFollowingGroup.mScale != currentGroup->mScale ||
@@ -1215,23 +1220,25 @@ WebRenderCommandBuilder::DoGroupingForDi
                                                   const StackingContextHelper& aSc,
                                                   wr::DisplayListBuilder& aBuilder,
                                                   wr::IpcResourceUpdateQueue& aResources)
 {
   if (!aList->GetBottom()) {
     return;
   }
 
-  mClipManager.BeginList(aSc);
-  Grouper g(mClipManager);
+  mCurrentClipManager->BeginList(aSc);
+  Grouper g(*mCurrentClipManager);
   int32_t appUnitsPerDevPixel = aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel();
   GP("DoGroupingForDisplayList\n");
 
   g.mDisplayListBuilder = aDisplayListBuilder;
-  RefPtr<WebRenderGroupData> groupData = CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
+  RefPtr<WebRenderGroupData> groupData =
+    CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem,
+                                                         aBuilder.GetRenderRoot());
   bool snapped;
   nsRect groupBounds = aWrappingItem->GetBounds(aDisplayListBuilder, &snapped);
   DIGroup& group = groupData->mSubGroup;
   auto p = group.mGroupBounds;
   auto q = groupBounds;
   gfx::Size scale = aSc.GetInheritedScale();
   auto trans = ViewAs<LayerPixel>(aSc.GetSnappingSurfaceTransform().GetTranslation());
   auto snappedTrans = LayerIntPoint::Floor(trans);
@@ -1294,17 +1301,17 @@ WebRenderCommandBuilder::DoGroupingForDi
   // mLayerBounds.TopLeft() in the blob image we want to stop doing this
   // adjustment.
   group.mPaintRect = group.mPaintRect - group.mLayerBounds.TopLeft();
   g.mTransform = Matrix::Scaling(scale.width, scale.height)
                                 .PostTranslate(residualOffset.x, residualOffset.y);
   group.mScale = scale;
   group.mScrollId = scrollId;
   g.ConstructGroups(aDisplayListBuilder, this, aBuilder, aResources, &group, aList, aSc);
-  mClipManager.EndList(aSc);
+  mCurrentClipManager->EndList(aSc);
 }
 
 void
 WebRenderCommandBuilder::Destroy()
 {
   mLastCanvasDatas.Clear();
   ClearCachedResources();
 }
@@ -1329,38 +1336,49 @@ WebRenderCommandBuilder::NeedsEmptyTrans
 }
 
 void
 WebRenderCommandBuilder::BuildWebRenderCommands(wr::DisplayListBuilder& aBuilder,
                                                 wr::IpcResourceUpdateQueue& aResourceUpdates,
                                                 nsDisplayList* aDisplayList,
                                                 nsDisplayListBuilder* aDisplayListBuilder,
                                                 WebRenderScrollData& aScrollData,
-                                                wr::LayoutSize& aContentSize,
                                                 const nsTArray<wr::WrFilterOp>& aFilters)
 {
   StackingContextHelper sc;
+  StackingContextHelper sc2;
+
   aScrollData = WebRenderScrollData(mManager);
   MOZ_ASSERT(mLayerScrollData.empty());
   mLastCanvasDatas.Clear();
   mLastAsr = nullptr;
   mBuilderDumpIndex = 0;
   mContainsSVGGroup = false;
   MOZ_ASSERT(mDumpIndent == 0);
-  mClipManager.BeginBuild(mManager, aBuilder);
+  if (aBuilder.GetRenderRoot() == wr::RenderRoot::Content) {
+    mContentRectClipManager.BeginBuild(mManager, aBuilder);
+  } else if (aBuilder.HasSubBuilder()) {
+    mClipManager.BeginBuild(mManager, aBuilder);
+    mContentRectClipManager.BeginBuild(mManager, aBuilder.SubBuilder());
+  } else {
+    mClipManager.BeginBuild(mManager, aBuilder);
+  }
 
   {
     if (!mZoomProp && gfxPrefs::APZAllowZooming() && XRE_IsContentProcess()) {
       mZoomProp.emplace();
       mZoomProp->effect_type = wr::WrAnimationType::Transform;
       mZoomProp->id = AnimationHelper::GetNextCompositorAnimationsId();
     }
 
     StackingContextHelper pageRootSc(sc, nullptr, aBuilder, aFilters,
         LayoutDeviceRect(), nullptr, mZoomProp.ptrOr(nullptr));
+    if (aBuilder.HasSubBuilder()) {
+      pageRootSc.CreateSubContextHelper(sc2, aFilters);
+    }
     if (ShouldDumpDisplayList(aDisplayListBuilder)) {
       mBuilderDumpIndex = aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
     }
     CreateWebRenderCommandsFromDisplayList(aDisplayList, nullptr, aDisplayListBuilder,
                                            pageRootSc, aBuilder, aResourceUpdates);
   }
 
   // Make a "root" layer data that has everything else as descendants
@@ -1378,17 +1396,24 @@ 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();
-  mClipManager.EndBuild();
+  if (aBuilder.GetRenderRoot() == wr::RenderRoot::Content) {
+    mContentRectClipManager.EndBuild();
+  } else if (aBuilder.HasSubBuilder()) {
+    mClipManager.EndBuild();
+    mContentRectClipManager.EndBuild();
+  } else {
+    mClipManager.EndBuild();
+  }
 
   // Remove the user data those are not displayed on the screen and
   // also reset the data to unused for next transaction.
   RemoveUnusedAndResetWebRenderUserData();
 }
 
 bool
 WebRenderCommandBuilder::ShouldDumpDisplayList(nsDisplayListBuilder* aBuilder)
@@ -1401,32 +1426,39 @@ WebRenderCommandBuilder::ShouldDumpDispl
 void
 WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList(nsDisplayList* aDisplayList,
                                                                 nsDisplayItem* aWrappingItem,
                                                                 nsDisplayListBuilder* aDisplayListBuilder,
                                                                 const StackingContextHelper& aSc,
                                                                 wr::DisplayListBuilder& aBuilder,
                                                                 wr::IpcResourceUpdateQueue& aResources)
 {
+  ClipManager* previousClipManager = mCurrentClipManager;
+  if (aBuilder.GetRenderRoot() == wr::RenderRoot::Content) {
+    mCurrentClipManager = &mContentRectClipManager;
+  } else {
+    mCurrentClipManager = &mClipManager;
+  }
+
   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;
   }
 
   bool dumpEnabled = ShouldDumpDisplayList(aDisplayListBuilder);
   if (dumpEnabled) {
     // If we're inside a nested display list, print the WR DL items from the
     // wrapper item before we start processing the nested items.
     mBuilderDumpIndex = aBuilder.Dump(mDumpIndent + 1, Some(mBuilderDumpIndex), Nothing());
   }
 
   mDumpIndent++;
-  mClipManager.BeginList(aSc);
+  mCurrentClipManager->BeginList(aSc);
 
   bool apzEnabled = mManager->AsyncPanZoomEnabled();
 
   FlattenedDisplayItemIterator iter(aDisplayListBuilder, aDisplayList);
   while (nsDisplayItem* i = iter.GetNext()) {
     nsDisplayItem* item = i;
     DisplayItemType itemType = item->GetType();
 
@@ -1485,17 +1517,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);
       }
     }
 
-    mClipManager.BeginItem(item, aSc);
+    mCurrentClipManager->BeginItem(item, aSc);
 
     { // scope restoreDoGrouping
       AutoRestore<bool> restoreDoGrouping(mDoGrouping);
       if (itemType == DisplayItemType::TYPE_SVG_WRAPPER) {
         // Inside an <svg>, all display items that are not LAYER_ACTIVE wrapper
         // display items (like animated transforms / opacity) share the same
         // animated geometry root, so we can combine subsequent items of that
         // type into the same image.
@@ -1579,43 +1611,46 @@ WebRenderCommandBuilder::CreateWebRender
           mLayerScrollData.back().Initialize(mManager->GetScrollData(), item,
               descendants, stopAtAsr, aSc.GetDeferredTransformMatrix());
         }
       }
     }
   }
 
   mDumpIndent--;
-  mClipManager.EndList(aSc);
+  mCurrentClipManager->EndList(aSc);
+  mCurrentClipManager = previousClipManager;
 }
 
 void
 WebRenderCommandBuilder::PushOverrideForASR(const ActiveScrolledRoot* aASR,
                                             const wr::WrClipId& aClipId)
 {
-  mClipManager.PushOverrideForASR(aASR, aClipId);
+  mCurrentClipManager->PushOverrideForASR(aASR, aClipId);
 }
 
 void
 WebRenderCommandBuilder::PopOverrideForASR(const ActiveScrolledRoot* aASR)
 {
-  mClipManager.PopOverrideForASR(aASR);
+  mCurrentClipManager->PopOverrideForASR(aASR);
 }
 
 Maybe<wr::ImageKey>
 WebRenderCommandBuilder::CreateImageKey(nsDisplayItem* aItem,
                                             ImageContainer* aContainer,
                                             mozilla::wr::DisplayListBuilder& aBuilder,
                                             mozilla::wr::IpcResourceUpdateQueue& aResources,
                                             mozilla::wr::ImageRendering aRendering,
                                             const StackingContextHelper& aSc,
                                             gfx::IntSize& aSize,
                                             const Maybe<LayoutDeviceRect>& aAsyncImageBounds)
 {
-  RefPtr<WebRenderImageData> imageData = CreateOrRecycleWebRenderUserData<WebRenderImageData>(aItem);
+  RefPtr<WebRenderImageData> imageData =
+    CreateOrRecycleWebRenderUserData<WebRenderImageData>(aItem,
+                                                         aBuilder.GetRenderRoot());
   MOZ_ASSERT(imageData);
 
   if (aContainer->IsAsync()) {
     MOZ_ASSERT(aAsyncImageBounds);
 
     LayoutDeviceRect rect = aAsyncImageBounds.value();
     LayoutDeviceRect scBounds(LayoutDevicePoint(0, 0), rect.Size());
     gfx::MaybeIntSize scaleToSize;
@@ -1865,17 +1900,19 @@ WebRenderCommandBuilder::GenerateFallbac
 {
   bool useBlobImage = gfxPrefs::WebRenderBlobImages() && !aItem->MustPaintOnContentSide();
   Maybe<gfx::Color> highlight = Nothing();
   if (gfxPrefs::WebRenderHighlightPaintedLayers()) {
     highlight = Some(useBlobImage ? gfx::Color(1.0, 0.0, 0.0, 0.5)
                                   : gfx::Color(1.0, 1.0, 0.0, 0.5));
   }
 
-  RefPtr<WebRenderFallbackData> fallbackData = CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem);
+  RefPtr<WebRenderFallbackData> fallbackData =
+    CreateOrRecycleWebRenderUserData<WebRenderFallbackData>(aItem,
+                                                            aBuilder.GetRenderRoot());
 
   bool snap;
   nsRect itemBounds = aItem->GetBounds(aDisplayListBuilder, &snap);
 
   // Blob images will only draw the visible area of the blob so we don't need to clip
   // them here and can just rely on the webrender clipping.
   // TODO We also don't clip native themed widget to avoid over-invalidation during scrolling.
   // it would be better to support a sort of straming/tiling scheme for large ones but the hope
@@ -1970,17 +2007,19 @@ WebRenderCommandBuilder::GenerateFallbac
       std::vector<RefPtr<ScaledFont>> fonts;
 
       RefPtr<WebRenderDrawEventRecorder> recorder =
         MakeAndAddRef<WebRenderDrawEventRecorder>([&] (MemStream &aStream, std::vector<RefPtr<ScaledFont>> &aScaledFonts) {
           size_t count = aScaledFonts.size();
           aStream.write((const char*)&count, sizeof(count));
           for (auto& scaled : aScaledFonts) {
             BlobFont font = {
-              mManager->WrBridge()->GetFontKeyForScaledFont(scaled, &aResources),
+              mManager->WrBridge()->GetFontKeyForScaledFont(scaled,
+                                                            aBuilder.GetRenderRoot(),
+                                                            &aResources),
               scaled
             };
             aStream.write((const char*)&font, sizeof(font));
           }
           fonts = std::move(aScaledFonts);
         });
       RefPtr<gfx::DrawTarget> dummyDt =
         gfx::Factory::CreateDrawTarget(gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
@@ -2029,17 +2068,17 @@ WebRenderCommandBuilder::GenerateFallbac
           isInvalidated = PaintItemByDrawTarget(aItem, dt, offset,
                                                 aDisplayListBuilder,
                                                 fallbackData->mBasicLayerManager, scale,
                                                 highlight);
         }
 
         if (isInvalidated) {
           // Update image if there it's invalidated.
-          if (!helper.UpdateImage()) {
+          if (!helper.UpdateImage(aBuilder.GetRenderRoot())) {
             return nullptr;
           }
         } else {
           // If there is no invalidation region and we don't have a image key,
           // it means we don't need to push image for the item.
           if (!fallbackData->GetImageKey().isSome()) {
             return nullptr;
           }
@@ -2124,17 +2163,18 @@ WebRenderCommandBuilder::RemoveUnusedAnd
 
       MOZ_ASSERT(frame->HasProperty(WebRenderUserDataProperty::Key()));
 
       WebRenderUserDataTable* userDataTable =
         frame->GetProperty(WebRenderUserDataProperty::Key());
 
       MOZ_ASSERT(userDataTable->Count());
 
-      userDataTable->Remove(WebRenderUserDataKey(data->GetDisplayItemKey(), data->GetType()));
+      userDataTable->Remove(WebRenderUserDataKey(data->GetDisplayItemKey(),
+                                                 data->GetType()));
 
       if (!userDataTable->Count()) {
         frame->RemoveProperty(WebRenderUserDataProperty::Key());
         delete userDataTable;
       }
 
       if (data->GetType() == WebRenderUserData::UserDataType::eCanvas) {
         mLastCanvasDatas.RemoveEntry(data->AsCanvasData());
@@ -2154,18 +2194,19 @@ WebRenderCommandBuilder::ClearCachedReso
   RemoveUnusedAndResetWebRenderUserData();
   // UserDatas should only be in the used state during a call to WebRenderCommandBuilder::BuildWebRenderCommands
   // The should always be false upon return from BuildWebRenderCommands().
   MOZ_RELEASE_ASSERT(mWebRenderUserDatas.Count() == 0);
 }
 
 
 
-WebRenderGroupData::WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderUserData(aWRManager, aItem)
+WebRenderGroupData::WebRenderGroupData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                       wr::RenderRoot aRenderRoot)
+  : WebRenderUserData(aWRManager, aItem, aRenderRoot)
 {
   MOZ_COUNT_CTOR(WebRenderGroupData);
 }
 
 WebRenderGroupData::~WebRenderGroupData()
 {
   MOZ_COUNT_DTOR(WebRenderGroupData);
   GP("Group data destruct\n");
--- a/gfx/layers/wr/WebRenderCommandBuilder.h
+++ b/gfx/layers/wr/WebRenderCommandBuilder.h
@@ -32,16 +32,17 @@ class WebRenderUserData;
 
 class WebRenderCommandBuilder {
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderUserData>> WebRenderUserDataRefTable;
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderCanvasData>> CanvasDataSet;
 
 public:
   explicit WebRenderCommandBuilder(WebRenderLayerManager* aManager)
   : mManager(aManager)
+  , mCurrentClipManager(nullptr)
   , mLastAsr(nullptr)
   , mBuilderDumpIndex(0)
   , mDumpIndent(0)
   , mDoGrouping(false)
   , mContainsSVGGroup(false)
   {}
 
   void Destroy();
@@ -50,17 +51,16 @@ public:
 
   bool NeedsEmptyTransaction();
 
   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 wr::WrClipId& aClipId);
   void PopOverrideForASR(const ActiveScrolledRoot* aASR);
 
   Maybe<wr::ImageKey> CreateImageKey(nsDisplayItem* aItem,
                                      ImageContainer* aContainer,
@@ -124,16 +124,17 @@ public:
   bool GetContainsSVGGroup() { return mContainsSVGGroup; }
 
   // Those are data that we kept between transactions. We used to cache some
   // data in the layer. But in layers free mode, we don't have layer which
   // means we need some other place to cached the data between transaction.
   // We store the data in frame's property.
   template<class T> already_AddRefed<T>
   CreateOrRecycleWebRenderUserData(nsDisplayItem* aItem,
+                                   wr::RenderRoot aRenderRoot,
                                    bool* aOutIsRecycled = nullptr)
   {
     MOZ_ASSERT(aItem);
     nsIFrame* frame = aItem->Frame();
     if (aOutIsRecycled) {
       *aOutIsRecycled = true;
     }
 
@@ -146,40 +147,43 @@ public:
     }
 
     RefPtr<WebRenderUserData>& data = userDataTable->GetOrInsert(WebRenderUserDataKey(aItem->GetPerFrameKey(), T::Type()));
     if (!data) {
       // To recreate a new user data, we should remove the data from the table first.
       if (data) {
         data->RemoveFromTable();
       }
-      data = new T(mManager, aItem);
+      data = new T(mManager, aItem, aRenderRoot);
       mWebRenderUserDatas.PutEntry(data);
       if (aOutIsRecycled) {
         *aOutIsRecycled = false;
       }
     }
 
     MOZ_ASSERT(data);
     MOZ_ASSERT(data->GetType() == T::Type());
+    MOZ_ASSERT(data->GetRenderRoot() == aRenderRoot);
 
     // Mark the data as being used. We will remove unused user data in the end of EndTransaction.
     data->SetUsed(true);
 
     if (T::Type() == WebRenderUserData::UserDataType::eCanvas) {
       mLastCanvasDatas.PutEntry(data->AsCanvasData());
     }
     RefPtr<T> res = static_cast<T*>(data.get());
     return res.forget();
   }
 
   WebRenderLayerManager* mManager;
 
 private:
   ClipManager mClipManager;
+  ClipManager mContentRectClipManager;
+  ClipManager* mCurrentClipManager;
 
   // 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/layers/wr/WebRenderLayerManager.cpp
+++ b/gfx/layers/wr/WebRenderLayerManager.cpp
@@ -36,16 +36,17 @@ WebRenderLayerManager::WebRenderLayerMan
   , mLatestTransactionId{0}
   , mWindowOverlayChanged(false)
   , mNeedsComposite(false)
   , mIsFirstPaint(false)
   , mTarget(nullptr)
   , mPaintSequenceNumber(0)
   , mWebRenderCommandBuilder(this)
   , mLastDisplayListSize(0)
+  , mLastContentDisplayListSize(0)
 {
   MOZ_COUNT_CTOR(WebRenderLayerManager);
 }
 
 KnowsCompositor*
 WebRenderLayerManager::AsKnowsCompositor()
 {
   return mWrChild;
@@ -68,18 +69,20 @@ WebRenderLayerManager::Initialize(PCompo
     // reinitialization. We can expect to be notified again to reinitialize
     // (which may or may not be using WebRender).
     gfxCriticalNote << "Failed to create WebRenderBridgeChild.";
     return false;
   }
 
   TextureFactoryIdentifier textureFactoryIdentifier;
   wr::MaybeIdNamespace idNamespace;
+  wr::RenderRoot renderRoot = (mWidget->GetOwningTabChild() && gfxPrefs::WebRenderSplitRenderRoots()) ?
+    wr::RenderRoot::Content : wr::RenderRoot::Default;
   // Sync ipc
-  bridge->SendEnsureConnected(&textureFactoryIdentifier, &idNamespace);
+  bridge->SendEnsureConnected(renderRoot, &textureFactoryIdentifier, &idNamespace);
   if (textureFactoryIdentifier.mParentBackend == LayersBackend::LAYERS_NONE ||
       idNamespace.isNothing()) {
     gfxCriticalNote << "Failed to connect WebRenderBridgeChild.";
     return false;
   }
 
   mWrChild = static_cast<WebRenderBridgeChild*>(bridge);
   WrBridge()->SetWebRenderLayerManager(this);
@@ -284,18 +287,36 @@ WebRenderLayerManager::EndTransactionWit
   AUTO_PROFILER_TRACING("Paint", "RenderLayers");
 
   // Since we don't do repeat transactions right now, just set the time
   mAnimationReadyTime = TimeStamp::Now();
 
   WrBridge()->BeginTransaction();
 
   LayoutDeviceIntSize size = mWidget->GetClientSize();
-  wr::LayoutSize contentSize { (float)size.width, (float)size.height };
-  wr::DisplayListBuilder builder(WrBridge()->GetPipeline(), contentSize, mLastDisplayListSize);
+
+  LayoutDeviceRect contentRect = aDisplayListBuilder ?
+    aDisplayListBuilder->GetContentRenderRootRect() :
+    LayoutDeviceRect();
+
+  LayoutDeviceRect mainRect;
+  if (!contentRect.IsEmpty()) {
+    mainRect = LayoutDeviceRect(0, 0, size.width, contentRect.y);
+  } else {
+    mainRect = LayoutDeviceRect(0, 0, size.width, size.height);
+  }
+  wr::LayoutRect mainLayoutRect = wr::ToRoundedLayoutRect(mainRect);
+  wr::DisplayListBuilder builder(WrBridge()->GetPipeline(),
+                                 mainLayoutRect.size,
+                                 mLastDisplayListSize);
+  wr::LayoutRect contentLayoutRect = wr::ToRoundedLayoutRect(contentRect);
+  if (!contentRect.IsEmpty()) {
+    builder.CreateSubBuilder(contentLayoutRect.size, mLastContentDisplayListSize);
+  }
+
   wr::IpcResourceUpdateQueue resourceUpdates(WrBridge());
   wr::usize builderDumpIndex = 0;
   bool containsSVGGroup = false;
   bool dumpEnabled = mWebRenderCommandBuilder.ShouldDumpDisplayList(aDisplayListBuilder);
   if (dumpEnabled) {
     printf_stderr("-- WebRender display list build --\n");
   }
 
@@ -305,17 +326,16 @@ WebRenderLayerManager::EndTransactionWit
     // generating the WR display list is the closest equivalent
     PaintTelemetry::AutoRecord record(PaintTelemetry::Metric::Layerization);
 
     mWebRenderCommandBuilder.BuildWebRenderCommands(builder,
                                                     resourceUpdates,
                                                     aDisplayList,
                                                     aDisplayListBuilder,
                                                     mScrollData,
-                                                    contentSize,
                                                     aFilters);
     builderDumpIndex = mWebRenderCommandBuilder.GetBuilderDumpIndex();
     containsSVGGroup = mWebRenderCommandBuilder.GetContainsSVGGroup();
   } else {
     // ViewToPaint does not have frame yet, then render only background clolor.
     MOZ_ASSERT(!aDisplayListBuilder && aBackground);
     aBackground->AddWebRenderCommands(builder);
     if (dumpEnabled) {
@@ -353,46 +373,71 @@ WebRenderLayerManager::EndTransactionWit
   // Get the time of when the refresh driver start its tick (if available), otherwise
   // use the time of when LayerManager::BeginTransaction was called.
   TimeStamp refreshStart = mTransactionIdAllocator->GetTransactionStart();
   if (!refreshStart) {
     refreshStart = mTransactionStart;
   }
 
   if (mAsyncResourceUpdates) {
-    if (resourceUpdates.IsEmpty()) {
+    if (resourceUpdates.IsEmpty() && !resourceUpdates.HasSubQueue()) {
       resourceUpdates = std::move(mAsyncResourceUpdates.ref());
     } else {
       // If we can't just swap the queue, we need to take the slow path and
       // send the update as a separate message. We don't need to schedule a
       // composite however because that will happen with EndTransaction.
-      WrBridge()->UpdateResources(mAsyncResourceUpdates.ref());
+      if (mAsyncResourceUpdates->HasSubQueue()) {
+        WrBridge()->UpdateResources(mAsyncResourceUpdates->SubQueue(),
+                                    wr::RenderRoot::Content);
+      }
+
+      if (!mAsyncResourceUpdates->IsEmpty()) {
+        WrBridge()->UpdateResources(mAsyncResourceUpdates.ref(),
+                                    wr::RenderRoot::Default);
+      }
     }
     mAsyncResourceUpdates.reset();
   }
 
-  DiscardImagesInTransaction(resourceUpdates);
+  DiscardImagesInTransaction(resourceUpdates, wr::RenderRoot::Default);
+  DiscardImagesInTransaction(resourceUpdates.SubQueue(), wr::RenderRoot::Content);
+
   WrBridge()->RemoveExpiredFontKeys(resourceUpdates);
+  if (resourceUpdates.HasSubQueue()) {
+    WrBridge()->RemoveExpiredFontKeys(resourceUpdates.SubQueue());
+  }
 
   // Skip the synchronization for buffer since we also skip the painting during
   // device-reset status.
   if (!gfxPlatform::GetPlatform()->DidRenderingDeviceReset()) {
     if (WrBridge()->GetSyncObject() &&
         WrBridge()->GetSyncObject()->IsSyncObjectValid()) {
       WrBridge()->GetSyncObject()->Synchronize();
     }
   }
 
   wr::BuiltDisplayList dl;
-  builder.Finalize(contentSize, dl);
+  wr::LayoutSize mainContentSize;
+  wr::BuiltDisplayList contentDL = {};
+  wr::LayoutSize contentContentSize = {};
+  builder.Finalize(mainContentSize, dl);
+  if (builder.GetSendSubBuilderDisplayList()) {
+    builder.Finalize(contentContentSize, contentDL, wr::RenderRoot::Content);
+  }
+
   mLastDisplayListSize = dl.dl.inner.capacity;
+  mLastContentDisplayListSize = contentDL.dl.inner.capacity;
 
   {
     AUTO_PROFILER_TRACING("Paint", "ForwardDPTransaction");
-    WrBridge()->EndTransaction(contentSize, dl, resourceUpdates, size.ToUnknownSize(),
+    WrBridge()->EndTransaction(mainContentSize, dl, resourceUpdates,
+                               RoundedToInt(mainRect).ToUnknownRect(),
+                               builder.GetSendSubBuilderDisplayList(),
+                               contentContentSize, contentDL, resourceUpdates.SubQueue(),
+                               RoundedToInt(contentRect).ToUnknownRect(),
                                mLatestTransactionId, mScrollData, containsSVGGroup,
                                refreshStart, mTransactionStart, mURL);
   }
 
   mTransactionStart = TimeStamp();
 
   MakeSnapshotIfRequired(size);
   mNeedsComposite = false;
@@ -468,46 +513,61 @@ WebRenderLayerManager::MakeSnapshotIfReq
   DrawTarget* dt = mTarget->GetDrawTarget();
   MOZ_RELEASE_ASSERT(dt);
   dt->FillRect(dst, pattern);
 
   mTarget = nullptr;
 }
 
 void
-WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key)
+WebRenderLayerManager::AddImageKeyForDiscard(wr::ImageKey key, wr::RenderRoot aRenderRoot)
 {
-  mImageKeysToDelete.AppendElement(key);
+  if (aRenderRoot == wr::RenderRoot::Content) {
+    mContentRectImageKeysToDelete.AppendElement(key);
+  } else {
+    mImageKeysToDelete.AppendElement(key);
+  }
 }
 
 void
-WebRenderLayerManager::AddBlobImageKeyForDiscard(wr::BlobImageKey key)
+WebRenderLayerManager::AddBlobImageKeyForDiscard(wr::BlobImageKey key, wr::RenderRoot aRenderRoot)
 {
-  mBlobImageKeysToDelete.AppendElement(key);
+  if (aRenderRoot == wr::RenderRoot::Content) {
+    mContentRectBlobImageKeysToDelete.AppendElement(key);
+  } else {
+    mBlobImageKeysToDelete.AppendElement(key);
+  }
 }
 
 void
-WebRenderLayerManager::DiscardImagesInTransaction(wr::IpcResourceUpdateQueue& aResources)
+WebRenderLayerManager::DiscardImagesInTransaction(wr::IpcResourceUpdateQueue& aResources,
+                                                  wr::RenderRoot aRenderRoot)
 {
-  for (const auto& key : mImageKeysToDelete) {
+  auto& imageKeysToDelete = aRenderRoot == wr::RenderRoot::Content ?
+    mContentRectImageKeysToDelete : mImageKeysToDelete;
+  auto& blobImageKeysToDelete = aRenderRoot == wr::RenderRoot::Content ?
+    mContentRectBlobImageKeysToDelete : mBlobImageKeysToDelete;
+  for (const auto& key : imageKeysToDelete) {
     aResources.DeleteImage(key);
   }
-  for (const auto& key : mBlobImageKeysToDelete) {
+  for (const auto& key : blobImageKeysToDelete) {
     aResources.DeleteBlobImage(key);
   }
-  mImageKeysToDelete.Clear();
-  mBlobImageKeysToDelete.Clear();
+  imageKeysToDelete.Clear();
+  blobImageKeysToDelete.Clear();
 }
 
 void
 WebRenderLayerManager::DiscardImages()
 {
   wr::IpcResourceUpdateQueue resources(WrBridge());
-  DiscardImagesInTransaction(resources);
-  WrBridge()->UpdateResources(resources);
+  DiscardImagesInTransaction(resources, wr::RenderRoot::Default);
+  WrBridge()->UpdateResources(resources, wr::RenderRoot::Default);
+  DiscardImagesInTransaction(resources.SubQueue(), wr::RenderRoot::Content);
+  WrBridge()->UpdateResources(resources.SubQueue(), wr::RenderRoot::Content);
 }
 
 void
 WebRenderLayerManager::AddActiveCompositorAnimationId(uint64_t aId)
 {
   // In layers-free mode we track the active compositor animation ids on the
   // client side so that we don't try to discard the same animation id multiple
   // times. We could just ignore the multiple-discard on the parent side, but
@@ -539,17 +599,19 @@ WebRenderLayerManager::DiscardCompositor
 
 void
 WebRenderLayerManager::DiscardLocalImages()
 {
   // Removes images but doesn't tell the parent side about them
   // This is useful in empty / failed transactions where we created
   // image keys but didn't tell the parent about them yet.
   mImageKeysToDelete.Clear();
+  mContentRectImageKeysToDelete.Clear();
   mBlobImageKeysToDelete.Clear();
+  mContentRectBlobImageKeysToDelete.Clear();
 }
 
 void
 WebRenderLayerManager::SetLayersObserverEpoch(LayersObserverEpoch aEpoch)
 {
   if (WrBridge()->IPCOpen()) {
     WrBridge()->SendSetLayersObserverEpoch(aEpoch);
   }
@@ -768,17 +830,24 @@ WebRenderLayerManager::FlushAsyncResourc
 {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mAsyncResourceUpdates) {
     return;
   }
 
   if (!IsDestroyed() && WrBridge()) {
-    WrBridge()->UpdateResources(mAsyncResourceUpdates.ref());
+    if (!mAsyncResourceUpdates->IsEmpty()) {
+      WrBridge()->UpdateResources(mAsyncResourceUpdates.ref(),
+                                  wr::RenderRoot::Default);
+    }
+    if (mAsyncResourceUpdates->HasSubQueue()) {
+      WrBridge()->UpdateResources(mAsyncResourceUpdates->SubQueue(),
+                                  wr::RenderRoot::Content);
+    }
   }
 
   mAsyncResourceUpdates.reset();
 }
 
 void
 WebRenderLayerManager::RegisterAsyncAnimation(const wr::ImageKey& aKey,
                                               SharedSurfacesAnimation* aAnimation)
--- a/gfx/layers/wr/WebRenderLayerManager.h
+++ b/gfx/layers/wr/WebRenderLayerManager.h
@@ -128,20 +128,21 @@ public:
 
   virtual already_AddRefed<PersistentBufferProvider>
   CreatePersistentBufferProvider(const gfx::IntSize& aSize, gfx::SurfaceFormat aFormat) override;
 
   bool AsyncPanZoomEnabled() const override;
 
   // adds an imagekey to a list of keys that will be discarded on the next
   // transaction or destruction
-  void AddImageKeyForDiscard(wr::ImageKey);
-  void AddBlobImageKeyForDiscard(wr::BlobImageKey);
+  void AddImageKeyForDiscard(wr::ImageKey, wr::RenderRoot aRenderRoot);
+  void AddBlobImageKeyForDiscard(wr::BlobImageKey, wr::RenderRoot aRenderRoot);
   void DiscardImages();
-  void DiscardImagesInTransaction(wr::IpcResourceUpdateQueue& aResourceUpdates);
+  void DiscardImagesInTransaction(wr::IpcResourceUpdateQueue& aResourceUpdates,
+                                  wr::RenderRoot aRenderRoot);
   void DiscardLocalImages();
 
   wr::IpcResourceUpdateQueue& AsyncResourceUpdates();
   void FlushAsyncResourceUpdates();
 
   void RegisterAsyncAnimation(const wr::ImageKey& aKey, SharedSurfacesAnimation* aAnimation);
   void DeregisterAsyncAnimation(const wr::ImageKey& aKey);
   void ClearAsyncAnimations();
@@ -186,17 +187,19 @@ private:
    * Take a snapshot of the parent context, and copy
    * it into mTarget.
    */
   void MakeSnapshotIfRequired(LayoutDeviceIntSize aSize);
 
 private:
   nsIWidget* MOZ_NON_OWNING_REF mWidget;
   nsTArray<wr::ImageKey> mImageKeysToDelete;
+  nsTArray<wr::ImageKey> mContentRectImageKeysToDelete;
   nsTArray<wr::BlobImageKey> mBlobImageKeysToDelete;
+  nsTArray<wr::BlobImageKey> mContentRectBlobImageKeysToDelete;
 
   // Set of compositor animation ids for which there are active animations (as
   // of the last transaction) on the compositor side.
   std::unordered_set<uint64_t> mActiveCompositorAnimationIds;
   // Compositor animation ids for animations that are done now and that we want
   // the compositor to discard information for.
   nsTArray<uint64_t> mDiscardedCompositorAnimationsIds;
 
@@ -230,16 +233,17 @@ private:
   // See equivalent field in ClientLayerManager
   APZTestData mApzTestData;
 
   TimeStamp mTransactionStart;
   nsCString mURL;
   WebRenderCommandBuilder mWebRenderCommandBuilder;
 
   size_t mLastDisplayListSize;
+  size_t mLastContentDisplayListSize;
 
   Maybe<wr::IpcResourceUpdateQueue> mAsyncResourceUpdates;
   std::unordered_map<uint64_t, RefPtr<SharedSurfacesAnimation>> mAsyncAnimations;
 };
 
 } // namespace layers
 } // namespace mozilla
 
--- a/gfx/layers/wr/WebRenderMessageUtils.h
+++ b/gfx/layers/wr/WebRenderMessageUtils.h
@@ -146,16 +146,25 @@ struct ParamTraits<mozilla::wr::LayoutPo
 
 template<>
 struct ParamTraits<mozilla::wr::WrImageMask>
   : public PlainOldDataSerializer<mozilla::wr::WrImageMask>
 {
 };
 
 template<>
+struct ParamTraits<mozilla::wr::RenderRoot>
+  : public ContiguousEnumSerializer<
+        mozilla::wr::RenderRoot,
+        mozilla::wr::RenderRoot::Default,
+        mozilla::wr::RenderRoot::Count>
+{
+};
+
+template<>
 struct ParamTraits<mozilla::wr::ImageRendering>
   : public ContiguousEnumSerializer<
         mozilla::wr::ImageRendering,
         mozilla::wr::ImageRendering::Auto,
         mozilla::wr::ImageRendering::Sentinel>
 {
 };
 
--- a/gfx/layers/wr/WebRenderScrollData.cpp
+++ b/gfx/layers/wr/WebRenderScrollData.cpp
@@ -18,16 +18,17 @@ namespace mozilla {
 namespace layers {
 
 WebRenderLayerScrollData::WebRenderLayerScrollData()
   : mDescendantCount(-1)
   , mTransformIsPerspective(false)
   , mEventRegionsOverride(EventRegionsOverride::NoOverride)
   , mScrollbarAnimationId(0)
   , mFixedPosScrollContainerId(ScrollableLayerGuid::NULL_SCROLL_ID)
+  , mRenderRoot(wr::RenderRoot::Default)
 {
 }
 
 WebRenderLayerScrollData::~WebRenderLayerScrollData()
 {
 }
 
 void
@@ -150,23 +151,25 @@ WebRenderLayerScrollData::Dump(const Web
     (int)mScrollbarData.mScrollbarLayerType, mScrollbarAnimationId);
   printf_stderr("  fixed pos container: %" PRIu64 "\n",
     mFixedPosScrollContainerId);
 }
 
 WebRenderScrollData::WebRenderScrollData()
   : mManager(nullptr)
   , mIsFirstPaint(false)
+  , mSkippedContentRect(false)
   , mPaintSequenceNumber(0)
 {
 }
 
 WebRenderScrollData::WebRenderScrollData(WebRenderLayerManager* aManager)
   : mManager(aManager)
   , mIsFirstPaint(false)
+  , mSkippedContentRect(false)
   , mPaintSequenceNumber(0)
 {
 }
 
 WebRenderScrollData::~WebRenderScrollData()
 {
 }
 
@@ -239,16 +242,28 @@ WebRenderScrollData::SetIsFirstPaint()
 
 bool
 WebRenderScrollData::IsFirstPaint() const
 {
   return mIsFirstPaint;
 }
 
 void
+WebRenderScrollData::SetSkippedContentRect()
+{
+  mSkippedContentRect = true;
+}
+
+bool
+WebRenderScrollData::SkippedContentRect() const
+{
+  return mSkippedContentRect;
+}
+
+void
 WebRenderScrollData::SetPaintSequenceNumber(uint32_t aPaintSequenceNumber)
 {
   mPaintSequenceNumber = aPaintSequenceNumber;
 }
 
 uint32_t
 WebRenderScrollData::GetPaintSequenceNumber() const
 {
--- a/gfx/layers/wr/WebRenderScrollData.h
+++ b/gfx/layers/wr/WebRenderScrollData.h
@@ -13,16 +13,18 @@
 #include "FrameMetrics.h"
 #include "ipc/IPCMessageUtils.h"
 #include "LayersTypes.h"
 #include "mozilla/Attributes.h"
 #include "mozilla/GfxMessageUtils.h"
 #include "mozilla/layers/LayerAttributes.h"
 #include "mozilla/layers/LayersMessageUtils.h"
 #include "mozilla/layers/FocusTarget.h"
+#include "mozilla/layers/WebRenderMessageUtils.h"
+#include "mozilla/webrender/WebRenderTypes.h"
 #include "mozilla/Maybe.h"
 #include "nsTArrayForwardDeclare.h"
 
 class nsDisplayItem;
 
 namespace mozilla {
 
 struct ActiveScrolledRoot;
@@ -81,16 +83,19 @@ public:
   void SetScrollbarData(const ScrollbarData& aData) { mScrollbarData = aData; }
   const ScrollbarData& GetScrollbarData() const { return mScrollbarData; }
   void SetScrollbarAnimationId(const uint64_t& aId) { mScrollbarAnimationId = aId; }
   const uint64_t& GetScrollbarAnimationId() const { return mScrollbarAnimationId; }
 
   void SetFixedPositionScrollContainerId(ScrollableLayerGuid::ViewID aId) { mFixedPosScrollContainerId = aId; }
   ScrollableLayerGuid::ViewID GetFixedPositionScrollContainerId() const { return mFixedPosScrollContainerId; }
 
+  void SetRenderRoot(wr::RenderRoot aRenderRoot) { mRenderRoot = aRenderRoot; }
+  wr::RenderRoot GetRenderRoot() { return mRenderRoot; }
+
   void SetZoomAnimationId(const uint64_t& aId) { mZoomAnimationId = Some(aId); }
   Maybe<uint64_t> GetZoomAnimationId() const { return mZoomAnimationId; }
 
   void Dump(const WebRenderScrollData& aOwner) const;
 
   friend struct IPC::ParamTraits<WebRenderLayerScrollData>;
 
 private:
@@ -113,16 +118,17 @@ private:
   gfx::Matrix4x4 mTransform;
   bool mTransformIsPerspective;
   LayerIntRegion mVisibleRegion;
   Maybe<LayersId> mReferentId;
   EventRegionsOverride mEventRegionsOverride;
   ScrollbarData mScrollbarData;
   uint64_t mScrollbarAnimationId;
   ScrollableLayerGuid::ViewID mFixedPosScrollContainerId;
+  wr::RenderRoot mRenderRoot;
   Maybe<uint64_t> mZoomAnimationId;
 };
 
 // Data needed by APZ, for the whole layer tree. One instance of this class
 // is created for each transaction sent over PWebRenderBridge. It is populated
 // with information from the WebRender layer tree on the client side and the
 // information is used by APZ on the parent side.
 class WebRenderScrollData
@@ -150,16 +156,18 @@ public:
   const ScrollMetadata& GetScrollMetadata(size_t aIndex) const;
   Maybe<size_t> HasMetadataFor(const ScrollableLayerGuid::ViewID& aScrollId) const;
 
   const FocusTarget& GetFocusTarget() const { return mFocusTarget; }
   void SetFocusTarget(const FocusTarget& aFocusTarget);
 
   void SetIsFirstPaint();
   bool IsFirstPaint() const;
+  void SetSkippedContentRect();
+  bool SkippedContentRect() const;
   void SetPaintSequenceNumber(uint32_t aPaintSequenceNumber);
   uint32_t GetPaintSequenceNumber() const;
 
   void ApplyUpdates(const ScrollUpdatesMap& aUpdates,
                     uint32_t aPaintSequenceNumber);
 
   friend struct IPC::ParamTraits<WebRenderScrollData>;
 
@@ -195,16 +203,17 @@ private:
   // descendants that layer had, which allows reconstructing the traversal on the
   // other side.
   nsTArray<WebRenderLayerScrollData> mLayerScrollData;
 
   // The focus information for this layer tree
   FocusTarget mFocusTarget;
 
   bool mIsFirstPaint;
+  bool mSkippedContentRect;
   uint32_t mPaintSequenceNumber;
 };
 
 } // namespace layers
 } // namespace mozilla
 
 namespace IPC {
 
@@ -233,16 +242,17 @@ struct ParamTraits<mozilla::layers::WebR
     WriteParam(aMsg, aParam.mTransform);
     WriteParam(aMsg, aParam.mTransformIsPerspective);
     WriteParam(aMsg, aParam.mVisibleRegion);
     WriteParam(aMsg, aParam.mReferentId);
     WriteParam(aMsg, aParam.mEventRegionsOverride);
     WriteParam(aMsg, aParam.mScrollbarData);
     WriteParam(aMsg, aParam.mScrollbarAnimationId);
     WriteParam(aMsg, aParam.mFixedPosScrollContainerId);
+    WriteParam(aMsg, aParam.mRenderRoot);
     WriteParam(aMsg, aParam.mZoomAnimationId);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->mDescendantCount)
         && ReadParam(aMsg, aIter, &aResult->mScrollIds)
@@ -250,42 +260,45 @@ struct ParamTraits<mozilla::layers::WebR
         && ReadParam(aMsg, aIter, &aResult->mTransform)
         && ReadParam(aMsg, aIter, &aResult->mTransformIsPerspective)
         && ReadParam(aMsg, aIter, &aResult->mVisibleRegion)
         && ReadParam(aMsg, aIter, &aResult->mReferentId)
         && ReadParam(aMsg, aIter, &aResult->mEventRegionsOverride)
         && ReadParam(aMsg, aIter, &aResult->mScrollbarData)
         && ReadParam(aMsg, aIter, &aResult->mScrollbarAnimationId)
         && ReadParam(aMsg, aIter, &aResult->mFixedPosScrollContainerId)
+        && ReadParam(aMsg, aIter, &aResult->mRenderRoot)
         && ReadParam(aMsg, aIter, &aResult->mZoomAnimationId);
   }
 };
 
 template<>
 struct ParamTraits<mozilla::layers::WebRenderScrollData>
 {
   typedef mozilla::layers::WebRenderScrollData paramType;
 
   static void
   Write(Message* aMsg, const paramType& aParam)
   {
     WriteParam(aMsg, aParam.mScrollMetadatas);
     WriteParam(aMsg, aParam.mLayerScrollData);
     WriteParam(aMsg, aParam.mFocusTarget);
     WriteParam(aMsg, aParam.mIsFirstPaint);
+    WriteParam(aMsg, aParam.mSkippedContentRect);
     WriteParam(aMsg, aParam.mPaintSequenceNumber);
   }
 
   static bool
   Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     return ReadParam(aMsg, aIter, &aResult->mScrollMetadatas)
         && ReadParam(aMsg, aIter, &aResult->mLayerScrollData)
         && ReadParam(aMsg, aIter, &aResult->mFocusTarget)
         && ReadParam(aMsg, aIter, &aResult->mIsFirstPaint)
+        && ReadParam(aMsg, aIter, &aResult->mSkippedContentRect)
         && ReadParam(aMsg, aIter, &aResult->mPaintSequenceNumber)
         && aResult->RepopulateMap();
   }
 };
 
 } // namespace IPC
 
 #endif /* GFX_WEBRENDERSCROLLDATA_H */
--- a/gfx/layers/wr/WebRenderUserData.cpp
+++ b/gfx/layers/wr/WebRenderUserData.cpp
@@ -66,22 +66,24 @@ WebRenderUserData::ProcessInvalidateForI
   if (image && image->IsAsyncAnimatedImage()) {
     return true;
   }
 
   aFrame->SchedulePaint();
   return false;
 }
 
-WebRenderUserData::WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
+WebRenderUserData::WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                     wr::RenderRoot aRenderRoot)
   : mWRManager(aWRManager)
   , mFrame(aItem->Frame())
   , mDisplayItemKey(aItem->GetPerFrameKey())
   , mTable(aWRManager->GetWebRenderUserDataTable())
   , mUsed(false)
+  , mRenderRoot(aRenderRoot)
 {
 }
 
 WebRenderUserData::~WebRenderUserData()
 {
 }
 
 void
@@ -91,47 +93,48 @@ WebRenderUserData::RemoveFromTable()
 }
 
 WebRenderBridgeChild*
 WebRenderUserData::WrBridge() const
 {
   return mWRManager->WrBridge();
 }
 
-WebRenderImageData::WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderUserData(aWRManager, aItem)
+WebRenderImageData::WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                       wr::RenderRoot aRenderRoot)
+  : WebRenderUserData(aWRManager, aItem, aRenderRoot)
   , mOwnsKey(false)
 {
 }
 
 WebRenderImageData::~WebRenderImageData()
 {
   ClearImageKey();
 
   if (mPipelineId) {
-    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(), mRenderRoot);
   }
 }
 
 bool
 WebRenderImageData::IsAsyncAnimatedImage() const
 {
   return mContainer && mContainer->GetSharedSurfacesAnimation();
 }
 
 void
 WebRenderImageData::ClearImageKey()
 {
   if (mKey) {
     // If we don't own the key, then the owner is responsible for discarding the
     // key when appropriate.
     if (mOwnsKey) {
-      mWRManager->AddImageKeyForDiscard(mKey.value());
+      mWRManager->AddImageKeyForDiscard(mKey.value(), mRenderRoot);
       if (mTextureOfImage) {
-        WrBridge()->ReleaseTextureOfImage(mKey.value());
+        WrBridge()->ReleaseTextureOfImage(mKey.value(), mRenderRoot);
         mTextureOfImage = nullptr;
       }
     }
     mKey.reset();
   }
   mOwnsKey = false;
   MOZ_ASSERT(!mTextureOfImage);
 }
@@ -171,17 +174,17 @@ WebRenderImageData::UpdateImageKey(Image
     return Nothing();
   }
 
   MOZ_ASSERT(mImageClient->AsImageClientSingle());
 
   ImageClientSingle* imageClient = mImageClient->AsImageClientSingle();
   uint32_t oldCounter = imageClient->GetLastUpdateGenerationCounter();
 
-  bool ret = imageClient->UpdateImage(aContainer, /* unused */0);
+  bool ret = imageClient->UpdateImage(aContainer, /* unused */0, mRenderRoot);
   RefPtr<TextureClient> currentTexture = imageClient->GetForwardedTexture();
   if (!ret || !currentTexture) {
     // Delete old key
     ClearImageKey();
     return Nothing();
   }
 
   // Reuse old key if generation is not updated.
@@ -232,29 +235,31 @@ WebRenderImageData::CreateAsyncImageWebR
                                                       const LayoutDeviceRect& aSCBounds,
                                                       const gfx::Matrix4x4& aSCTransform,
                                                       const gfx::MaybeIntSize& aScaleToSize,
                                                       const wr::ImageRendering& aFilter,
                                                       const wr::MixBlendMode& aMixBlendMode,
                                                       bool aIsBackfaceVisible)
 {
   MOZ_ASSERT(aContainer->IsAsync());
+  MOZ_ASSERT(mRenderRoot == aBuilder.GetRenderRoot());
 
   if (mPipelineId.isSome() && mContainer != aContainer) {
     // In this case, we need to remove the existed pipeline and create new one
     // because the ImageContainer is changed.
-    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref());
+    WrBridge()->RemovePipelineIdForCompositable(mPipelineId.ref(), mRenderRoot);
     mPipelineId.reset();
   }
 
   if (!mPipelineId) {
     // Alloc async image pipeline id.
     mPipelineId = Some(WrBridge()->GetCompositorBridgeChild()->GetNextPipelineId());
     WrBridge()->AddPipelineIdForAsyncCompositable(mPipelineId.ref(),
-                                                  aContainer->GetAsyncContainerHandle());
+                                                  aContainer->GetAsyncContainerHandle(),
+                                                  mRenderRoot);
     mContainer = aContainer;
   }
   MOZ_ASSERT(!mImageClient);
 
   // Push IFrame for async image pipeline.
   //
   // We don't push a stacking context for this async image pipeline here.
   // Instead, we do it inside the iframe that hosts the image. As a result,
@@ -265,17 +270,18 @@ WebRenderImageData::CreateAsyncImageWebR
   wr::LayoutRect r = wr::ToRoundedLayoutRect(aBounds);
   aBuilder.PushIFrame(r, aIsBackfaceVisible, mPipelineId.ref(), /*ignoreMissingPipelines*/ false);
 
   WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(mPipelineId.value(),
                                                                    aSCBounds,
                                                                    aSCTransform,
                                                                    aScaleToSize,
                                                                    aFilter,
-                                                                   aMixBlendMode));
+                                                                   aMixBlendMode),
+                                        mRenderRoot);
 }
 
 void
 WebRenderImageData::CreateImageClientIfNeeded()
 {
   if (!mImageClient) {
     mImageClient = ImageClient::CreateImageClient(CompositableType::IMAGE,
                                                   WrBridge(),
@@ -283,18 +289,19 @@ WebRenderImageData::CreateImageClientIfN
     if (!mImageClient) {
       return;
     }
 
     mImageClient->Connect();
   }
 }
 
-WebRenderFallbackData::WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderImageData(aWRManager, aItem)
+WebRenderFallbackData::WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                             wr::RenderRoot aRenderRoot)
+  : WebRenderImageData(aWRManager, aItem, aRenderRoot)
   , mInvalid(false)
 {
 }
 
 WebRenderFallbackData::~WebRenderFallbackData()
 {
 }
 
@@ -327,42 +334,44 @@ WebRenderFallbackData::GetImageKey()
 
   return mKey;
 }
 
 void
 WebRenderFallbackData::ClearImageKey()
 {
   if (mBlobKey && mOwnsKey) {
-    mWRManager->AddBlobImageKeyForDiscard(mBlobKey.value());
+    mWRManager->AddBlobImageKeyForDiscard(mBlobKey.value(), mRenderRoot);
   }
   mBlobKey.reset();
 
   WebRenderImageData::ClearImageKey();
 }
 
-WebRenderAnimationData::WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderUserData(aWRManager, aItem)
+WebRenderAnimationData::WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                               wr::RenderRoot aRenderRoot)
+  : WebRenderUserData(aWRManager, aItem, aRenderRoot)
 {
 }
 
 WebRenderAnimationData::~WebRenderAnimationData()
 {
   // It may be the case that nsDisplayItem that created this WebRenderUserData
   // gets destroyed without getting a chance to discard the compositor animation
   // id, so we should do it as part of cleanup here.
   uint64_t animationId = mAnimationInfo.GetCompositorAnimationsId();
   // animationId might be 0 if mAnimationInfo never held any active animations.
   if (animationId) {
     mWRManager->AddCompositorAnimationsIdForDiscard(animationId);
   }
 }
 
-WebRenderCanvasData::WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem)
-  : WebRenderUserData(aWRManager, aItem)
+WebRenderCanvasData::WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                                         wr::RenderRoot aRenderRoot)
+  : WebRenderUserData(aWRManager, aItem, aRenderRoot)
 {
 }
 
 WebRenderCanvasData::~WebRenderCanvasData()
 {
   if (mCanvasRenderer) {
     mCanvasRenderer->ClearCachedResources();
   }
@@ -378,17 +387,18 @@ WebRenderCanvasRendererAsync*
 WebRenderCanvasData::GetCanvasRenderer()
 {
   return mCanvasRenderer.get();
 }
 
 WebRenderCanvasRendererAsync*
 WebRenderCanvasData::CreateCanvasRenderer()
 {
-  mCanvasRenderer = MakeUnique<WebRenderCanvasRendererAsync>(mWRManager);
+  mCanvasRenderer = MakeUnique<WebRenderCanvasRendererAsync>(mWRManager,
+                                                             mRenderRoot);
   return mCanvasRenderer.get();
 }
 
 void
 DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable)
 {
   for (auto iter = aTable->Iter(); !iter.Done(); iter.Next()) {
     iter.UserData()->RemoveFromTable();
--- a/gfx/layers/wr/WebRenderUserData.h
+++ b/gfx/layers/wr/WebRenderUserData.h
@@ -56,17 +56,18 @@ public:
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderUserData> > WebRenderUserDataRefTable;
 
   static bool SupportsAsyncUpdate(nsIFrame* aFrame);
 
   static bool ProcessInvalidateForImage(nsIFrame* aFrame, DisplayItemType aType);
 
   NS_INLINE_DECL_REFCOUNTING(WebRenderUserData)
 
-  WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderUserData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                    wr::RenderRoot aRenderRoot);
 
   virtual WebRenderImageData* AsImageData() { return nullptr; }
   virtual WebRenderFallbackData* AsFallbackData() { return nullptr; }
   virtual WebRenderCanvasData* AsCanvasData() { return nullptr; }
   virtual WebRenderGroupData* AsGroupData() { return nullptr; }
 
   enum class UserDataType {
     eImage,
@@ -76,28 +77,30 @@ public:
     eGroup,
   };
 
   virtual UserDataType GetType() = 0;
   bool IsUsed() { return mUsed; }
   void SetUsed(bool aUsed) { mUsed = aUsed; }
   nsIFrame* GetFrame() { return mFrame; }
   uint32_t GetDisplayItemKey() { return mDisplayItemKey; }
+  wr::RenderRoot GetRenderRoot() { return mRenderRoot; }
   void RemoveFromTable();
   virtual nsDisplayItemGeometry* GetGeometry() { return nullptr; }
 protected:
   virtual ~WebRenderUserData();
 
   WebRenderBridgeChild* WrBridge() const;
 
   RefPtr<WebRenderLayerManager> mWRManager;
   nsIFrame* mFrame;
   uint32_t mDisplayItemKey;
   WebRenderUserDataRefTable* mTable;
   bool mUsed;
+  wr::RenderRoot mRenderRoot;
 };
 
 struct WebRenderUserDataKey {
   WebRenderUserDataKey(uint32_t aFrameKey, WebRenderUserData::UserDataType aType)
     : mFrameKey(aFrameKey)
     , mType(aType)
   { }
 
@@ -116,17 +119,18 @@ struct WebRenderUserDataKey {
 
 typedef nsRefPtrHashtable<nsGenericHashKey<mozilla::layers::WebRenderUserDataKey>, WebRenderUserData> WebRenderUserDataTable;
 
 /// Holds some data used to share TextureClient/ImageClient with the parent
 /// process except if used with blob images (watch your step).
 class WebRenderImageData : public WebRenderUserData
 {
 public:
-  WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderImageData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                     wr::RenderRoot aRenderRoot);
   virtual ~WebRenderImageData();
 
   virtual WebRenderImageData* AsImageData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eImage; }
   static UserDataType Type() { return UserDataType::eImage; }
   virtual Maybe<wr::ImageKey> GetImageKey() { return mKey; }
   void SetImageKey(const wr::ImageKey& aKey);
   already_AddRefed<ImageClient> GetImageClient();
@@ -172,17 +176,18 @@ protected:
 /// a texture.
 ///
 /// TODO(nical) It would be much better to separate the two use cases into separate classes and
 /// not have the blob image related code inherit from WebRenderImageData (the current code only
 /// works if we carefully use a subset of the parent code).
 class WebRenderFallbackData : public WebRenderImageData
 {
 public:
-  WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderFallbackData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                        wr::RenderRoot aRenderRoot);
   virtual ~WebRenderFallbackData();
 
   virtual WebRenderFallbackData* AsFallbackData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eFallback; }
   static UserDataType Type() { return UserDataType::eFallback; }
   nsDisplayItemGeometry* GetGeometry() override;
   void SetGeometry(nsAutoPtr<nsDisplayItemGeometry> aGeometry);
   nsRect GetBounds() { return mBounds; }
@@ -207,42 +212,43 @@ protected:
   bool mInvalid;
   gfx::Size mScale;
   std::vector<RefPtr<gfx::ScaledFont>> mFonts;
 };
 
 class WebRenderAnimationData : public WebRenderUserData
 {
 public:
-  WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderAnimationData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                         wr::RenderRoot aRenderRoot);
   virtual ~WebRenderAnimationData();
 
   virtual UserDataType GetType() override { return UserDataType::eAnimation; }
   static UserDataType Type() { return UserDataType::eAnimation; }
   AnimationInfo& GetAnimationInfo() { return mAnimationInfo; }
 
 protected:
   AnimationInfo mAnimationInfo;
 };
 
 class WebRenderCanvasData : public WebRenderUserData
 {
 public:
-  WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem);
+  WebRenderCanvasData(WebRenderLayerManager* aWRManager, nsDisplayItem* aItem,
+                      wr::RenderRoot aRenderRoot);
   virtual ~WebRenderCanvasData();
 
   virtual WebRenderCanvasData* AsCanvasData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eCanvas; }
   static UserDataType Type() { return UserDataType::eCanvas; }
 
   void ClearCanvasRenderer();
   WebRenderCanvasRendererAsync* GetCanvasRenderer();
   WebRenderCanvasRendererAsync* CreateCanvasRenderer();
 protected:
-
   UniquePtr<WebRenderCanvasRendererAsync> mCanvasRenderer;
 };
 
 extern void DestroyWebRenderUserDataTable(WebRenderUserDataTable* aTable);
 
 struct WebRenderUserDataProperty {
   NS_DECLARE_FRAME_PROPERTY_WITH_DTOR(Key, WebRenderUserDataTable, DestroyWebRenderUserDataTable)
 };
--- a/gfx/thebes/gfxFontMissingGlyphs.cpp
+++ b/gfx/thebes/gfxFontMissingGlyphs.cpp
@@ -160,17 +160,19 @@ public:
         mManager->RemoveUserData(&sWRUserDataKey);
     }
 
     layers::WebRenderLayerManager* mManager;
 
     static UserDataKey sWRUserDataKey;
 };
 
-static RefPtr<SourceSurface> gWRGlyphAtlas[8];
+static const int CONTENT_RECT_GLYPH_ATLAS = 8;
+
+static RefPtr<SourceSurface> gWRGlyphAtlas[16];
 static LinkedList<WRUserData> gWRUsers;
 UserDataKey WRUserData::sWRUserDataKey;
 
 /**
  * Generates a transformed WebRender mini-font atlas for a given orientation.
  */
 static already_AddRefed<SourceSurface>
 MakeWRGlyphAtlas(const Matrix* aMat)
@@ -215,51 +217,53 @@ MakeWRGlyphAtlas(const Matrix* aMat)
 static void
 PurgeWRGlyphAtlas()
 {
     // For each WR layer manager, we need go through each atlas orientation
     // and see if it has a stashed image key. If it does, remove the image
     // from the layer manager.
     for (WRUserData* user : gWRUsers) {
         auto* manager = user->mManager;
-        for (size_t i = 0; i < 8; i++) {
+        for (size_t i = 0; i < 16; i++) {
             if (gWRGlyphAtlas[i]) {
                 uint32_t handle =
                     (uint32_t)(uintptr_t)gWRGlyphAtlas[i]->GetUserData(
                         reinterpret_cast<UserDataKey*>(manager));
                 if (handle) {
+                    // TODO wr::RenderRoot::Unknown
                     manager->AddImageKeyForDiscard(
-                        wr::ImageKey{manager->WrBridge()->GetNamespace(), handle});
+                        wr::ImageKey{manager->WrBridge()->GetNamespace(), handle},
+                        (i & CONTENT_RECT_GLYPH_ATLAS) ? wr::RenderRoot::Content : wr::RenderRoot::Default);
                 }
             }
         }
     }
     // Remove the layer managers' destroy notifications only after processing
     // so as not to mess up gWRUsers iteration.
     while (!gWRUsers.isEmpty()) {
         gWRUsers.popFirst()->Remove();
     }
     // Finally, clear out the atlases.
-    for (size_t i = 0; i < 8; i++) {
+    for (size_t i = 0; i < 16; i++) {
         gWRGlyphAtlas[i] = nullptr;
     }
 }
 
 WRUserData::WRUserData(layers::WebRenderLayerManager* aManager)
     : mManager(aManager)
 {
     gWRUsers.insertFront(this);
 }
 
 WRUserData::~WRUserData()
 {
     // When the layer manager is destroyed, we need go through each
     // atlas and remove any assigned image keys.
     if (isInList()) {
-        for (size_t i = 0; i < 8; i++) {
+        for (size_t i = 0; i < 16; i++) {
             if (gWRGlyphAtlas[i]) {
                 gWRGlyphAtlas[i]->RemoveUserData(reinterpret_cast<UserDataKey*>(mManager));
             }
         }
     }
 }
 
 static already_AddRefed<SourceSurface>
@@ -269,25 +273,30 @@ GetWRGlyphAtlas(DrawTarget& aDrawTarget,
     // Encode orientation in the key.
     if (aMat) {
         if (aMat->_11 == 0) {
             key |= 4 | (aMat->_12 < 0 ? 1 : 0) | (aMat->_21 < 0 ? 2 : 0);
         } else {
             key |= (aMat->_11 < 0 ? 1 : 0) | (aMat->_22 < 0 ? 2 : 0);
         }
     }
+    // The atlas may exist, but an image key may not be assigned for it to
+    // the given layer manager.
+    auto* tdt = static_cast<layout::TextDrawTarget*>(&aDrawTarget);
+    if (tdt->GetRenderRoot() == wr::RenderRoot::Content) {
+        key |= CONTENT_RECT_GLYPH_ATLAS;
+    }
+
     // Check if an atlas was already created, or create one if necessary.
     RefPtr<SourceSurface> atlas = gWRGlyphAtlas[key];
     if (!atlas) {
         atlas = MakeWRGlyphAtlas(aMat);
         gWRGlyphAtlas[key] = atlas;
     }
-    // The atlas may exist, but an image key may not be assigned for it to
-    // the given layer manager.
-    auto* tdt = static_cast<layout::TextDrawTarget*>(&aDrawTarget);
+
     auto* manager = tdt->WrLayerManager();
     if (!atlas->GetUserData(reinterpret_cast<UserDataKey*>(manager))) {
         // No image key, so we need to map the atlas' data for transfer to WR.
         RefPtr<DataSourceSurface> dataSurface = atlas->GetDataSurface();
         if (!dataSurface) {
             return nullptr;
         }
         DataSourceSurface::ScopedMap map(dataSurface, DataSourceSurface::READ);
--- a/gfx/thebes/gfxPrefs.h
+++ b/gfx/thebes/gfxPrefs.h
@@ -540,16 +540,17 @@ private:
   DECL_GFX_PREF(Live, "gfx.webrender.blob-images",             WebRenderBlobImages, bool, true);
   DECL_GFX_PREF(Live, "gfx.webrender.blob.invalidation",       WebRenderBlobInvalidation, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.blob.paint-flashing",     WebRenderBlobPaintFlashing, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.dl.dump-parent",          WebRenderDLDumpParent, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.dl.dump-content",         WebRenderDLDumpContent, bool, false);
   DECL_GFX_PREF(Once, "gfx.webrender.enabled",                 WebRenderEnabledDoNotUseDirectly, bool, false);
   DECL_GFX_PREF(Once, "gfx.webrender.force-disabled",          WebRenderForceDisabled, bool, false);
   DECL_GFX_PREF(Live, "gfx.webrender.highlight-painted-layers",WebRenderHighlightPaintedLayers, bool, false);
+  DECL_GFX_PREF(Once, "gfx.webrender.split-render-roots",      WebRenderSplitRenderRoots, bool, false);
 
   // Use vsync events generated by hardware
   DECL_GFX_PREF(Once, "gfx.work-around-driver-bugs",           WorkAroundDriverBugs, bool, true);
 
   DECL_GFX_PREF(Live, "gl.ignore-dx-interop2-blacklist",       IgnoreDXInterop2Blacklist, bool, false);
   DECL_GFX_PREF(Live, "gl.msaa-level",                         MSAALevel, uint32_t, 2);
 #if defined(XP_MACOSX)
   DECL_GFX_PREF(Live, "gl.multithreaded",                      GLMultithreaded, bool, false);
--- a/gfx/webrender_bindings/RenderThread.cpp
+++ b/gfx/webrender_bindings/RenderThread.cpp
@@ -503,26 +503,28 @@ RenderThread::SetDestroyed(wr::WindowId 
   if (it == mWindowInfos.end()) {
     MOZ_ASSERT(false);
     return;
   }
   it->second->mIsDestroyed = true;
 }
 
 void
-RenderThread::IncPendingFrameCount(wr::WindowId aWindowId, const TimeStamp& aStartTime)
+RenderThread::IncPendingFrameCount(wr::WindowId aWindowId, const TimeStamp& aStartTime,
+                                   uint8_t aDocFrameCount)
 {
   MutexAutoLock lock(mFrameCountMapLock);
   auto it = mWindowInfos.find(AsUint64(aWindowId));
   if (it == mWindowInfos.end()) {
     MOZ_ASSERT(false);
     return;
   }
   it->second->mPendingCount++;
   it->second->mStartTimes.push(aStartTime);
+  it->second->mDocFrameCounts.push(aDocFrameCount);
 }
 
 void
 RenderThread::DecPendingFrameCount(wr::WindowId aWindowId)
 {
   MutexAutoLock lock(mFrameCountMapLock);
   auto it = mWindowInfos.find(AsUint64(aWindowId));
   if (it == mWindowInfos.end()) {
@@ -540,26 +542,39 @@ RenderThread::DecPendingFrameCount(wr::W
   // in CompositorBridgeParent::ComposeToTarget also counts such frames. And
   // anyway this should be relatively infrequent so it shouldn't skew the
   // numbers much.
   mozilla::Telemetry::AccumulateTimeDelta(mozilla::Telemetry::COMPOSITE_TIME,
                                           info->mStartTimes.front());
   info->mStartTimes.pop();
 }
 
-void
-RenderThread::IncRenderingFrameCount(wr::WindowId aWindowId)
+mozilla::Pair<bool, bool>
+RenderThread::IncRenderingFrameCount(wr::WindowId aWindowId, bool aRender)
 {
   MutexAutoLock lock(mFrameCountMapLock);
   auto it = mWindowInfos.find(AsUint64(aWindowId));
   if (it == mWindowInfos.end()) {
     MOZ_ASSERT(false);
-    return;
+    return MakePair(false, false);
   }
-  it->second->mRenderingCount++;
+
+  it->second->mDocFramesSeen++;
+  if (it->second->mDocFramesSeen < it->second->mDocFrameCounts.front()) {
+    it->second->mRender |= aRender;
+    return MakePair(false, it->second->mRender);
+  } else {
+    MOZ_ASSERT(it->second->mDocFramesSeen == it->second->mDocFrameCounts.front());
+    bool render = it->second->mRender || aRender;
+    it->second->mRender = false;
+    it->second->mRenderingCount++;
+    it->second->mDocFrameCounts.pop();
+    it->second->mDocFramesSeen = 0;
+    return MakePair(true, render);
+  }
 }
 
 void
 RenderThread::FrameRenderingComplete(wr::WindowId aWindowId)
 {
   MutexAutoLock lock(mFrameCountMapLock);
   auto it = mWindowInfos.find(AsUint64(aWindowId));
   if (it == mWindowInfos.end()) {
@@ -902,18 +917,20 @@ CreateGLContext()
   // with ANGLE.
   return nullptr;
 }
 
 extern "C" {
 
 static void HandleFrame(mozilla::wr::WrWindowId aWindowId, bool aRender)
 {
-  mozilla::wr::RenderThread::Get()->IncRenderingFrameCount(aWindowId);
-  mozilla::wr::RenderThread::Get()->HandleFrame(aWindowId, aRender);
+  auto incResult = mozilla::wr::RenderThread::Get()->IncRenderingFrameCount(aWindowId, aRender);
+  if (incResult.first()) {
+    mozilla::wr::RenderThread::Get()->HandleFrame(aWindowId, incResult.second());
+  }
 }
 
 void wr_notifier_wake_up(mozilla::wr::WrWindowId aWindowId)
 {
   mozilla::wr::RenderThread::Get()->WakeUp(aWindowId);
 }
 
 void wr_notifier_new_frame_ready(mozilla::wr::WrWindowId aWindowId)
@@ -929,18 +946,18 @@ void wr_notifier_nop_frame_done(mozilla:
 void wr_notifier_external_event(mozilla::wr::WrWindowId aWindowId, size_t aRawEvent)
 {
   mozilla::UniquePtr<mozilla::wr::RendererEvent> evt(
     reinterpret_cast<mozilla::wr::RendererEvent*>(aRawEvent));
   mozilla::wr::RenderThread::Get()->RunEvent(mozilla::wr::WindowId(aWindowId),
                                              std::move(evt));
 }
 
-void wr_schedule_render(mozilla::wr::WrWindowId aWindowId)
+void wr_schedule_render(mozilla::wr::WrWindowId aWindowId, mozilla::wr::WrDocumentId aDocumentId)
 {
   RefPtr<mozilla::layers::CompositorBridgeParent> cbp =
       mozilla::layers::CompositorBridgeParent::GetCompositorBridgeParentFromWindowId(aWindowId);
   if (cbp) {
-    cbp->ScheduleRenderOnCompositorThread();
+    cbp->ScheduleRenderOnCompositorThread(aDocumentId.mHandle);
   }
 }
 
 } // extern C
--- a/gfx/webrender_bindings/RenderThread.h
+++ b/gfx/webrender_bindings/RenderThread.h
@@ -192,21 +192,21 @@ public:
 
   /// Can be called from any thread.
   bool IsDestroyed(wr::WindowId aWindowId);
   /// Can be called from any thread.
   void SetDestroyed(wr::WindowId aWindowId);
   /// Can be called from any thread.
   bool TooManyPendingFrames(wr::WindowId aWindowId);
   /// Can be called from any thread.
-  void IncPendingFrameCount(wr::WindowId aWindowId, const TimeStamp& aStartTime);
+  void IncPendingFrameCount(wr::WindowId aWindowId, const TimeStamp& aStartTime, uint8_t aDocFrameCount);
   /// Can be called from any thread.
   void DecPendingFrameCount(wr::WindowId aWindowId);
   /// Can be called from any thread.
-  void IncRenderingFrameCount(wr::WindowId aWindowId);
+  mozilla::Pair<bool, bool> IncRenderingFrameCount(wr::WindowId aWindowId, bool aRender);
   /// Can be called from any thread.
   void FrameRenderingComplete(wr::WindowId aWindowId);
 
   void NotifySlowFrame(wr::WindowId aWindowId);
 
   /// Can be called from any thread.
   WebRenderThreadPool& ThreadPool() { return mThreadPool; }
 
@@ -251,21 +251,25 @@ private:
   // An optional shared GLContext to be used for all
   // windows.
   RefPtr<gl::GLContext> mSharedGL;
 
   std::map<wr::WindowId, UniquePtr<RendererOGL>> mRenderers;
 
   struct WindowInfo {
     bool mIsDestroyed = false;
+    bool mSeenFrame = false;
+    bool mRender = false;
     int64_t mPendingCount = 0;
     int64_t mRenderingCount = 0;
+    uint8_t mDocFramesSeen = 0;
     // One entry in this queue for each pending frame, so the length
     // should always equal mPendingCount
     std::queue<TimeStamp> mStartTimes;
+    std::queue<uint8_t> mDocFrameCounts;
     bool mHadSlowFrame = false;
   };
 
   Mutex mFrameCountMapLock;
   std::unordered_map<uint64_t, WindowInfo*> mWindowInfos;
 
   Mutex mRenderTextureMapLock;
   std::unordered_map<uint64_t, RefPtr<RenderTextureHost>> mRenderTextures;
--- a/gfx/webrender_bindings/WebRenderAPI.cpp
+++ b/gfx/webrender_bindings/WebRenderAPI.cpp
@@ -362,16 +362,29 @@ WebRenderAPI::CreateDocument(LayoutDevic
   return api.forget();
 }
 
 wr::WrIdNamespace
 WebRenderAPI::GetNamespace() {
   return wr_api_get_namespace(mDocHandle);
 }
 
+WebRenderAPI::WebRenderAPI(wr::DocumentHandle* aHandle, wr::WindowId aId, uint32_t aMaxTextureSize, bool aUseANGLE, bool aUseDComp, bool aUseTripleBuffering, layers::SyncHandle aSyncHandle)
+  : mDocHandle(aHandle)
+  , mId(aId)
+  , mMaxTextureSize(aMaxTextureSize)
+  , mUseANGLE(aUseANGLE)
+  , mUseDComp(aUseDComp)
+  , mUseTripleBuffering(aUseTripleBuffering)
+  , mSyncHandle(aSyncHandle)
+  , mDebugFlags({ 0 })
+{
+  mDocumentId = wr_api_get_document_id(aHandle);
+}
+
 WebRenderAPI::~WebRenderAPI()
 {
   if (!mRootDocumentApi) {
     wr_api_delete_document(mDocHandle);
   }
 
   if (!mRootApi) {
     RenderThread::Get()->SetDestroyed(GetId());
@@ -827,48 +840,85 @@ void
 WebRenderAPI::RunOnRenderThread(UniquePtr<RendererEvent> aEvent)
 {
   auto event = reinterpret_cast<uintptr_t>(aEvent.release());
   wr_api_send_external_event(mDocHandle, event);
 }
 
 DisplayListBuilder::DisplayListBuilder(PipelineId aId,
                                        const wr::LayoutSize& aContentSize,
-                                       size_t aCapacity)
+                                       size_t aCapacity,
+                                       RenderRoot aRenderRoot)
   : mActiveFixedPosTracker(nullptr)
+  , mSubBuilder(nullptr)
+  , mPipelineId(aId)
+  , mContentSize(aContentSize)
+  , mRenderRoot(aRenderRoot)
+  , mSendSubBuilderDisplayList(false)
 {
   MOZ_COUNT_CTOR(DisplayListBuilder);
   mWrState = wr_state_new(aId, aContentSize, aCapacity);
 }
 
 DisplayListBuilder::~DisplayListBuilder()
 {
   MOZ_COUNT_DTOR(DisplayListBuilder);
   wr_state_delete(mWrState);
 }
 
 void DisplayListBuilder::Save() { wr_dp_save(mWrState); }
 void DisplayListBuilder::Restore() { wr_dp_restore(mWrState); }
 void DisplayListBuilder::ClearSave() { wr_dp_clear_save(mWrState); }
 
+DisplayListBuilder& DisplayListBuilder::CreateSubBuilder(const wr::LayoutSize& aContentSize,
+                                                         size_t aCapacity)
+{
+  MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default);
+  MOZ_ASSERT(!mSubBuilder);
+  mSubBuilder = MakeUnique<DisplayListBuilder>(mPipelineId,
+                                               aContentSize,
+                                               aCapacity,
+                                               wr::RenderRoot::Content);
+  return *mSubBuilder;
+}
+
+DisplayListBuilder& DisplayListBuilder::SubBuilder()
+{
+  return *mSubBuilder;
+}
+
+bool DisplayListBuilder::HasSubBuilder()
+{
+  return !!mSubBuilder;
+}
+
 usize DisplayListBuilder::Dump(usize aIndent,
                                const Maybe<usize>& aStart,
                                const Maybe<usize>& aEnd)
 {
   return wr_dump_display_list(mWrState, aIndent, aStart.ptrOr(nullptr), aEnd.ptrOr(nullptr));
 }
 
 void
 DisplayListBuilder::Finalize(wr::LayoutSize& aOutContentSize,
-                             BuiltDisplayList& aOutDisplayList)
+                             BuiltDisplayList& aOutDisplayList,
+                             RenderRoot aRenderRoot)
 {
-  wr_api_finalize_builder(mWrState,
-                          &aOutContentSize,
-                          &aOutDisplayList.dl_desc,
-                          &aOutDisplayList.dl.inner);
+  if (aRenderRoot == mRenderRoot) {
+    wr_api_finalize_builder(mWrState,
+                            &aOutContentSize,
+                            &aOutDisplayList.dl_desc,
+                            &aOutDisplayList.dl.inner);
+  } else {
+    MOZ_ASSERT(mSubBuilder && mSubBuilder->mRenderRoot == aRenderRoot);
+    wr_api_finalize_builder(mSubBuilder->mWrState,
+                            &aOutContentSize,
+                            &aOutDisplayList.dl_desc,
+                            &aOutDisplayList.dl.inner);
+  }
 }
 
 Maybe<wr::WrClipId>
 DisplayListBuilder::PushStackingContext(const wr::LayoutRect& aBounds,
                                         const wr::WrClipId* aClipNodeId,
                                         const WrAnimationProperty* aAnimation,
                                         const float* aOpacity,
                                         const gfx::Matrix4x4* aTransform,
--- a/gfx/webrender_bindings/WebRenderAPI.h
+++ b/gfx/webrender_bindings/WebRenderAPI.h
@@ -235,50 +235,46 @@ public:
 
   void WakeSceneBuilder();
   void FlushSceneBuilder();
 
   void NotifyMemoryPressure();
   void AccumulateMemoryReport(wr::MemoryReport*);
 
   wr::WrIdNamespace GetNamespace();
+  wr::WrDocumentId GetDocumentId() const
+  {
+    return mDocumentId;
+  }
   uint32_t GetMaxTextureSize() const { return mMaxTextureSize; }
   bool GetUseANGLE() const { return mUseANGLE; }
   bool GetUseDComp() const { return mUseDComp; }
   bool GetUseTripleBuffering() const { return mUseTripleBuffering; }
   layers::SyncHandle GetSyncHandle() const { return mSyncHandle; }
 
   void Capture();
 
 protected:
-  WebRenderAPI(wr::DocumentHandle* aHandle, wr::WindowId aId, int32_t aMaxTextureSize, bool aUseANGLE, bool aUseDComp, bool aUseTripleBuffering, layers::SyncHandle aSyncHandle)
-    : mDocHandle(aHandle)
-    , mId(aId)
-    , mMaxTextureSize(aMaxTextureSize)
-    , mUseANGLE(aUseANGLE)
-    , mUseDComp(aUseDComp)
-    , mUseTripleBuffering(aUseTripleBuffering)
-    , mSyncHandle(aSyncHandle)
-    , mDebugFlags({ 0 })
-  {}
+  WebRenderAPI(wr::DocumentHandle* aHandle, wr::WindowId aId, uint32_t aMaxTextureSize, bool aUseANGLE, bool aUseDComp, bool aUseTripleBuffering, layers::SyncHandle aSyncHandle);
 
   ~WebRenderAPI();
   // Should be used only for shutdown handling
   void WaitFlushed();
 
   void UpdateDebugFlags(uint32_t aFlags);
 
   wr::DocumentHandle* mDocHandle;
   wr::WindowId mId;
   int32_t mMaxTextureSize;
   bool mUseANGLE;
   bool mUseDComp;
   bool mUseTripleBuffering;
   layers::SyncHandle mSyncHandle;
   wr::DebugFlags mDebugFlags;
+  wr::WrDocumentId mDocumentId;
 
   // We maintain alive the root api to know when to shut the render backend down,
   // and the root api for the document to know when to delete the document.
   // mRootApi is null for the api object that owns the channel (and is responsible
   // for shutting it down), and mRootDocumentApi is null for the api object owning
   // (and responsible for destroying) a given document.
   // All api objects in the same window use the same channel, and some api objects
   // write to the same document (but there is only one owner for each channel and
@@ -316,28 +312,46 @@ private:
 
 /// This is a simple C++ wrapper around WrState defined in the rust bindings.
 /// We may want to turn this into a direct wrapper on top of WebRenderFrameBuilder
 /// instead, so the interface may change a bit.
 class DisplayListBuilder {
 public:
   explicit DisplayListBuilder(wr::PipelineId aId,
                               const wr::LayoutSize& aContentSize,
-                              size_t aCapacity = 0);
+                              size_t aCapacity = 0,
+                              RenderRoot aRenderRoot = RenderRoot::Default);
   DisplayListBuilder(DisplayListBuilder&&) = default;
 
   ~DisplayListBuilder();
 
   void Save();
   void Restore();
   void ClearSave();
   usize Dump(usize aIndent, const Maybe<usize>& aStart, const Maybe<usize>& aEnd);
 
   void Finalize(wr::LayoutSize& aOutContentSize,
-                wr::BuiltDisplayList& aOutDisplayList);
+                wr::BuiltDisplayList& aOutDisplayList,
+                RenderRoot aRenderRoot = RenderRoot::Default);
+
+  RenderRoot GetRenderRoot() { return mRenderRoot; }
+  bool HasSubBuilder();
+  DisplayListBuilder& CreateSubBuilder(const wr::LayoutSize& aContentSize,
+                                       size_t aCapacity);
+  DisplayListBuilder& SubBuilder();
+
+  bool GetSendSubBuilderDisplayList()
+  {
+    return mSendSubBuilderDisplayList;
+  }
+
+  void SetSendContentRectDisplayList()
+  {
+    mSendSubBuilderDisplayList = true;
+  }
 
   Maybe<wr::WrClipId> PushStackingContext(
           const wr::LayoutRect& aBounds, // TODO: We should work with strongly typed rects
           const wr::WrClipId* aClipNodeId,
           const wr::WrAnimationProperty* aAnimation,
           const float* aOpacity,
           const gfx::Matrix4x4* aTransform,
           wr::TransformStyle aTransformStyle,
@@ -593,16 +607,23 @@ protected:
   // there is no clip rect to merge with.
   Maybe<wr::LayoutRect> mClipChainLeaf;
 
   RefPtr<layout::TextDrawTarget> mCachedTextDT;
   RefPtr<gfxContext> mCachedContext;
 
   FixedPosScrollTargetTracker* mActiveFixedPosTracker;
 
+  UniquePtr<DisplayListBuilder> mSubBuilder;
+  wr::PipelineId mPipelineId;
+  wr::LayoutSize mContentSize;
+
+  RenderRoot mRenderRoot;
+  bool mSendSubBuilderDisplayList;
+
   friend class WebRenderAPI;
 };
 
 Maybe<wr::ImageFormat>
 SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat);
 
 } // namespace wr
 } // namespace mozilla
--- a/gfx/webrender_bindings/WebRenderTypes.h
+++ b/gfx/webrender_bindings/WebRenderTypes.h
@@ -54,16 +54,31 @@ struct ExternalImageKeyPair
 {
   ImageKey key;
   ExternalImageId id;
 };
 
 /* Generate a brand new window id and return it. */
 WindowId NewWindowId();
 
+enum class RenderRoot : uint8_t {
+  Default = 0,
+
+  // Everything below the chrome - event if it is not properly "content"
+  // (e.g. devtools, sidebars, statuspanel, etc.)
+  Content,
+
+  Count,
+};
+
+// Sometimes it's unknown where something comes from. Usually this is
+// a resource update. In these cases we have to generate frames for
+// all documents, since they might need to update their caches.
+const RenderRoot kRenderRootUnknown = RenderRoot::Count;
+
 inline DebugFlags NewDebugFlags(uint32_t aFlags) {
   DebugFlags flags;
   flags.mBits = aFlags;
   return flags;
 }
 
 inline Maybe<wr::ImageFormat>
 SurfaceFormatToImageFormat(gfx::SurfaceFormat aFormat) {
--- a/gfx/webrender_bindings/src/bindings.rs
+++ b/gfx/webrender_bindings/src/bindings.rs
@@ -475,16 +475,29 @@ pub struct WrOpacityProperty {
 
 /// cbindgen:field-names=[mHandle]
 /// cbindgen:derive-lt=true
 /// cbindgen:derive-lte=true
 #[repr(C)]
 #[derive(Copy, Clone, Debug, PartialEq, Eq)]
 pub struct WrWindowId(u64);
 
+/// cbindgen:field-names=[mHandle]
+/// cbindgen:derive-lt=true
+/// cbindgen:derive-lte=true
+#[repr(C)]
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub struct WrDocumentId(u64);
+
+impl WrDocumentId {
+    pub fn from_document_id(document_id: DocumentId) -> WrDocumentId {
+        WrDocumentId(((document_id.0).0 as u64) << 32 | (document_id.1 as u64))
+    }
+}
+
 fn get_proc_address(glcontext_ptr: *mut c_void,
                     name: &str)
                     -> *const c_void {
 
     extern "C" {
         fn get_proc_address_from_glcontext(glcontext_ptr: *mut c_void,
                                            procname: *const c_char)
                                            -> *const c_void;
@@ -537,17 +550,17 @@ struct CppNotifier {
 unsafe impl Send for CppNotifier {}
 
 extern "C" {
     fn wr_notifier_wake_up(window_id: WrWindowId);
     fn wr_notifier_new_frame_ready(window_id: WrWindowId);
     fn wr_notifier_nop_frame_done(window_id: WrWindowId);
     fn wr_notifier_external_event(window_id: WrWindowId,
                                   raw_event: usize);
-    fn wr_schedule_render(window_id: WrWindowId);
+    fn wr_schedule_render(window_id: WrWindowId, document_id: WrDocumentId);
 
     fn wr_transaction_notification_notified(handler: usize, when: Checkpoint);
 }
 
 impl RenderNotifier for CppNotifier {
     fn clone(&self) -> Box<RenderNotifier> {
         Box::new(CppNotifier {
             window_id: self.window_id,
@@ -782,32 +795,32 @@ impl SceneBuilderHooks for APZCallbacks 
 
     fn pre_scene_swap(&self, scenebuild_time: u64) {
         unsafe {
             record_telemetry_time(TelemetryProbe::SceneBuildTime, scenebuild_time);
             apz_pre_scene_swap(self.window_id);
         }
     }
 
-    fn post_scene_swap(&self, info: PipelineInfo, sceneswap_time: u64) {
+    fn post_scene_swap(&self, document_id: DocumentId, info: PipelineInfo, sceneswap_time: u64) {
         let info = WrPipelineInfo::new(info);
         unsafe {
             record_telemetry_time(TelemetryProbe::SceneSwapTime, sceneswap_time);
             apz_post_scene_swap(self.window_id, info);
         }
 
         // After a scene swap we should schedule a render for the next vsync,
         // otherwise there's no guarantee that the new scene will get rendered
         // anytime soon
-        unsafe { wr_schedule_render(self.window_id) }
+        unsafe { wr_schedule_render(self.window_id, WrDocumentId::from_document_id(document_id)) }
         unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); }
     }
 
-    fn post_resource_update(&self) {
-        unsafe { wr_schedule_render(self.window_id) }
+    fn post_resource_update(&self, document_id: DocumentId) {
+        unsafe { wr_schedule_render(self.window_id, WrDocumentId::from_document_id(document_id)) }
         unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); }
     }
 
     fn post_empty_scene_build(&self) {
         unsafe { gecko_profiler_end_marker(b"SceneBuilding\0".as_ptr() as *const c_char); }
     }
 
     fn poke(&self) {
@@ -1101,16 +1114,21 @@ pub extern "C" fn wr_window_new(window_i
     *out_handle = Box::into_raw(Box::new(
             DocumentHandle::new(sender.create_api_by_client(next_namespace_id()), window_size, layer)));
     *out_renderer = Box::into_raw(Box::new(renderer));
 
     return true;
 }
 
 #[no_mangle]
+pub extern "C" fn wr_api_get_document_id(dh: &mut DocumentHandle) -> WrDocumentId {
+    WrDocumentId::from_document_id(dh.document_id)
+}
+
+#[no_mangle]
 pub extern "C" fn wr_api_create_document(
     root_dh: &mut DocumentHandle,
     out_handle: &mut *mut DocumentHandle,
     doc_size: DeviceIntSize,
     layer: i8,
 ) {
     assert!(unsafe { is_in_compositor_thread() });
 
@@ -1304,17 +1322,18 @@ pub extern "C" fn wr_transaction_set_win
     txn.set_window_parameters(
         *window_size,
         *doc_rect,
         1.0,
     );
 }
 
 #[no_mangle]
-pub extern "C" fn wr_transaction_generate_frame(txn: &mut Transaction) {
+pub extern "C" fn wr_transaction_generate_frame(
+    txn: &mut Transaction) {
     txn.generate_frame();
 }
 
 #[no_mangle]
 pub extern "C" fn wr_transaction_invalidate_rendered_frame(txn: &mut Transaction) {
     txn.invalidate_rendered_frame();
 }
 
--- a/gfx/webrender_bindings/webrender_ffi_generated.h
+++ b/gfx/webrender_bindings/webrender_ffi_generated.h
@@ -452,16 +452,30 @@ struct WrWindowId {
   bool operator<(const WrWindowId& aOther) const {
     return mHandle < aOther.mHandle;
   }
   bool operator<=(const WrWindowId& aOther) const {
     return mHandle <= aOther.mHandle;
   }
 };
 
+struct WrDocumentId {
+  uint64_t mHandle;
+
+  bool operator==(const WrDocumentId& aOther) const {
+    return mHandle == aOther.mHandle;
+  }
+  bool operator<(const WrDocumentId& aOther) const {
+    return mHandle < aOther.mHandle;
+  }
+  bool operator<=(const WrDocumentId& aOther) const {
+    return mHandle <= aOther.mHandle;
+  }
+};
+
 // This type carries no valuable semantics for WR. However, it reflects the fact that
 // clients (Servo) may generate pipelines by different semi-independent sources.
 // These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
 // Having this extra Id field enables them to generate `PipelineId` without collision.
 using PipelineSourceId = uint32_t;
 
 // From the point of view of WR, `PipelineId` is completely opaque and generic as long as
 // it's clonable, serializable, comparable, and hashable.
@@ -1226,16 +1240,20 @@ void wr_api_clear_all_caches(DocumentHan
 WR_DESTRUCTOR_SAFE_FUNC;
 
 WR_INLINE
 void wr_api_clone(DocumentHandle *aDh,
                   DocumentHandle **aOutHandle)
 WR_FUNC;
 
 WR_INLINE
+WrDocumentId wr_api_get_document_id(DocumentHandle *aDh)
+WR_FUNC;
+
+WR_INLINE
 void wr_api_create_document(DocumentHandle *aRootDh,
                             DocumentHandle **aOutHandle,
                             DeviceIntSize aDocSize,
                             int8_t aLayer)
 WR_FUNC;
 
 WR_INLINE
 void wr_api_delete(DocumentHandle *aDh)
@@ -1816,17 +1834,17 @@ void wr_resource_updates_update_image(Tr
                                       const WrImageDescriptor *aDescriptor,
                                       WrVecU8 *aBytes)
 WR_FUNC;
 
 WR_INLINE
 uintptr_t wr_root_scroll_node_id()
 WR_FUNC;
 
-extern void wr_schedule_render(WrWindowId aWindowId);
+extern void wr_schedule_render(WrWindowId aWindowId, WrDocumentId aDocumentId);
 
 WR_INLINE
 void wr_set_item_tag(WrState *aState,
                      uint64_t aScrollId,
                      uint16_t aHitInfo)
 WR_FUNC;
 
 WR_INLINE
--- a/gfx/wr/webrender/src/frame_builder.rs
+++ b/gfx/wr/webrender/src/frame_builder.rs
@@ -1,14 +1,14 @@
 /* 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/. */
 
 use api::{ColorF, DeviceIntPoint, DevicePixelScale, LayoutPixel, PicturePixel, RasterPixel};
-use api::{DeviceIntRect, DeviceIntSize, DocumentLayer, FontRenderMode};
+use api::{DeviceIntRect, DeviceIntSize, DocumentId, DocumentLayer, FontRenderMode};
 use api::{LayoutPoint, LayoutRect, LayoutSize, PipelineId, RasterSpace, WorldPoint, WorldRect, WorldPixel};
 use clip::{ClipDataStore, ClipStore};
 use clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
 use display_list_flattener::{DisplayListFlattener};
 use gpu_cache::GpuCache;
 use gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
 use hit_test::{HitTester, HitTestingRun};
 use internal_types::{FastHashMap, PlaneSplitter};
@@ -315,16 +315,17 @@ impl FrameBuilder {
         Some(render_task_id)
     }
 
     pub fn build(
         &mut self,
         resource_cache: &mut ResourceCache,
         gpu_cache: &mut GpuCache,
         stamp: FrameStamp,
+        document_id: DocumentId,
         clip_scroll_tree: &mut ClipScrollTree,
         pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>,
         device_pixel_scale: DevicePixelScale,
         layer: DocumentLayer,
         pan: WorldPoint,
         texture_cache_profile: &mut TextureCacheProfileCounters,
         gpu_cache_profile: &mut GpuCacheProfileCounters,
         scene_properties: &SceneProperties,
@@ -338,17 +339,17 @@ impl FrameBuilder {
         );
 
         let mut profile_counters = FrameProfileCounters::new();
         profile_counters
             .total_primitives
             .set(self.prim_store.prim_count());
 
         resource_cache.begin_frame(stamp);
-        gpu_cache.begin_frame(stamp.frame_id());
+        gpu_cache.begin_frame(stamp.frame_id(), document_id);
 
         let mut transform_palette = TransformPalette::new();
         clip_scroll_tree.update_tree(
             pan,
             scene_properties,
             Some(&mut transform_palette),
         );
 
--- a/gfx/wr/webrender/src/gpu_cache.rs
+++ b/gfx/wr/webrender/src/gpu_cache.rs
@@ -19,19 +19,20 @@
 //! data is not in the cache, the user provided closure
 //! will be invoked to build the data.
 //!
 //! After ```end_frame``` has occurred, callers can
 //! use the ```get_address``` API to get the allocated
 //! address in the GPU cache of a given resource slot
 //! for this frame.
 
-use api::{PremultipliedColorF, TexelRect};
+use api::{DocumentId, PremultipliedColorF, TexelRect};
 use api::{VoidPtrToSizeFn};
 use euclid::TypedRect;
+use internal_types::{FastHashMap};
 use profiler::GpuCacheProfileCounters;
 use render_backend::FrameId;
 use renderer::MAX_VERTEX_TEXTURE_WIDTH;
 use std::{mem, u16, u32};
 use std::ops::Add;
 use std::os::raw::c_void;
 
 
@@ -323,17 +324,17 @@ struct Texture {
     rows: Vec<Row>,
     // Free lists of available blocks for each supported
     // block size in the texture. These are intrusive
     // linked lists.
     free_lists: FreeBlockLists,
     // Linked list of currently occupied blocks. This
     // makes it faster to iterate blocks looking for
     // candidates to be evicted from the cache.
-    occupied_list_head: Option<BlockIndex>,
+    occupied_list_heads: FastHashMap<DocumentId, BlockIndex>,
     // Pending blocks that have been written this frame
     // and will need to be sent to the GPU.
     pending_blocks: Vec<GpuBlockData>,
     // Pending update commands.
     updates: Vec<GpuCacheUpdate>,
     // Profile stats
     allocated_block_count: usize,
 }
@@ -342,17 +343,17 @@ impl Texture {
     fn new() -> Self {
         Texture {
             height: GPU_CACHE_INITIAL_HEIGHT,
             blocks: Vec::new(),
             rows: Vec::new(),
             free_lists: FreeBlockLists::new(),
             pending_blocks: Vec::new(),
             updates: Vec::new(),
-            occupied_list_head: None,
+            occupied_list_heads: FastHashMap::default(),
             allocated_block_count: 0,
         }
     }
 
     // Reports the CPU heap usage of this Texture struct.
     fn malloc_size_of(&self, op: VoidPtrToSizeFn) -> usize {
         let mut size = 0;
         unsafe {
@@ -367,16 +368,17 @@ impl Texture {
     // Push new data into the cache. The ```pending_block_index``` field represents
     // where the data was pushed into the texture ```pending_blocks``` array.
     // Return the allocated address for this data.
     fn push_data(
         &mut self,
         pending_block_index: Option<usize>,
         block_count: usize,
         frame_id: FrameId,
+        document_id: DocumentId
     ) -> CacheLocation {
         // Find the appropriate free list to use based on the block size.
         let (alloc_size, free_list) = self.free_lists
             .get_actual_block_count_and_free_list(block_count);
 
         // See if we need a new row (if free-list has nothing available)
         if free_list.is_none() {
             if self.rows.len() as i32 == self.height {
@@ -406,19 +408,19 @@ impl Texture {
         // Given the code above, it's now guaranteed that there is a block
         // available in the appropriate free-list. Pull a block from the
         // head of the list.
         let free_block_index = free_list.take().unwrap();
         let block = &mut self.blocks[free_block_index.0 as usize];
         *free_list = block.next;
 
         // Add the block to the occupied linked list.
-        block.next = self.occupied_list_head;
+        block.next = self.occupied_list_heads.get(&document_id).map(|x| *x);
         block.last_access_time = frame_id;
-        self.occupied_list_head = Some(free_block_index);
+        self.occupied_list_heads.insert(document_id, free_block_index);
         self.allocated_block_count += alloc_size;
 
         if let Some(pending_block_index) = pending_block_index {
             // Add this update to the pending list of blocks that need
             // to be updated on the GPU.
             self.updates.push(GpuCacheUpdate::Copy {
                 block_index: pending_block_index,
                 block_count,
@@ -429,21 +431,22 @@ impl Texture {
         CacheLocation {
             block_index: free_block_index,
             epoch: block.epoch,
         }
     }
 
     // Run through the list of occupied cache blocks and evict
     // any old blocks that haven't been referenced for a while.
-    fn evict_old_blocks(&mut self, frame_id: FrameId) {
+    fn evict_old_blocks(&mut self, frame_id: FrameId, document_id: DocumentId) {
+        debug_assert!(document_id != DocumentId::INVALID);
         // Prune any old items from the list to make room.
         // Traverse the occupied linked list and see
         // which items have not been used for a long time.
-        let mut current_block = self.occupied_list_head;
+        let mut current_block = self.occupied_list_heads.get(&document_id).map(|x| *x);
         let mut prev_block: Option<BlockIndex> = None;
 
         while let Some(index) = current_block {
             let (next_block, should_unlink) = {
                 let block = &mut self.blocks[index.0 as usize];
 
                 let next_block = block.next;
                 let mut should_unlink = false;
@@ -475,17 +478,24 @@ impl Texture {
             // If the block was released, we will need to remove it
             // from the occupied linked list.
             if should_unlink {
                 match prev_block {
                     Some(prev_block) => {
                         self.blocks[prev_block.0 as usize].next = next_block;
                     }
                     None => {
-                        self.occupied_list_head = next_block;
+                        match next_block {
+                            Some(next_block) => {
+                                self.occupied_list_heads.insert(document_id, next_block);
+                            }
+                            None => {
+                                self.occupied_list_heads.remove(&document_id);
+                            }
+                        }
                     }
                 }
             } else {
                 prev_block = current_block;
             }
 
             current_block = next_block;
         }
@@ -494,16 +504,17 @@ impl Texture {
 
 
 /// A wrapper object for GPU data requests,
 /// works as a container that can only grow.
 #[must_use]
 pub struct GpuDataRequest<'a> {
     handle: &'a mut GpuCacheHandle,
     frame_id: FrameId,
+    document_id: DocumentId,
     start_index: usize,
     max_block_count: usize,
     texture: &'a mut Texture,
 }
 
 impl<'a> GpuDataRequest<'a> {
     pub fn push<B>(&mut self, block: B)
     where
@@ -517,55 +528,60 @@ impl<'a> GpuDataRequest<'a> {
     }
 }
 
 impl<'a> Drop for GpuDataRequest<'a> {
     fn drop(&mut self) {
         // Push the data to the texture pending updates list.
         let block_count = self.current_used_block_num();
         debug_assert!(block_count <= self.max_block_count);
+        debug_assert!(self.document_id != DocumentId::INVALID);
 
         let location = self.texture
-            .push_data(Some(self.start_index), block_count, self.frame_id);
+            .push_data(Some(self.start_index), block_count, self.frame_id,
+                       self.document_id);
         self.handle.location = Some(location);
     }
 }
 
 
 /// The main LRU cache interface.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct GpuCache {
-    /// Current frame ID.
+    /// Current FrameId.
     frame_id: FrameId,
+    document_id: DocumentId,
     /// CPU-side texture allocator.
     texture: Texture,
     /// Number of blocks requested this frame that don't
     /// need to be re-uploaded.
     saved_block_count: usize,
     /// True if the Renderer expects to receive the metadata
     /// about GPU blocks with on each update.
     in_debug: bool,
 }
 
 impl GpuCache {
     pub fn new() -> Self {
         GpuCache {
             frame_id: FrameId::INVALID,
+            document_id: DocumentId::INVALID,
             texture: Texture::new(),
             saved_block_count: 0,
             in_debug: false,
         }
     }
 
     /// Begin a new frame.
-    pub fn begin_frame(&mut self, frame_id: FrameId) {
+    pub fn begin_frame(&mut self, frame_id: FrameId, document_id: DocumentId) {
         debug_assert!(self.texture.pending_blocks.is_empty());
         self.frame_id = frame_id;
-        self.texture.evict_old_blocks(self.frame_id);
+        self.document_id = document_id;
+        self.texture.evict_old_blocks(self.frame_id, document_id);
         self.saved_block_count = 0;
     }
 
     // Invalidate a (possibly) existing block in the cache.
     // This means the next call to request() for this location
     // will rebuild the data and upload it to the GPU.
     pub fn invalidate(&mut self, handle: &GpuCacheHandle) {
         if let Some(ref location) = handle.location {
@@ -593,63 +609,67 @@ impl GpuCache {
                 }
                 return None;
             }
         }
 
         Some(GpuDataRequest {
             handle,
             frame_id: self.frame_id,
+            document_id: self.document_id,
             start_index: self.texture.pending_blocks.len(),
             texture: &mut self.texture,
             max_block_count,
         })
     }
 
     // Push an array of data blocks to be uploaded to the GPU
     // unconditionally for this frame. The cache handle will
     // assert if the caller tries to retrieve the address
     // of this handle on a subsequent frame. This is typically
     // used for uploading data that changes every frame, and
     // therefore makes no sense to try and cache.
     pub fn push_per_frame_blocks(&mut self, blocks: &[GpuBlockData]) -> GpuCacheHandle {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         let start_index = self.texture.pending_blocks.len();
         self.texture.pending_blocks.extend_from_slice(blocks);
         let location = self.texture
-            .push_data(Some(start_index), blocks.len(), self.frame_id);
+            .push_data(Some(start_index), blocks.len(), self.frame_id, self.document_id);
         GpuCacheHandle {
             location: Some(location),
         }
     }
 
     // Reserve space in the cache for per-frame blocks that
     // will be resolved by the render thread via the
     // external image callback.
     pub fn push_deferred_per_frame_blocks(&mut self, block_count: usize) -> GpuCacheHandle {
-        let location = self.texture.push_data(None, block_count, self.frame_id);
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        let location = self.texture.push_data(None, block_count, self.frame_id, self.document_id);
         GpuCacheHandle {
             location: Some(location),
         }
     }
 
     /// End the frame. Return the list of updates to apply to the
     /// device specific cache texture.
     pub fn end_frame(
-        &self,
+        &mut self,
         profile_counters: &mut GpuCacheProfileCounters,
     ) -> FrameId {
         profile_counters
             .allocated_rows
             .set(self.texture.rows.len());
         profile_counters
             .allocated_blocks
             .set(self.texture.allocated_block_count);
         profile_counters
             .saved_blocks
             .set(self.saved_block_count);
+        self.document_id = DocumentId::INVALID;
         self.frame_id
     }
 
     /// Extract the pending updates from the cache.
     pub fn extract_updates(&mut self) -> GpuCacheUpdateList {
         GpuCacheUpdateList {
             frame_id: self.frame_id,
             height: self.texture.height,
--- a/gfx/wr/webrender/src/render_backend.rs
+++ b/gfx/wr/webrender/src/render_backend.rs
@@ -208,16 +208,18 @@ impl FrameResources {
         FrameResources {
             clip_data_store: ClipDataStore::new(),
             prim_data_store: PrimitiveDataStore::new(),
         }
     }
 }
 
 struct Document {
+    id: DocumentId,
+
     // The latest built scene, usable to build frames.
     // received from the scene builder thread.
     scene: Scene,
 
     // Temporary list of removed pipelines received from the scene builder
     // thread and forwarded to the renderer.
     removed_pipelines: Vec<PipelineId>,
 
@@ -259,21 +261,23 @@ struct Document {
     /// Contains various vecs of data that is used only during frame building,
     /// where we want to recycle the memory each new display list, to avoid constantly
     /// re-allocating and moving memory around.
     scratch: PrimitiveScratchBuffer,
 }
 
 impl Document {
     pub fn new(
+        id: DocumentId,
         window_size: DeviceIntSize,
         layer: DocumentLayer,
         default_device_pixel_ratio: f32,
     ) -> Self {
         Document {
+            id,
             scene: Scene::new(),
             removed_pipelines: Vec::new(),
             view: DocumentView {
                 window_size,
                 inner_rect: DeviceIntRect::new(DeviceIntPoint::zero(), window_size),
                 layer,
                 pan: DeviceIntPoint::zero(),
                 page_zoom_factor: 1.0,
@@ -415,16 +419,17 @@ impl Document {
                 "First frame increment must happen before build_frame()");
 
         let frame = {
             let frame_builder = self.frame_builder.as_mut().unwrap();
             let frame = frame_builder.build(
                 resource_cache,
                 gpu_cache,
                 self.stamp,
+                self.id,
                 &mut self.clip_scroll_tree,
                 &self.scene.pipelines,
                 accumulated_scale_factor,
                 self.view.layer,
                 pan,
                 &mut resource_profile.texture_cache,
                 &mut resource_profile.gpu_cache,
                 &self.dynamic_properties,
@@ -758,17 +763,17 @@ impl RenderBackend {
                             // The document was removed while we were building it, skip it.
                             // TODO: we might want to just ensure that removed documents are
                             // always forwarded to the scene builder thread to avoid this case.
                             if let Some(tx) = result_tx {
                                 tx.send(SceneSwapResult::Aborted).unwrap();
                             }
                             continue;
                         }
-
+                        self.resource_cache.set_document(txn.document_id);
                         self.resource_cache.add_rasterized_blob_images(
                             replace(&mut txn.rasterized_blobs, Vec::new())
                         );
                         if let Some(rasterizer) = txn.blob_rasterizer.take() {
                             self.resource_cache.set_blob_rasterizer(rasterizer);
                         }
 
                         self.update_document(
@@ -778,16 +783,17 @@ impl RenderBackend {
                             replace(&mut txn.frame_ops, Vec::new()),
                             replace(&mut txn.notifications, Vec::new()),
                             txn.render_frame,
                             txn.invalidate_rendered_frame,
                             &mut frame_counter,
                             &mut profile_counters,
                             has_built_scene,
                         );
+                        self.resource_cache.clear_document();
                     },
                     SceneBuilderResult::FlushComplete(tx) => {
                         tx.send(()).ok();
                     }
                     SceneBuilderResult::ExternalEvent(evt) => {
                         self.notifier.external_event(evt);
                     }
                     SceneBuilderResult::ClearNamespace(id) => {
@@ -883,16 +889,17 @@ impl RenderBackend {
                 sender.send(self.next_namespace_id()).unwrap();
             }
             ApiMsg::CloneApiByClient(namespace_id) => {
                 assert!(self.namespace_alloc_by_client);
                 debug_assert!(!self.documents.iter().any(|(did, _doc)| did.0 == namespace_id));
             }
             ApiMsg::AddDocument(document_id, initial_size, layer) => {
                 let document = Document::new(
+                    document_id,
                     initial_size,
                     layer,
                     self.default_device_pixel_ratio,
                 );
                 self.documents.insert(document_id, document);
             }
             ApiMsg::DeleteDocument(document_id) => {
                 self.documents.remove(&document_id);
@@ -1061,16 +1068,17 @@ impl RenderBackend {
             frame_ops: transaction_msg.frame_ops,
             rasterized_blobs: Vec::new(),
             notifications: transaction_msg.notifications,
             set_root_pipeline: None,
             render_frame: transaction_msg.generate_frame,
             invalidate_rendered_frame: transaction_msg.invalidate_rendered_frame,
         });
 
+        self.resource_cache.set_document(txn.document_id);
         self.resource_cache.pre_scene_building_update(
             &mut txn.resource_updates,
             &mut profile_counters.resources,
         );
 
         for scene_msg in transaction_msg.scene_ops.drain(..) {
             let _timer = profile_counters.total_time.timer();
             self.process_scene_msg(
@@ -1100,16 +1108,17 @@ impl RenderBackend {
                 replace(&mut txn.notifications, Vec::new()),
                 txn.render_frame,
                 txn.invalidate_rendered_frame,
                 frame_counter,
                 profile_counters,
                 false
             );
 
+            self.resource_cache.clear_document();
             return;
         }
 
         let doc = self.documents.get_mut(&document_id).unwrap();
 
         if txn.should_build_scene() {
             txn.request_scene_build = Some(SceneRequest {
                 view: doc.view.clone(),
@@ -1119,16 +1128,17 @@ impl RenderBackend {
         }
 
         let tx = if transaction_msg.low_priority {
             &self.low_priority_scene_tx
         } else {
             &self.scene_tx
         };
 
+        self.resource_cache.clear_document();
         tx.send(SceneBuilderRequest::Transaction(txn)).unwrap();
     }
 
     fn update_document(
         &mut self,
         document_id: DocumentId,
         resource_updates: Vec<ResourceUpdate>,
         doc_resource_updates: Option<DocumentResourceUpdates>,
--- a/gfx/wr/webrender/src/renderer.rs
+++ b/gfx/wr/webrender/src/renderer.rs
@@ -2063,17 +2063,25 @@ impl Renderer {
     /// Should be called before `render()`, as texture cache updates are done here.
     pub fn update(&mut self) {
         profile_scope!("update");
         // Pull any pending results and return the most recent.
         while let Ok(msg) = self.result_rx.try_recv() {
             match msg {
                 ResultMsg::PublishPipelineInfo(mut pipeline_info) => {
                     for (pipeline_id, epoch) in pipeline_info.epochs {
-                        self.pipeline_info.epochs.insert(pipeline_id, epoch);
+                        // pipeline_ids can be duplicated, since we can have multiple documents
+                        // with the same pipeline_id. However it's correct to always report the
+                        // latest one, and we can get into a sticky situation if we accidentally
+                        // report an old epoch because that document didn't have any updates for
+                        // the pipeline.
+                        let entry = self.pipeline_info.epochs.entry(pipeline_id).or_insert(Epoch(0));
+                        if *entry < epoch {
+                            *entry = epoch;
+                        }
                     }
                     self.pipeline_info.removed_pipelines.extend(pipeline_info.removed_pipelines.drain(..));
                 }
                 ResultMsg::PublishDocument(
                     document_id,
                     mut doc,
                     texture_update_list,
                     profile_counters,
@@ -4687,21 +4695,21 @@ pub trait SceneBuilderHooks {
     fn register(&self);
     /// This is called before each scene build starts.
     fn pre_scene_build(&self);
     /// This is called before each scene swap occurs.
     fn pre_scene_swap(&self, scenebuild_time: u64);
     /// This is called after each scene swap occurs. The PipelineInfo contains
     /// the updated epochs and pipelines removed in the new scene compared to
     /// the old scene.
-    fn post_scene_swap(&self, info: PipelineInfo, sceneswap_time: u64);
+    fn post_scene_swap(&self, document_id: DocumentId, info: PipelineInfo, sceneswap_time: u64);
     /// This is called after a resource update operation on the scene builder
     /// thread, in the case where resource updates were applied without a scene
     /// build.
-    fn post_resource_update(&self);
+    fn post_resource_update(&self, document_id: DocumentId);
     /// This is called after a scene build completes without any changes being
     /// made. We guarantee that each pre_scene_build call will be matched with
     /// exactly one of post_scene_swap, post_resource_update or
     /// post_empty_scene_build.
     fn post_empty_scene_build(&self);
     /// This is a generic callback which provides an opportunity to run code
     /// on the scene builder thread. This is called as part of the main message
     /// loop of the scene builder thread, but outside of any specific message
--- a/gfx/wr/webrender/src/resource_cache.rs
+++ b/gfx/wr/webrender/src/resource_cache.rs
@@ -1,16 +1,16 @@
 /* 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/. */
 
 use api::{AddFont, BlobImageResources, AsyncBlobImageRasterizer, ResourceUpdate};
 use api::{BlobImageDescriptor, BlobImageHandler, BlobImageRequest, RasterizedBlobImage};
 use api::{ClearCache, ColorF, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
-use api::{DebugFlags, FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
+use api::{DebugFlags, DocumentId, FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
 use api::{ExternalImageData, ExternalImageType, BlobImageResult, BlobImageParams};
 use api::{FontInstanceData, FontInstanceOptions, FontInstancePlatformOptions, FontVariation};
 use api::{GlyphDimensions, IdNamespace};
 use api::{ImageData, ImageDescriptor, ImageKey, ImageRendering, ImageDirtyRect, DirtyRect};
 use api::{BlobImageKey, BlobDirtyRect, MemoryReport, VoidPtrToSizeFn};
 use api::{TileOffset, TileSize, TileRange, BlobImageData, LayoutIntRect, LayoutIntSize};
 use app_units::Au;
 #[cfg(feature = "capture")]
@@ -25,17 +25,17 @@ use glyph_cache::GlyphCache;
 #[cfg(not(feature = "pathfinder"))]
 use glyph_cache::GlyphCacheEntry;
 use glyph_rasterizer::{FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer};
 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
 use gpu_types::UvRectKind;
 use image::{compute_tile_range, for_each_tile_in_range};
 use internal_types::{FastHashMap, FastHashSet, TextureSource, TextureUpdateList};
 use profiler::{ResourceProfileCounters, TextureCacheProfileCounters};
-use render_backend::{FrameId, FrameStamp};
+use render_backend::FrameStamp;
 use render_task::{RenderTaskCache, RenderTaskCacheKey, RenderTaskId};
 use render_task::{RenderTaskCacheEntry, RenderTaskCacheEntryHandle, RenderTaskTree};
 use smallvec::SmallVec;
 use std::collections::hash_map::Entry::{self, Occupied, Vacant};
 use std::collections::hash_map::IterMut;
 use std::{cmp, mem};
 use std::fmt::Debug;
 use std::hash::Hash;
@@ -397,29 +397,48 @@ impl BlobImageResources for Resources {
             }),
             None => None,
         }
     }
 }
 
 pub type GlyphDimensionsCache = FastHashMap<(FontInstance, GlyphIndex), Option<GlyphDimensions>>;
 
+struct DocumentResourceCache {
+    cached_glyphs: GlyphCache,
+    cached_images: ImageCache,
+    cached_render_tasks: RenderTaskCache,
+
+    resources: Resources,
+}
+
+impl DocumentResourceCache {
+    fn new() -> Self {
+        DocumentResourceCache {
+            cached_glyphs: GlyphCache::new(),
+            cached_images: ResourceClassCache::new(),
+            cached_render_tasks: RenderTaskCache::new(),
+
+            resources: Resources::default(),
+        }
+    }
+}
+
 /// High-level container for resources managed by the `RenderBackend`.
 ///
 /// This includes a variety of things, including images, fonts, and glyphs,
 /// which may be stored as memory buffers, GPU textures, or handles to resources
 /// managed by the OS or other parts of WebRender.
 pub struct ResourceCache {
-    cached_glyphs: GlyphCache,
-    cached_images: ImageCache,
-    cached_render_tasks: RenderTaskCache,
+    doc_cache: DocumentResourceCache,
+    doc_caches: FastHashMap<DocumentId, DocumentResourceCache>,
 
-    resources: Resources,
     state: State,
-    current_frame_id: FrameId,
+    now: FrameStamp,
+    document_id: DocumentId,
 
     pub texture_cache: TextureCache,
 
     // TODO(gw): We should expire (parts of) this cache semi-regularly!
     cached_glyph_dimensions: GlyphDimensionsCache,
     glyph_rasterizer: GlyphRasterizer,
 
     // The set of images that aren't present or valid in the texture cache,
@@ -440,24 +459,23 @@ pub struct ResourceCache {
 
 impl ResourceCache {
     pub fn new(
         texture_cache: TextureCache,
         glyph_rasterizer: GlyphRasterizer,
         blob_image_handler: Option<Box<BlobImageHandler>>,
     ) -> Self {
         ResourceCache {
-            cached_glyphs: GlyphCache::new(),
-            cached_images: ResourceClassCache::new(),
-            cached_render_tasks: RenderTaskCache::new(),
-            resources: Resources::default(),
+            doc_caches: FastHashMap::default(),
+            doc_cache: DocumentResourceCache::new(),
             cached_glyph_dimensions: FastHashMap::default(),
             texture_cache,
             state: State::Idle,
-            current_frame_id: FrameId::INVALID,
+            now: FrameStamp::INVALID,
+            document_id: DocumentId::INVALID,
             pending_image_requests: FastHashSet::default(),
             glyph_rasterizer,
             blob_image_handler,
             rasterized_blob_images: FastHashMap::default(),
             blob_image_templates: FastHashMap::default(),
             missing_blob_images: Vec::new(),
             blob_image_rasterizer: None,
         }
@@ -488,17 +506,17 @@ impl ResourceCache {
         &mut self,
         key: RenderTaskCacheKey,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
         user_data: Option<[f32; 3]>,
         is_opaque: bool,
         f: F,
     ) -> RenderTaskCacheEntryHandle where F: FnOnce(&mut RenderTaskTree) -> RenderTaskId {
-        self.cached_render_tasks.request_render_task(
+        self.doc_cache.cached_render_tasks.request_render_task(
             key,
             &mut self.texture_cache,
             gpu_cache,
             render_tasks,
             user_data,
             is_opaque,
             |render_task_tree| Ok(f(render_task_tree))
         ).expect("Failed to request a render task from the resource cache!")
@@ -673,41 +691,44 @@ impl ResourceCache {
                 } else {
                     *image = RasterizedBlob::NonTiled(vec![data]);
                 }
             }
         }
     }
 
     pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         // Push the new font to the font renderer, and also store
         // it locally for glyph metric requests.
         self.glyph_rasterizer.add_font(font_key, template.clone());
-        self.resources.font_templates.insert(font_key, template);
+        self.doc_cache.resources.font_templates.insert(font_key, template);
     }
 
     pub fn delete_font_template(&mut self, font_key: FontKey) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         self.glyph_rasterizer.delete_font(font_key);
-        self.resources.font_templates.remove(&font_key);
-        self.cached_glyphs
+        self.doc_cache.resources.font_templates.remove(&font_key);
+        self.doc_cache.cached_glyphs
             .clear_fonts(|font| font.font_key == font_key);
         if let Some(ref mut r) = self.blob_image_handler {
             r.delete_font(font_key);
         }
     }
 
     pub fn add_font_instance(
         &mut self,
         instance_key: FontInstanceKey,
         font_key: FontKey,
         glyph_size: Au,
         options: Option<FontInstanceOptions>,
         platform_options: Option<FontInstancePlatformOptions>,
         variations: Vec<FontVariation>,
     ) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         let FontInstanceOptions {
             render_mode,
             flags,
             bg_color,
             synthetic_italics,
             ..
         } = options.unwrap_or_default();
         let instance = FontInstance::new(
@@ -716,85 +737,90 @@ impl ResourceCache {
             ColorF::new(0.0, 0.0, 0.0, 1.0),
             bg_color,
             render_mode,
             flags,
             synthetic_italics,
             platform_options,
             variations,
         );
-        self.resources.font_instances
+        self.doc_cache.resources.font_instances
             .write()
             .unwrap()
             .insert(instance_key, instance);
     }
 
     pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
-        self.resources.font_instances
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        self.doc_cache.resources.font_instances
             .write()
             .unwrap()
             .remove(&instance_key);
         if let Some(ref mut r) = self.blob_image_handler {
             r.delete_font_instance(instance_key);
         }
     }
 
     pub fn get_font_instances(&self) -> FontInstanceMap {
-        self.resources.font_instances.clone()
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        self.doc_cache.resources.font_instances.clone()
     }
 
     pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<FontInstance> {
-        let instance_map = self.resources.font_instances.read().unwrap();
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        let instance_map = self.doc_cache.resources.font_instances.read().unwrap();
         instance_map.get(&instance_key).cloned()
     }
 
     pub fn add_image_template(
         &mut self,
         image_key: ImageKey,
         descriptor: ImageDescriptor,
         data: CachedImageData,
         mut tiling: Option<TileSize>,
     ) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         if tiling.is_none() && Self::should_tile(self.max_texture_size(), &descriptor, &data) {
             // We aren't going to be able to upload a texture this big, so tile it, even
             // if tiling was not requested.
             tiling = Some(DEFAULT_TILE_SIZE);
         }
 
         let resource = ImageResource {
             descriptor,
             data,
             tiling,
             viewport_tiles: None,
         };
 
-        self.resources.image_templates.insert(image_key, resource);
+        self.doc_cache.resources.image_templates.insert(image_key, resource);
     }
 
     pub fn update_image_template(
         &mut self,
         image_key: ImageKey,
         descriptor: ImageDescriptor,
         data: CachedImageData,
         dirty_rect: &ImageDirtyRect,
     ) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         let max_texture_size = self.max_texture_size();
-        let image = match self.resources.image_templates.get_mut(image_key) {
+        let image = match self.doc_cache.resources.image_templates.get_mut(image_key) {
             Some(res) => res,
             None => panic!("Attempt to update non-existent image"),
         };
 
         let mut tiling = image.tiling;
         if tiling.is_none() && Self::should_tile(max_texture_size, &descriptor, &data) {
             tiling = Some(DEFAULT_TILE_SIZE);
         }
 
         // Each cache entry stores its own copy of the image's dirty rect. This allows them to be
         // updated independently.
-        match self.cached_images.try_get_mut(&image_key) {
+        match self.doc_cache.cached_images.try_get_mut(&image_key) {
             Some(&mut ImageResult::UntiledAuto(ref mut entry)) => {
                 entry.dirty_rect = entry.dirty_rect.union(dirty_rect);
             }
             Some(&mut ImageResult::Multi(ref mut entries)) => {
                 let tile_size = tiling.unwrap();
                 for (key, entry) in entries.iter_mut() {
                     // We want the dirty rect relative to the tile and not the whole image.
                     let local_dirty_rect = match key.tile {
@@ -871,21 +897,22 @@ impl ResourceCache {
             descriptor: *descriptor,
             tiling,
             dirty_rect: dirty_rect.union(&image.dirty_rect),
             viewport_tiles: image.viewport_tiles,
         };
     }
 
     pub fn delete_image_template(&mut self, image_key: ImageKey) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         // Remove the template.
-        let value = self.resources.image_templates.remove(image_key);
+        let value = self.doc_cache.resources.image_templates.remove(image_key);
 
         // Release the corresponding texture cache entry, if any.
-        if let Some(mut cached) = self.cached_images.remove(&image_key) {
+        if let Some(mut cached) = self.doc_cache.cached_images.remove(&image_key) {
             cached.drop_from_cache(&mut self.texture_cache);
         }
 
         match value {
             Some(image) => if image.data.is_blob() {
                 let blob_key = BlobImageKey(image_key);
                 self.blob_image_handler.as_mut().unwrap().delete(blob_key);
                 self.blob_image_templates.remove(&blob_key);
@@ -898,17 +925,18 @@ impl ResourceCache {
         }
     }
 
     /// Check if an image has changed since it was last requested.
     pub fn is_image_dirty(
         &self,
         image_key: ImageKey,
     ) -> bool {
-        match self.cached_images.try_get(&image_key) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        match self.doc_cache.cached_images.try_get(&image_key) {
             Some(ImageResult::UntiledAuto(ref info)) => {
                 !info.dirty_rect.is_empty()
             }
             Some(ImageResult::Multi(ref entries)) => {
                 for (_, entry) in &entries.resources {
                     if !entry.dirty_rect.is_empty() {
                         return true;
                     }
@@ -924,19 +952,20 @@ impl ResourceCache {
         }
     }
 
     pub fn request_image(
         &mut self,
         request: ImageRequest,
         gpu_cache: &mut GpuCache,
     ) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         debug_assert_eq!(self.state, State::AddResources);
 
-        let template = match self.resources.image_templates.get(request.key) {
+        let template = match self.doc_cache.resources.image_templates.get(request.key) {
             Some(template) => template,
             None => {
                 warn!("ERROR: Trying to render deleted / non-existent key");
                 debug!("key={:?}", request.key);
                 return
             }
         };
 
@@ -947,21 +976,21 @@ impl ResourceCache {
 
         let side_size =
             template.tiling.map_or(cmp::max(template.descriptor.size.width, template.descriptor.size.height),
                                    |tile_size| tile_size as i32);
         if side_size > self.texture_cache.max_texture_size() {
             // The image or tiling size is too big for hardware texture size.
             warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!",
                   template.descriptor.size.width, template.descriptor.size.height, template.tiling.unwrap_or(0));
-            self.cached_images.insert(request.key, ImageResult::Err(ImageCacheError::OverLimitSize));
+            self.doc_cache.cached_images.insert(request.key, ImageResult::Err(ImageCacheError::OverLimitSize));
             return;
         }
 
-        let storage = match self.cached_images.entry(request.key) {
+        let storage = match self.doc_cache.cached_images.entry(request.key) {
             Occupied(e) => {
                 // We might have an existing untiled entry, and need to insert
                 // a second entry. In such cases we need to move the old entry
                 // out first, replacing it with a dummy entry, and then creating
                 // the tiled/multi-entry variant.
                 let entry = e.into_mut();
                 if !request.is_untiled_auto() {
                     let untiled_entry = match entry {
@@ -1066,16 +1095,17 @@ impl ResourceCache {
             }
         }
     }
 
     pub fn create_blob_scene_builder_requests(
         &mut self,
         keys: &[BlobImageKey]
     ) -> (Option<Box<AsyncBlobImageRasterizer>>, Vec<BlobImageParams>) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         if self.blob_image_handler.is_none() {
             return (None, Vec::new());
         }
 
         let mut blob_request_params = Vec::new();
         for key in keys {
             let template = self.blob_image_templates.get_mut(key).unwrap();
 
@@ -1152,17 +1182,17 @@ impl ResourceCache {
                                 tile: Some(tile),
                             },
                             descriptor,
                             dirty_rect: DirtyRect::All,
                         }
                     );
                 });
             } else {
-                let mut needs_upload = match self.cached_images.try_get(&key.as_image()) {
+                let mut needs_upload = match self.doc_cache.cached_images.try_get(&key.as_image()) {
                     Some(&ImageResult::UntiledAuto(ref entry)) => {
                         self.texture_cache.needs_upload(&entry.texture_cache_handle)
                     }
                     _ => true,
                 };
 
                 // If the queue of ratserized updates is growing it probably means that
                 // the texture is not getting uploaded because the display item is off-screen.
@@ -1202,25 +1232,26 @@ impl ResourceCache {
                         },
                         dirty_rect,
                     }
                 );
             }
             template.dirty_rect = DirtyRect::empty();
         }
         let handler = self.blob_image_handler.as_mut().unwrap();
-        handler.prepare_resources(&self.resources, &blob_request_params);
+        handler.prepare_resources(&self.doc_cache.resources, &blob_request_params);
         (Some(handler.create_blob_rasterizer()), blob_request_params)
     }
 
     fn discard_tiles_outside_visible_area(
         &mut self,
         key: BlobImageKey,
         area: &DeviceIntRect
     ) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         let template = match self.blob_image_templates.get(&key) {
             Some(template) => template,
             None => {
                 //println!("Missing image template (key={:?})!", key);
                 return;
             }
         };
         let tile_size = match template.tiling {
@@ -1236,17 +1267,17 @@ impl ResourceCache {
         let tile_range = compute_tile_range(
             &area,
             tile_size,
         );
 
         tiles.retain(|tile, _| { tile_range.contains(tile) });
 
         let texture_cache = &mut self.texture_cache;
-        match self.cached_images.try_get_mut(&key.as_image()) {
+        match self.doc_cache.cached_images.try_get_mut(&key.as_image()) {
             Some(&mut ImageResult::Multi(ref mut entries)) => {
                 entries.retain(|key, entry| {
                     if key.tile.is_none() || tile_range.contains(&key.tile.unwrap()) {
                         return true;
                     }
                     texture_cache.mark_unused(&entry.texture_cache_handle);
                     return false;
                 });
@@ -1259,25 +1290,26 @@ impl ResourceCache {
         &mut self,
         mut font: FontInstance,
         glyph_keys: &[GlyphKey],
         gpu_cache: &mut GpuCache,
         render_task_tree: &mut RenderTaskTree,
         render_passes: &mut SpecialRenderPasses,
     ) {
         debug_assert_eq!(self.state, State::AddResources);
+        debug_assert!(self.document_id != DocumentId::INVALID);
 
         self.glyph_rasterizer.prepare_font(&mut font);
         self.glyph_rasterizer.request_glyphs(
-            &mut self.cached_glyphs,
+            &mut self.doc_cache.cached_glyphs,
             font,
             glyph_keys,
             &mut self.texture_cache,
             gpu_cache,
-            &mut self.cached_render_tasks,
+            &mut self.doc_cache.cached_render_tasks,
             render_task_tree,
             render_passes,
         );
     }
 
     pub fn pending_updates(&mut self) -> TextureUpdateList {
         self.texture_cache.pending_updates()
     }
@@ -1289,30 +1321,31 @@ impl ResourceCache {
         glyph_keys: &[GlyphKey],
         fetch_buffer: &mut Vec<GlyphFetchResult>,
         gpu_cache: &mut GpuCache,
         mut f: F,
     ) where
         F: FnMut(TextureSource, GlyphFormat, &[GlyphFetchResult]),
     {
         debug_assert_eq!(self.state, State::QueryResources);
+        debug_assert!(self.document_id != DocumentId::INVALID);
 
         self.glyph_rasterizer.prepare_font(&mut font);
 
         let mut current_texture_id = TextureSource::Invalid;
         let mut current_glyph_format = GlyphFormat::Subpixel;
         debug_assert!(fetch_buffer.is_empty());
 
         for (loop_index, key) in glyph_keys.iter().enumerate() {
            let (cache_item, glyph_format) =
                 match self.glyph_rasterizer.get_cache_item_for_glyph(key,
                                                                      &font,
-                                                                     &self.cached_glyphs,
+                                                                     &self.doc_cache.cached_glyphs,
                                                                      &self.texture_cache,
-                                                                     &self.cached_render_tasks) {
+                                                                     &self.doc_cache.cached_render_tasks) {
                     None => continue,
                     Some(result) => result,
                 };
             if current_texture_id != cache_item.texture_id ||
                 current_glyph_format != glyph_format {
                 if !fetch_buffer.is_empty() {
                     f(current_texture_id, current_glyph_format, fetch_buffer);
                     fetch_buffer.clear();
@@ -1338,20 +1371,21 @@ impl ResourceCache {
         mut font: FontInstance,
         glyph_keys: &[GlyphKey],
         fetch_buffer: &mut Vec<GlyphFetchResult>,
         gpu_cache: &mut GpuCache,
         mut f: F,
     ) where
         F: FnMut(TextureSource, GlyphFormat, &[GlyphFetchResult]),
     {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         debug_assert_eq!(self.state, State::QueryResources);
 
         self.glyph_rasterizer.prepare_font(&mut font);
-        let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font);
+        let glyph_key_cache = self.doc_cache.cached_glyphs.get_glyph_key_cache_for_font(&font);
 
         let mut current_texture_id = TextureSource::Invalid;
         let mut current_glyph_format = GlyphFormat::Subpixel;
         debug_assert!(fetch_buffer.is_empty());
 
         for (loop_index, key) in glyph_keys.iter().enumerate() {
             let (cache_item, glyph_format) = match *glyph_key_cache.get(key) {
                 GlyphCacheEntry::Cached(ref glyph) => {
@@ -1404,37 +1438,40 @@ impl ResourceCache {
         let image_info = self.get_image_info(request)?;
         Ok(self.get_texture_cache_item(&image_info.texture_cache_handle))
     }
 
     pub fn get_cached_render_task(
         &self,
         handle: &RenderTaskCacheEntryHandle,
     ) -> &RenderTaskCacheEntry {
-        self.cached_render_tasks.get_cache_entry(handle)
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        self.doc_cache.cached_render_tasks.get_cache_entry(handle)
     }
 
     #[inline]
     fn get_image_info(&self, request: ImageRequest) -> Result<&CachedImageInfo, ()> {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         // TODO(Jerry): add a debug option to visualize the corresponding area for
         // the Err() case of CacheItem.
-        match *self.cached_images.get(&request.key) {
+        match *self.doc_cache.cached_images.get(&request.key) {
             ImageResult::UntiledAuto(ref image_info) => Ok(image_info),
             ImageResult::Multi(ref entries) => Ok(entries.get(&request.into())),
             ImageResult::Err(_) => Err(()),
         }
     }
 
     #[inline]
     pub fn get_texture_cache_item(&self, handle: &TextureCacheHandle) -> CacheItem {
         self.texture_cache.get(handle)
     }
 
     pub fn get_image_properties(&self, image_key: ImageKey) -> Option<ImageProperties> {
-        let image_template = &self.resources.image_templates.get(image_key);
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        let image_template = &self.doc_cache.resources.image_templates.get(image_key);
 
         image_template.map(|image_template| {
             let external_image = match image_template.data {
                 CachedImageData::External(ext_image) => match ext_image.image_type {
                     ExternalImageType::TextureHandle(_) => Some(ext_image),
                     // external buffer uses resource_cache.
                     ExternalImageType::Buffer => None,
                 },
@@ -1445,82 +1482,105 @@ impl ResourceCache {
             ImageProperties {
                 descriptor: image_template.descriptor,
                 external_image,
                 tiling: image_template.tiling,
             }
         })
     }
 
+    pub fn set_document(&mut self, document_id: DocumentId) {
+        debug_assert_eq!(self.document_id, DocumentId::INVALID);
+        self.document_id = document_id;
+        self.doc_cache = self.doc_caches
+                             .remove(&self.document_id)
+                             .unwrap_or_else(|| DocumentResourceCache::new());
+        self.texture_cache.set_document(document_id);
+    }
+
+    pub fn clear_document(&mut self) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        self.texture_cache.clear_document();
+        self.doc_caches.insert(self.document_id,
+                               mem::replace(&mut self.doc_cache, DocumentResourceCache::new()));
+        self.document_id = DocumentId::INVALID;
+    }
+
     pub fn begin_frame(&mut self, stamp: FrameStamp) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         debug_assert_eq!(self.state, State::Idle);
+
+        self.now = stamp;
         self.state = State::AddResources;
         self.texture_cache.begin_frame(stamp);
-        self.cached_glyphs.begin_frame(&self.texture_cache, &self.cached_render_tasks, &mut self.glyph_rasterizer);
-        self.cached_render_tasks.begin_frame(&mut self.texture_cache);
-        self.current_frame_id = stamp.frame_id();
+        self.doc_cache.cached_glyphs.begin_frame(&self.texture_cache, &self.doc_cache.cached_render_tasks, &mut self.glyph_rasterizer);
+        self.doc_cache.cached_render_tasks.begin_frame(&mut self.texture_cache);
     }
 
     pub fn block_until_all_resources_added(
         &mut self,
         gpu_cache: &mut GpuCache,
         render_tasks: &mut RenderTaskTree,
         texture_cache_profile: &mut TextureCacheProfileCounters,
     ) {
         profile_scope!("block_until_all_resources_added");
 
         debug_assert_eq!(self.state, State::AddResources);
+        debug_assert!(self.document_id != DocumentId::INVALID);
+
         self.state = State::QueryResources;
 
         self.glyph_rasterizer.resolve_glyphs(
-            &mut self.cached_glyphs,
+            &mut self.doc_cache.cached_glyphs,
             &mut self.texture_cache,
             gpu_cache,
-            &mut self.cached_render_tasks,
+            &mut self.doc_cache.cached_render_tasks,
             render_tasks,
             texture_cache_profile,
         );
 
         self.rasterize_missing_blob_images();
 
         // Apply any updates of new / updated images (incl. blobs) to the texture cache.
         self.update_texture_cache(gpu_cache);
         render_tasks.prepare_for_render();
-        self.cached_render_tasks.update(
+        self.doc_cache.cached_render_tasks.update(
             gpu_cache,
             &mut self.texture_cache,
             render_tasks,
         );
         self.texture_cache.end_frame(texture_cache_profile);
     }
 
     fn rasterize_missing_blob_images(&mut self) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         if self.missing_blob_images.is_empty() {
             return;
         }
 
         self.blob_image_handler
             .as_mut()
             .unwrap()
-            .prepare_resources(&self.resources, &self.missing_blob_images);
+            .prepare_resources(&self.doc_cache.resources, &self.missing_blob_images);
 
         let is_low_priority = false;
         let rasterized_blobs = self.blob_image_rasterizer
             .as_mut()
             .unwrap()
             .rasterize(&self.missing_blob_images, is_low_priority);
 
         self.add_rasterized_blob_images(rasterized_blobs);
 
         self.missing_blob_images.clear();
     }
 
     fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         for request in self.pending_image_requests.drain() {
-            let image_template = self.resources.image_templates.get_mut(request.key).unwrap();
+            let image_template = self.doc_cache.resources.image_templates.get_mut(request.key).unwrap();
             debug_assert!(image_template.data.uses_texture_cache());
 
             let mut updates: SmallVec<[(CachedImageData, Option<DeviceIntRect>); 1]> = SmallVec::new();
 
             match image_template.data {
                 CachedImageData::Raw(..) | CachedImageData::External(..) => {
                     // Safe to clone here since the Raw image data is an
                     // Arc, and the external image data is small.
@@ -1549,17 +1609,17 @@ impl ResourceCache {
                             debug_assert!(false, "invalid blob image request during frame building");
                             continue;
                         }
                     }
                 }
             };
 
             for (image_data, blob_rasterized_rect) in updates {
-                let entry = match *self.cached_images.get_mut(&request.key) {
+                let entry = match *self.doc_cache.cached_images.get_mut(&request.key) {
                     ImageResult::UntiledAuto(ref mut entry) => entry,
                     ImageResult::Multi(ref mut entries) => entries.get_mut(&request.into()),
                     ImageResult::Err(_) => panic!("Update requested for invalid entry")
                 };
 
                 let mut descriptor = image_template.descriptor.clone();
                 let mut dirty_rect = entry.dirty_rect.replace_with_empty();
 
@@ -1638,80 +1698,104 @@ impl ResourceCache {
                 );
             }
         }
     }
 
     pub fn end_frame(&mut self) {
         debug_assert_eq!(self.state, State::QueryResources);
         self.state = State::Idle;
+        self.now = FrameStamp::INVALID;
     }
 
     pub fn set_debug_flags(&mut self, flags: DebugFlags) {
         self.texture_cache.set_debug_flags(flags);
     }
 
     pub fn clear(&mut self, what: ClearCache) {
-        if what.contains(ClearCache::IMAGES) {
-            for (_key, mut cached) in self.cached_images.resources.drain() {
-                cached.drop_from_cache(&mut self.texture_cache);
+        debug_assert_eq!(self.document_id, DocumentId::INVALID);
+        let mut doc_caches = mem::replace(&mut self.doc_caches, FastHashMap::default());
+        for (&document_id, doc_cache) in doc_caches.iter_mut() {
+            self.document_id = document_id;
+            self.doc_cache = mem::replace(doc_cache, DocumentResourceCache::new());
+            self.texture_cache.set_document(document_id);
+            if what.contains(ClearCache::IMAGES) {
+                for (_key, mut cached) in self.doc_cache.cached_images.resources.drain() {
+                    cached.drop_from_cache(&mut self.texture_cache);
+                }
+            }
+            if what.contains(ClearCache::GLYPHS) {
+                self.doc_cache.cached_glyphs.clear();
             }
-        }
-        if what.contains(ClearCache::GLYPHS) {
-            self.cached_glyphs.clear();
+            if what.contains(ClearCache::GLYPH_DIMENSIONS) {
+                self.cached_glyph_dimensions.clear();
+            }
+            if what.contains(ClearCache::RENDER_TASKS) {
+                self.doc_cache.cached_render_tasks.clear();
+            }
+            if what.contains(ClearCache::TEXTURE_CACHE) {
+                self.texture_cache.clear();
+            }
+            if what.contains(ClearCache::RASTERIZED_BLOBS) {
+                self.rasterized_blob_images.clear();
+            }
+            self.texture_cache.clear_document();
+            *doc_cache = mem::replace(&mut self.doc_cache, DocumentResourceCache::new());
+            self.document_id = DocumentId::INVALID;
         }
-        if what.contains(ClearCache::GLYPH_DIMENSIONS) {
-            self.cached_glyph_dimensions.clear();
-        }
-        if what.contains(ClearCache::RENDER_TASKS) {
-            self.cached_render_tasks.clear();
-        }
-        if what.contains(ClearCache::TEXTURE_CACHE) {
-            self.texture_cache.clear();
-        }
-        if what.contains(ClearCache::RASTERIZED_BLOBS) {
-            self.rasterized_blob_images.clear();
-        }
+        self.doc_caches = doc_caches;
     }
 
     pub fn clear_namespace(&mut self, namespace: IdNamespace) {
-        self.clear_images(|k| k.0 == namespace);
+        debug_assert_eq!(self.now, FrameStamp::INVALID);
+        let mut doc_caches = mem::replace(&mut self.doc_caches, FastHashMap::default());
+        for (&document_id, doc_cache) in doc_caches.iter_mut() {
+            self.document_id = document_id;
+            self.doc_cache = mem::replace(doc_cache, DocumentResourceCache::new());
+            self.texture_cache.set_document(document_id);
+            self.clear_images(|k| k.0 == namespace);
 
-        self.resources.font_instances
-            .write()
-            .unwrap()
-            .retain(|key, _| key.0 != namespace);
-        for &key in self.resources.font_templates.keys().filter(|key| key.0 == namespace) {
-            self.glyph_rasterizer.delete_font(key);
+            self.doc_cache.resources.font_instances
+                .write()
+                .unwrap()
+                .retain(|key, _| key.0 != namespace);
+            for &key in self.doc_cache.resources.font_templates.keys().filter(|key| key.0 == namespace) {
+                self.glyph_rasterizer.delete_font(key);
+            }
+            self.doc_cache.resources
+                .font_templates
+                .retain(|key, _| key.0 != namespace);
+            self.doc_cache.cached_glyphs
+                .clear_fonts(|font| font.font_key.0 == namespace);
+            *doc_cache = mem::replace(&mut self.doc_cache, DocumentResourceCache::new());
+            self.texture_cache.clear_document();
+            self.document_id = DocumentId::INVALID;
         }
-        self.resources
-            .font_templates
-            .retain(|key, _| key.0 != namespace);
-        self.cached_glyphs
-            .clear_fonts(|font| font.font_key.0 == namespace);
+        self.doc_caches = doc_caches;
 
         if let Some(ref mut r) = self.blob_image_handler {
             r.clear_namespace(namespace);
         }
     }
 
     /// Reports the CPU heap usage of this ResourceCache.
     pub fn report_memory(&self, op: VoidPtrToSizeFn) -> MemoryReport {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         let mut report = MemoryReport::default();
 
         // Measure fonts. We only need the templates here, because the instances
         // don't have big buffers.
-        for (_, font) in self.resources.font_templates.iter() {
+        for (_, font) in self.doc_cache.resources.font_templates.iter() {
             if let FontTemplate::Raw(ref raw, _) = font {
                 report.fonts += unsafe { op(raw.as_ptr() as *const c_void) };
             }
         }
 
         // Measure images.
-        for (_, image) in self.resources.image_templates.images.iter() {
+        for (_, image) in self.doc_cache.resources.image_templates.images.iter() {
             report.images += match image.data {
                 CachedImageData::Raw(ref v) => unsafe { op(v.as_ptr() as *const c_void) },
                 CachedImageData::Blob | CachedImageData::External(..) => 0,
             }
         }
 
         // Mesure rasterized blobs.
         // TODO(gw): Temporarily disabled while we roll back a crash. We can re-enable
@@ -1728,34 +1812,41 @@ impl ResourceCache {
         }
         */
 
         report
     }
 
     /// Properly deletes all images matching the predicate.
     fn clear_images<F: Fn(&ImageKey) -> bool>(&mut self, f: F) {
-        let keys = self.resources.image_templates.images.keys().filter(|k| f(*k))
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        let keys = self.doc_cache.resources.image_templates.images.keys().filter(|k| f(*k))
             .cloned().collect::<SmallVec<[ImageKey; 16]>>();
 
         for key in keys {
             self.delete_image_template(key);
         }
 
-        let blob_f = |key: &BlobImageKey| { f(&key.as_image()) };
-        debug_assert!(!self.resources.image_templates.images.keys().any(&f));
-        debug_assert!(!self.cached_images.resources.keys().any(&f));
-        debug_assert!(!self.blob_image_templates.keys().any(&blob_f));
-        debug_assert!(!self.rasterized_blob_images.keys().any(&blob_f));
+        debug_assert!(!self.doc_cache.resources.image_templates.images.keys().any(&f));
+        debug_assert!(!self.doc_cache.cached_images.resources.keys().any(&f));
     }
 }
 
 impl Drop for ResourceCache {
     fn drop(&mut self) {
-        self.clear_images(|_| true);
+        let mut doc_caches = mem::replace(&mut self.doc_caches, FastHashMap::default());
+        for (&document_id, doc_cache) in doc_caches.iter_mut() {
+            self.document_id = document_id;
+            self.doc_cache = mem::replace(doc_cache, DocumentResourceCache::new());
+            self.texture_cache.set_document(document_id);
+            self.clear_images(|_| true);
+            *doc_cache = mem::replace(&mut self.doc_cache, DocumentResourceCache::new());
+            self.texture_cache.clear_document();
+            self.document_id = DocumentId::INVALID;
+        }
     }
 }
 
 pub fn get_blob_tiling(
     tiling: Option<TileSize>,
     descriptor: &ImageDescriptor,
     max_texture_size: i32,
 ) -> Option<TileSize> {
@@ -1821,28 +1912,28 @@ pub struct PlainResources {
     font_templates: FastHashMap<FontKey, PlainFontTemplate>,
     font_instances: FastHashMap<FontInstanceKey, FontInstance>,
     image_templates: FastHashMap<ImageKey, PlainImageTemplate>,
 }
 
 #[cfg(feature = "capture")]
 #[derive(Serialize)]
 pub struct PlainCacheRef<'a> {
-    current_frame_id: FrameId,
+    now: FrameStamp,
     glyphs: &'a GlyphCache,
     glyph_dimensions: &'a GlyphDimensionsCache,
     images: &'a ImageCache,
     render_tasks: &'a RenderTaskCache,
     textures: &'a TextureCache,
 }
 
 #[cfg(feature = "replay")]
 #[derive(Deserialize)]
 pub struct PlainCacheOwn {
-    current_frame_id: FrameId,
+    now: FrameStamp,
     glyphs: GlyphCache,
     glyph_dimensions: GlyphDimensionsCache,
     images: ImageCache,
     render_tasks: RenderTaskCache,
     textures: TextureCache,
 }
 
 #[cfg(feature = "replay")]
@@ -1867,17 +1958,17 @@ impl ResourceCache {
         &mut self, root: &PathBuf
     ) -> (PlainResources, Vec<ExternalCaptureImage>) {
         #[cfg(feature = "png")]
         use device::ReadPixelsFormat;
         use std::fs;
         use std::io::Write;
 
         info!("saving resource cache");
-        let res = &self.resources;
+        let res = &self.doc_cache.resources;
         let path_fonts = root.join("fonts");
         if !path_fonts.is_dir() {
             fs::create_dir(&path_fonts).unwrap();
         }
         let path_images = root.join("images");
         if !path_images.is_dir() {
             fs::create_dir(&path_images).unwrap();
         }
@@ -1966,17 +2057,17 @@ impl ResourceCache {
                     let result = if requires_tiling {
                         warn!("Tiled blob images aren't supported yet");
                         RasterizedBlobImage {
                             rasterized_rect: desc.size.into(),
                             data: Arc::new(vec![0; desc.compute_total_size() as usize])
                         }
                     } else {
                         let blob_handler = self.blob_image_handler.as_mut().unwrap();
-                        blob_handler.prepare_resources(&self.resources, blob_request_params);
+                        blob_handler.prepare_resources(&self.doc_cache.resources, blob_request_params);
                         let mut rasterizer = blob_handler.create_blob_rasterizer();
                         let (_, result) = rasterizer.rasterize(blob_request_params, false).pop().unwrap();
                         result.expect("Blob rasterization failed")
                     };
 
                     assert_eq!(result.rasterized_rect.size, desc.size);
                     assert_eq!(result.data.len(), desc.compute_total_size() as usize);
 
@@ -2043,21 +2134,21 @@ impl ResourceCache {
         };
 
         (resources, external_images)
     }
 
     #[cfg(feature = "capture")]
     pub fn save_caches(&self, _root: &PathBuf) -> PlainCacheRef {
         PlainCacheRef {
-            current_frame_id: self.current_frame_id,
-            glyphs: &self.cached_glyphs,
+            now: self.now,
+            glyphs: &self.doc_cache.cached_glyphs,
             glyph_dimensions: &self.cached_glyph_dimensions,
-            images: &self.cached_images,
-            render_tasks: &self.cached_render_tasks,
+            images: &self.doc_cache.cached_images,
+            render_tasks: &self.doc_cache.cached_render_tasks,
             textures: &self.texture_cache,
         }
     }
 
     #[cfg(feature = "replay")]
     pub fn load_capture(
         &mut self,
         resources: PlainResources,
@@ -2070,38 +2161,38 @@ impl ResourceCache {
         info!("loading resource cache");
         //TODO: instead of filling the local path to Arc<data> map as we process
         // each of the resource types, we could go through all of the local paths
         // and fill out the map as the first step.
         let mut raw_map = FastHashMap::<String, Arc<Vec<u8>>>::default();
 
         match caches {
             Some(cached) => {
-                self.current_frame_id = cached.current_frame_id;
-                self.cached_glyphs = cached.glyphs;
+                self.now = cached.now;
+                self.doc_cache.cached_glyphs = cached.glyphs;
                 self.cached_glyph_dimensions = cached.glyph_dimensions;
-                self.cached_images = cached.images;
-                self.cached_render_tasks = cached.render_tasks;
+                self.doc_cache.cached_images = cached.images;
+                self.doc_cache.cached_render_tasks = cached.render_tasks;
                 self.texture_cache = cached.textures;
             }
             None => {
-                self.current_frame_id = FrameId::INVALID;
-                self.cached_glyphs.clear();
+                self.now = FrameStamp::INVALID;
+                self.doc_cache.cached_glyphs.clear();
                 self.cached_glyph_dimensions.clear();
-                self.cached_images.clear();
-                self.cached_render_tasks.clear();
+                self.doc_cache.cached_images.clear();
+                self.doc_cache.cached_render_tasks.clear();
                 self.texture_cache = TextureCache::new(
                     self.texture_cache.max_texture_size(),
                     self.texture_cache.max_texture_layers(),
                 );
             }
         }
 
         self.glyph_rasterizer.reset();
-        let res = &mut self.resources;
+        let res = &mut self.doc_cache.resources;
         res.font_templates.clear();
         *res.font_instances.write().unwrap() = resources.font_instances;
         res.image_templates.images.clear();
 
         info!("\tfont templates...");
         let native_font_replacement = Arc::new(NATIVE_FONT.to_vec());
         for (key, plain_template) in resources.font_templates {
             let template = match plain_template {
--- a/gfx/wr/webrender/src/scene_builder.rs
+++ b/gfx/wr/webrender/src/scene_builder.rs
@@ -520,38 +520,40 @@ impl SceneBuilder {
 
                 hooks.pre_scene_swap(txn.scene_build_end_time - txn.scene_build_start_time);
 
                 (Some(info), Some(tx), Some(rx))
             }
             _ => (None, None, None),
         };
 
+        let document_id = txn.document_id;
         let scene_swap_start_time = precise_time_ns();
         let has_resources_updates = !txn.resource_updates.is_empty();
 
         self.tx.send(SceneBuilderResult::Transaction(txn, result_tx)).unwrap();
 
         let _ = self.api_tx.send(ApiMsg::WakeUp);
 
         if let Some(pipeline_info) = pipeline_info {
             // Block until the swap is done, then invoke the hook.
             let swap_result = result_rx.unwrap().recv();
             let scene_swap_time = precise_time_ns() - scene_swap_start_time;
-            self.hooks.as_ref().unwrap().post_scene_swap(pipeline_info, scene_swap_time);
+            self.hooks.as_ref().unwrap().post_scene_swap(document_id,
+                                                         pipeline_info, scene_swap_time);
             // Once the hook is done, allow the RB thread to resume
             match swap_result {
                 Ok(SceneSwapResult::Complete(resume_tx)) => {
                     resume_tx.send(()).ok();
                 },
                 _ => (),
             };
         } else if has_resources_updates {
             if let &Some(ref hooks) = &self.hooks {
-                hooks.post_resource_update();
+                hooks.post_resource_update(document_id);
             }
         } else {
             if let &Some(ref hooks) = &self.hooks {
                 hooks.post_empty_scene_build();
             }
         }
     }
 }
--- a/gfx/wr/webrender/src/texture_cache.rs
+++ b/gfx/wr/webrender/src/texture_cache.rs
@@ -1,20 +1,20 @@
 /* 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/. */
 
-use api::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize, DirtyRect, ImageDirtyRect};
-use api::{ExternalImageType, ImageFormat};
+use api::{DebugFlags, DeviceIntPoint, DeviceIntRect, DeviceIntSize};
+use api::{DirtyRect, ImageDirtyRect, DocumentId, ExternalImageType, ImageFormat};
 use api::ImageDescriptor;
 use device::{TextureFilter, total_gpu_bytes_allocated};
 use freelist::{FreeList, FreeListHandle, UpsertResult, WeakFreeListHandle};
 use gpu_cache::{GpuCache, GpuCacheHandle};
 use gpu_types::{ImageSource, UvRectKind};
-use internal_types::{CacheTextureId, LayerIndex, TextureUpdateList, TextureUpdateSource};
+use internal_types::{CacheTextureId, FastHashMap, LayerIndex, TextureUpdateList, TextureUpdateSource};
 use internal_types::{TextureSource, TextureCacheAllocInfo, TextureCacheUpdate};
 use profiler::{ResourceProfileCounter, TextureCacheProfileCounters};
 use render_backend::{FrameId, FrameStamp};
 use resource_cache::{CacheItem, CachedImageData};
 use std::cell::Cell;
 use std::cmp;
 use std::mem;
 use std::time::{Duration, SystemTime};
@@ -277,17 +277,17 @@ impl SharedTextures {
             (_, _) => unreachable!(),
         }
     }
 }
 
 /// Lists of strong handles owned by the texture cache. There is only one strong
 /// handle for each entry, but unlimited weak handles. Consumers receive the weak
 /// handles, and `TextureCache` owns the strong handles internally.
-#[derive(Default)]
+#[derive(Default, Debug)]
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct EntryHandles {
     /// Handles for each standalone texture cache entry.
     standalone: Vec<FreeListHandle<CacheEntryMarker>>,
     /// Handles for each shared texture cache entry.
     shared: Vec<FreeListHandle<CacheEntryMarker>>,
 }
@@ -405,16 +405,48 @@ impl EvictionThresholdBuilder {
 
         EvictionThreshold {
             id: self.now.frame_id() - max_frames,
             time: self.now.time() - Duration::from_millis(max_time_ms),
         }
     }
 }
 
+#[cfg_attr(feature = "capture", derive(Serialize))]
+#[cfg_attr(feature = "replay", derive(Deserialize))]
+pub struct DocumentTextureCache {
+    /// Set of texture arrays in different formats used for the shared cache.
+    shared_textures: SharedTextures,
+
+    /// The last `FrameStamp` in which we expired the shared cache.
+    last_shared_cache_expiration: FrameStamp,
+
+    /// The time at which we first reached the byte threshold for reclaiming
+    /// cache memory. `None if we haven't reached the threshold.
+    reached_reclaim_threshold: Option<SystemTime>,
+
+    /// Maintains the list of all current items in the texture cache.
+    entries: FreeList<CacheEntry, CacheEntryMarker>,
+
+    /// Strong handles for all entries allocated from the above `FreeList`.
+    handles: EntryHandles,
+}
+
+impl DocumentTextureCache {
+    pub fn new() -> Self {
+        DocumentTextureCache {
+            shared_textures: SharedTextures::new(),
+            last_shared_cache_expiration: FrameStamp::INVALID,
+            reached_reclaim_threshold: None,
+            entries: FreeList::new(),
+            handles: EntryHandles::default(),
+        }
+    }
+}
+
 /// General-purpose manager for images in GPU memory. This includes images,
 /// rasterized glyphs, rasterized blobs, cached render tasks, etc.
 ///
 /// The texture cache is owned and managed by the RenderBackend thread, and
 /// produces a series of commands to manipulate the textures on the Renderer
 /// thread. These commands are executed before any rendering is performed for
 /// a given frame.
 ///
@@ -425,19 +457,16 @@ impl EvictionThresholdBuilder {
 /// The TextureCache is different from the GpuCache in that the former stores
 /// images, whereas the latter stores data and parameters for use in the shaders.
 /// This means that the texture cache can be visualized, which is a good way to
 /// understand how it works. Enabling gfx.webrender.debug.texture-cache shows a
 /// live view of its contents in Firefox.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct TextureCache {
-    /// Set of texture arrays in different formats used for the shared cache.
-    shared_textures: SharedTextures,
-
     /// Maximum texture size supported by hardware.
     max_texture_size: i32,
 
     /// Maximum number of texture layers supported by hardware.
     max_texture_layers: usize,
 
     /// The current set of debug flags.
     debug_flags: DebugFlags,
@@ -447,29 +476,20 @@ pub struct TextureCache {
 
     /// A list of allocations and updates that need to be applied to the texture
     /// cache in the rendering thread this frame.
     #[cfg_attr(all(feature = "serde", any(feature = "capture", feature = "replay")), serde(skip))]
     pending_updates: TextureUpdateList,
 
     /// The current `FrameStamp`. Used for cache eviction policies.
     now: FrameStamp,
-
-    /// The last `FrameStamp` in which we expired the shared cache.
-    last_shared_cache_expiration: FrameStamp,
+    document_id: DocumentId,
 
-    /// The time at which we first reached the byte threshold for reclaiming
-    /// cache memory. `None if we haven't reached the threshold.
-    reached_reclaim_threshold: Option<SystemTime>,
-
-    /// Maintains the list of all current items in the texture cache.
-    entries: FreeList<CacheEntry, CacheEntryMarker>,
-
-    /// Strong handles for all entries allocated from the above `FreeList`.
-    handles: EntryHandles,
+    doc_caches: FastHashMap<DocumentId, DocumentTextureCache>,
+    doc_cache: DocumentTextureCache,
 }
 
 impl TextureCache {
     pub fn new(max_texture_size: i32, mut max_texture_layers: usize) -> Self {
         if cfg!(target_os = "macos") {
             // On MBP integrated Intel GPUs, texture arrays appear to be
             // implemented as a single texture of stacked layers, and that
             // texture appears to be subject to the texture size limit. As such,
@@ -491,27 +511,25 @@ impl TextureCache {
             //     the same max texture size of 16k. If we do encounter a driver
             //     with the same bug but a lower max texture size, we might need
             //     to rethink our strategy anyway, since a limit below 32MB might
             //     start to introduce performance issues.
             max_texture_layers = max_texture_layers.min(32);
         }
 
         TextureCache {
-            shared_textures: SharedTextures::new(),
             max_texture_size,
             max_texture_layers,
             debug_flags: DebugFlags::empty(),
             next_id: CacheTextureId(1),
             pending_updates: TextureUpdateList::new(),
             now: FrameStamp::INVALID,
-            last_shared_cache_expiration: FrameStamp::INVALID,
-            reached_reclaim_threshold: None,
-            entries: FreeList::new(),
-            handles: EntryHandles::default(),
+            document_id: DocumentId::INVALID,
+            doc_caches: FastHashMap::default(),
+            doc_cache: DocumentTextureCache::new(),
         }
     }
 
     /// Creates a TextureCache and sets it up with a valid `FrameStamp`, which
     /// is useful for avoiding panics when instantiating the `TextureCache`
     /// directly from unit test code.
     #[allow(dead_code)]
     pub fn new_for_testing(max_texture_size: i32, max_texture_layers: usize) -> Self {
@@ -522,141 +540,160 @@ impl TextureCache {
         cache
     }
 
     pub fn set_debug_flags(&mut self, flags: DebugFlags) {
         self.debug_flags = flags;
     }
 
     pub fn clear(&mut self) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         let standalone_entry_handles = mem::replace(
-            &mut self.handles.standalone,
+            &mut self.doc_cache.handles.standalone,
             Vec::new(),
         );
 
         for handle in standalone_entry_handles {
-            let entry = self.entries.free(handle);
+            let entry = self.doc_cache.entries.free(handle);
             entry.evict();
             self.free(entry);
         }
 
         let shared_entry_handles = mem::replace(
-            &mut self.handles.shared,
+            &mut self.doc_cache.handles.shared,
             Vec::new(),
         );
 
         for handle in shared_entry_handles {
-            let entry = self.entries.free(handle);
+            let entry = self.doc_cache.entries.free(handle);
             entry.evict();
             self.free(entry);
         }
 
-        assert!(self.entries.len() == 0);
+        assert!(self.doc_cache.entries.len() == 0);
+        self.doc_cache.shared_textures.clear(&mut self.pending_updates);
+    }
 
-        self.shared_textures.clear(&mut self.pending_updates);
+    pub fn set_document(&mut self, document_id: DocumentId) {
+        debug_assert_eq!(self.document_id, DocumentId::INVALID);
+        self.document_id = document_id;
+        self.doc_cache = self.doc_caches
+                             .remove(&document_id)
+                             .unwrap_or_else(|| DocumentTextureCache::new());
+    }
+
+    pub fn clear_document(&mut self) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        self.doc_caches.insert(self.document_id,
+                               mem::replace(&mut self.doc_cache, DocumentTextureCache::new()));
+        self.document_id = DocumentId::INVALID;
+        self.now = FrameStamp::INVALID;
     }
 
     /// Called at the beginning of each frame.
     pub fn begin_frame(&mut self, stamp: FrameStamp) {
         self.now = stamp;
         self.maybe_reclaim_shared_cache_memory();
     }
 
     /// Called at the beginning of each frame to periodically GC and reclaim
     /// storage if the cache has grown too large.
     fn maybe_reclaim_shared_cache_memory(&mut self) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         // The minimum number of bytes that we must be able to reclaim in order
         // to justify clearing the entire shared cache in order to shrink it.
         const RECLAIM_THRESHOLD_BYTES: usize = 5 * 1024 * 1024;
 
         // Normally the shared cache only gets GCed when we fail to allocate.
         // However, we also perform a periodic, conservative GC to ensure that
         // we recover unused memory in bounded time, rather than having it
         // depend on allocation patterns of subsequent content.
         let time_since_last_gc = self.now.time()
-            .duration_since(self.last_shared_cache_expiration.time())
+            .duration_since(self.doc_cache.last_shared_cache_expiration.time())
             .unwrap_or(Duration::default());
         let do_periodic_gc = time_since_last_gc >= Duration::from_secs(5) &&
-            self.shared_textures.size_in_bytes() >= RECLAIM_THRESHOLD_BYTES * 2;
+            self.doc_cache.shared_textures.size_in_bytes() >= RECLAIM_THRESHOLD_BYTES * 2;
         if do_periodic_gc {
             let threshold = EvictionThresholdBuilder::new(self.now)
                 .max_frames(1)
                 .max_time_s(10)
                 .build();
             self.maybe_expire_old_shared_entries(threshold);
         }
 
         // If we've had a sufficient number of unused layers for a sufficiently
         // long time, just blow the whole cache away to shrink it.
         //
         // We could do this more intelligently with a resize+blit, but that would
         // add complexity for a rare case.
-        if self.shared_textures.empty_region_bytes() >= RECLAIM_THRESHOLD_BYTES {
-            self.reached_reclaim_threshold.get_or_insert(self.now.time());
+        if self.doc_cache.shared_textures.empty_region_bytes() >= RECLAIM_THRESHOLD_BYTES {
+            self.doc_cache.reached_reclaim_threshold.get_or_insert(self.now.time());
         } else {
-            self.reached_reclaim_threshold = None;
+            self.doc_cache.reached_reclaim_threshold = None;
         }
-        if let Some(t) = self.reached_reclaim_threshold {
+        if let Some(t) = self.doc_cache.reached_reclaim_threshold {
             let dur = self.now.time().duration_since(t).unwrap_or(Duration::default());
             if dur >= Duration::from_secs(5) {
                 self.clear();
-                self.reached_reclaim_threshold = None;
+                self.doc_cache.reached_reclaim_threshold = None;
             }
         }
-
     }
 
     pub fn end_frame(&mut self, texture_cache_profile: &mut TextureCacheProfileCounters) {
         // Expire standalone entries.
         //
         // Most of the time, standalone cache entries correspond to images whose
         // width or height is greater than the region size in the shared cache, i.e.
         // 512 pixels. Cached render tasks also frequently get standalone entries,
         // but those use the Eviction::Eager policy (for now). So the tradeoff there
         // is largely around reducing texture upload jank while keeping memory usage
         // at an acceptable level.
         let threshold = self.default_eviction();
         self.expire_old_entries(EntryKind::Standalone, threshold);
 
-        self.shared_textures.array_a8_linear
+        self.doc_cache.shared_textures.array_a8_linear
             .update_profile(&mut texture_cache_profile.pages_a8_linear);
-        self.shared_textures.array_a16_linear
+        self.doc_cache.shared_textures.array_a16_linear
             .update_profile(&mut texture_cache_profile.pages_a16_linear);
-        self.shared_textures.array_rgba8_linear
+        self.doc_cache.shared_textures.array_rgba8_linear
             .update_profile(&mut texture_cache_profile.pages_rgba8_linear);
-        self.shared_textures.array_rgba8_nearest
+        self.doc_cache.shared_textures.array_rgba8_nearest
             .update_profile(&mut texture_cache_profile.pages_rgba8_nearest);
     }
 
     // Request an item in the texture cache. All images that will
     // be used on a frame *must* have request() called on their
     // handle, to update the last used timestamp and ensure
     // that resources are not flushed from the cache too early.
     //
     // Returns true if the image needs to be uploaded to the
     // texture cache (either never uploaded, or has been
     // evicted on a previous frame).
     pub fn request(&mut self, handle: &TextureCacheHandle, gpu_cache: &mut GpuCache) -> bool {
-        match self.entries.get_opt_mut(handle) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
+
+        match self.doc_cache.entries.get_opt_mut(handle) {
             // If an image is requested that is already in the cache,
             // refresh the GPU cache data associated with this item.
             Some(entry) => {
                 entry.last_access = self.now;
                 entry.update_gpu_cache(gpu_cache);
                 false
             }
             None => true,
         }
     }
 
     // Returns true if the image needs to be uploaded to the
     // texture cache (either never uploaded, or has been
     // evicted on a previous frame).
     pub fn needs_upload(&self, handle: &TextureCacheHandle) -> bool {
-        self.entries.get_opt(handle).is_none()
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        self.doc_cache.entries.get_opt(handle).is_none()
     }
 
     pub fn max_texture_size(&self) -> i32 {
         self.max_texture_size
     }
 
     #[allow(dead_code)]
     pub fn max_texture_layers(&self) -> usize {
@@ -676,23 +713,25 @@ impl TextureCache {
         data: Option<CachedImageData>,
         user_data: [f32; 3],
         mut dirty_rect: ImageDirtyRect,
         gpu_cache: &mut GpuCache,
         eviction_notice: Option<&EvictionNotice>,
         uv_rect_kind: UvRectKind,
         eviction: Eviction,
     ) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
+
         // Determine if we need to allocate texture cache memory
         // for this item. We need to reallocate if any of the following
         // is true:
         // - Never been in the cache
         // - Has been in the cache but was evicted.
         // - Exists in the cache but dimensions / format have changed.
-        let realloc = match self.entries.get_opt(handle) {
+        let realloc = match self.doc_cache.entries.get_opt(handle) {
             Some(entry) => {
                 entry.size != descriptor.size || entry.format != descriptor.format
             }
             None => {
                 // Not allocated, or was previously allocated but has been evicted.
                 true
             }
         };
@@ -700,17 +739,17 @@ impl TextureCache {
         if realloc {
             let params = CacheAllocParams { descriptor, filter, user_data, uv_rect_kind };
             self.allocate(&params, handle);
 
             // If we reallocated, we need to upload the whole item again.
             dirty_rect = DirtyRect::All;
         }
 
-        let entry = self.entries.get_opt_mut(handle)
+        let entry = self.doc_cache.entries.get_opt_mut(handle)
             .expect("BUG: handle must be valid now");
 
         // Install the new eviction notice for this update, if applicable.
         entry.eviction_notice = eviction_notice.cloned();
         entry.uv_rect_kind = uv_rect_kind;
 
         // Invalidate the contents of the resource rect in the GPU cache.
         // This ensures that the update_gpu_cache below will add
@@ -746,26 +785,28 @@ impl TextureCache {
             );
             self.pending_updates.push_update(op);
         }
     }
 
     // Check if a given texture handle has a valid allocation
     // in the texture cache.
     pub fn is_allocated(&self, handle: &TextureCacheHandle) -> bool {
-        self.entries.get_opt(handle).is_some()
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        self.doc_cache.entries.get_opt(handle).is_some()
     }
 
     // Retrieve the details of an item in the cache. This is used
     // during batch creation to provide the resource rect address
     // to the shaders and texture ID to the batching logic.
     // This function will assert in debug modes if the caller
     // tries to get a handle that was not requested this frame.
     pub fn get(&self, handle: &TextureCacheHandle) -> CacheItem {
-        let entry = self.entries
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        let entry = self.doc_cache.entries
             .get_opt(handle)
             .expect("BUG: was dropped from cache or not updated!");
         debug_assert_eq!(entry.last_access, self.now);
         let (layer_index, origin) = match entry.details {
             EntryDetails::Standalone { .. } => {
                 (0, DeviceIntPoint::zero())
             }
             EntryDetails::Cache {
@@ -785,17 +826,19 @@ impl TextureCache {
     /// A more detailed version of get(). This allows access to the actual
     /// device rect of the cache allocation.
     ///
     /// Returns a tuple identifying the texture, the layer, and the region.
     pub fn get_cache_location(
         &self,
         handle: &TextureCacheHandle,
     ) -> (CacheTextureId, LayerIndex, DeviceIntRect) {
-        let entry = self.entries
+        debug_assert!(self.document_id != DocumentId::INVALID);
+
+        let entry = self.doc_cache.entries
             .get_opt(handle)
             .expect("BUG: was dropped from cache or not updated!");
         debug_assert_eq!(entry.last_access, self.now);
         let (layer_index, origin) = match entry.details {
             EntryDetails::Standalone { .. } => {
                 (0, DeviceIntPoint::zero())
             }
             EntryDetails::Cache {
@@ -805,17 +848,18 @@ impl TextureCache {
             } => (layer_index, origin),
         };
         (entry.texture_id,
          layer_index as usize,
          DeviceIntRect::new(origin, entry.size))
     }
 
     pub fn mark_unused(&mut self, handle: &TextureCacheHandle) {
-        if let Some(entry) = self.entries.get_opt_mut(handle) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        if let Some(entry) = self.doc_cache.entries.get_opt_mut(handle) {
             // Set last accessed stamp invalid to ensure it gets cleaned up
             // next time we expire entries.
             entry.last_access = FrameStamp::INVALID;
             entry.eviction = Eviction::Auto;
         }
     }
 
     /// Returns the default eviction policy.
@@ -838,63 +882,66 @@ impl TextureCache {
             .max_time_s(3)
             .scale_by_pressure()
             .build()
     }
 
     /// Shared eviction code for standalone and shared entries.
     ///
     /// See `EvictionThreshold` for more details on policy.
-    fn expire_old_entries(&mut self, kind: EntryKind, threshold: EvictionThreshold) {
+    fn expire_old_entries(&mut self, kind: EntryKind,
+                          threshold: EvictionThreshold) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         // Iterate over the entries in reverse order, evicting the ones older than
         // the frame age threshold. Reverse order avoids iterator invalidation when
         // removing entries.
-        for i in (0..self.handles.select(kind).len()).rev() {
+        for i in (0..self.doc_cache.handles.select(kind).len()).rev() {
             let evict = {
-                let entry = self.entries.get(&self.handles.select(kind)[i]);
+                let entry = self.doc_cache.entries.get(&self.doc_cache.handles.select(kind)[i]);
                 match entry.eviction {
                     Eviction::Manual => false,
                     Eviction::Auto => threshold.should_evict(entry.last_access),
                     Eviction::Eager => entry.last_access < self.now,
                 }
             };
             if evict {
-                let handle = self.handles.select(kind).swap_remove(i);
-                let entry = self.entries.free(handle);
+                let handle = self.doc_cache.handles.select(kind).swap_remove(i);
+                let entry = self.doc_cache.entries.free(handle);
                 entry.evict();
                 self.free(entry);
             }
         }
     }
 
     /// Expires old shared entries, if we haven't done so this frame.
     ///
     /// Returns true if any entries were expired.
     fn maybe_expire_old_shared_entries(&mut self, threshold: EvictionThreshold) -> bool {
-        let old_len = self.handles.shared.len();
-        if self.last_shared_cache_expiration.frame_id() < self.now.frame_id() {
+        debug_assert!(self.document_id != DocumentId::INVALID);
+        let old_len = self.doc_cache.handles.shared.len();
+        if self.doc_cache.last_shared_cache_expiration.frame_id() < self.now.frame_id() {
             self.expire_old_entries(EntryKind::Shared, threshold);
-            self.last_shared_cache_expiration = self.now;
+            self.doc_cache.last_shared_cache_expiration = self.now;
         }
-        self.handles.shared.len() != old_len
+        self.doc_cache.handles.shared.len() != old_len
     }
 
     // Free a cache entry from the standalone list or shared cache.
     fn free(&mut self, entry: CacheEntry) {
         match entry.details {
             EntryDetails::Standalone { .. } => {
                 // This is a standalone texture allocation. Free it directly.
                 self.pending_updates.push_free(entry.texture_id);
             }
             EntryDetails::Cache {
                 origin,
                 layer_index,
             } => {
                 // Free the block in the given region.
-                let texture_array = self.shared_textures.select(entry.format, entry.filter);
+                let texture_array = self.doc_cache.shared_textures.select(entry.format, entry.filter);
                 let region = &mut texture_array.regions[layer_index];
 
                 if self.debug_flags.contains(
                     DebugFlags::TEXTURE_CACHE_DBG |
                     DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED) {
                     self.pending_updates.push_debug_clear(
                         entry.texture_id,
                         origin,
@@ -909,17 +956,17 @@ impl TextureCache {
     }
 
     // Attempt to allocate a block from the shared cache.
     fn allocate_from_shared_cache(
         &mut self,
         params: &CacheAllocParams
     ) -> Option<CacheEntry> {
         // Mutably borrow the correct texture.
-        let texture_array = self.shared_textures.select(
+        let texture_array = self.doc_cache.shared_textures.select(
             params.descriptor.format,
             params.filter,
         );
 
         // Lazy initialize this texture array if required.
         if texture_array.texture_id.is_none() {
             assert!(texture_array.regions.is_empty());
             let texture_id = self.next_id;
@@ -1025,34 +1072,34 @@ impl TextureCache {
         // If we failed to allocate and haven't GCed this frame, do so.
         //
         // If we hit our limit on layers in the shared cache, failing to
         // allocate will trigger standalone textures for every entry, including
         // tiny entries like glyphs. We really want to avoid this, so use a
         // maximally aggressive eviction threshold in that case (which
         // realistically should only happen on mac, where we have a tighter
         // layer limit).
-        let num_regions = self.shared_textures
+        let num_regions = self.doc_cache.shared_textures
             .select(params.descriptor.format, params.filter).regions.len();
         let threshold = if num_regions == self.max_texture_layers {
             EvictionThresholdBuilder::new(self.now).max_frames(1).build()
         } else {
             self.default_eviction()
         };
 
         if self.maybe_expire_old_shared_entries(threshold) {
             if let Some(entry) = self.allocate_from_shared_cache(params) {
                 return entry;
             }
         }
 
         let added_layer = {
             // If we've hit our layer limit, allocate standalone.
             let texture_array =
-                self.shared_textures.select(params.descriptor.format, params.filter);
+                self.doc_cache.shared_textures.select(params.descriptor.format, params.filter);
             // Add a layer, unless we've hit our limit.
             if num_regions < self.max_texture_layers as usize {
                 let info = TextureCacheAllocInfo {
                     width: TEXTURE_REGION_DIMENSIONS,
                     height: TEXTURE_REGION_DIMENSIONS,
                     format: params.descriptor.format,
                     filter: texture_array.filter,
                     layer_count: (num_regions + 1) as i32,
@@ -1072,47 +1119,48 @@ impl TextureCache {
         } else {
             self.allocate_standalone_entry(params)
         }
     }
 
     /// Allocates a cache entry for the given parameters, and updates the
     /// provided handle to point to the new entry.
     fn allocate(&mut self, params: &CacheAllocParams, handle: &mut TextureCacheHandle) {
+        debug_assert!(self.document_id != DocumentId::INVALID);
         let new_cache_entry = self.allocate_cache_entry(params);
         let new_kind = new_cache_entry.details.kind();
 
         // If the handle points to a valid cache entry, we want to replace the
         // cache entry with our newly updated location. We also need to ensure
         // that the storage (region or standalone) associated with the previous
         // entry here gets freed.
         //
         // If the handle is invalid, we need to insert the data, and append the
         // result to the corresponding vector.
         //
         // This is managed with a database style upsert operation.
-        match self.entries.upsert(handle, new_cache_entry) {
+        match self.doc_cache.entries.upsert(handle, new_cache_entry) {
             UpsertResult::Updated(old_entry) => {
                 if new_kind != old_entry.details.kind() {
                     // Handle the rare case than an update moves an entry from
                     // shared to standalone or vice versa. This involves a linear
                     // search, but should be rare enough not to matter.
                     let (from, to) = if new_kind == EntryKind::Standalone {
-                        (&mut self.handles.shared, &mut self.handles.standalone)
+                        (&mut self.doc_cache.handles.shared, &mut self.doc_cache.handles.standalone)
                     } else {
-                        (&mut self.handles.standalone, &mut self.handles.shared)
+                        (&mut self.doc_cache.handles.standalone, &mut self.doc_cache.handles.shared)
                     };
                     let idx = from.iter().position(|h| h.weak() == *handle).unwrap();
                     to.push(from.remove(idx));
                 }
                 self.free(old_entry);
             }
             UpsertResult::Inserted(new_handle) => {
                 *handle = new_handle.weak();
-                self.handles.select(new_kind).push(new_handle);
+                self.doc_cache.handles.select(new_kind).push(new_handle);
             }
         }
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Copy, Clone, PartialEq)]
--- a/gfx/wr/webrender_api/src/api.rs
+++ b/gfx/wr/webrender_api/src/api.rs
@@ -808,16 +808,20 @@ impl Epoch {
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Ord, PartialOrd, Deserialize, Serialize)]
 pub struct IdNamespace(pub u32);
 
 #[repr(C)]
 #[derive(Clone, Copy, Debug, Deserialize, Eq, Hash, PartialEq, Serialize)]
 pub struct DocumentId(pub IdNamespace, pub u32);
 
+impl DocumentId {
+    pub const INVALID: DocumentId = DocumentId(IdNamespace(0), 0);
+}
+
 /// This type carries no valuable semantics for WR. However, it reflects the fact that
 /// clients (Servo) may generate pipelines by different semi-independent sources.
 /// These pipelines still belong to the same `IdNamespace` and the same `DocumentId`.
 /// Having this extra Id field enables them to generate `PipelineId` without collision.
 pub type PipelineSourceId = u32;
 
 /// From the point of view of WR, `PipelineId` is completely opaque and generic as long as
 /// it's clonable, serializable, comparable, and hashable.
--- a/ipc/glue/ByteBuf.h
+++ b/ipc/glue/ByteBuf.h
@@ -87,18 +87,20 @@ template<>
 struct ParamTraits<mozilla::ipc::ByteBuf>
 {
   typedef mozilla::ipc::ByteBuf paramType;
 
   // this is where we transfer the memory from the ByteBuf to IPDL, avoiding a copy
   static void Write(Message* aMsg, paramType& aParam)
   {
     WriteParam(aMsg, aParam.mLen);
-    // hand over ownership of the buffer to the Message
-    aMsg->WriteBytesZeroCopy(aParam.mData, aParam.mLen, aParam.mCapacity);
+    if (aParam.mCapacity) {
+      // hand over ownership of the buffer to the Message
+      aMsg->WriteBytesZeroCopy(aParam.mData, aParam.mLen, aParam.mCapacity);
+    }
     aParam.mData = nullptr;
     aParam.mCapacity = 0;
     aParam.mLen = 0;
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter, paramType* aResult)
   {
     // We make a copy from the BufferList so that we get a contigous result.
--- a/layout/generic/TextDrawTarget.h
+++ b/layout/generic/TextDrawTarget.h
@@ -109,16 +109,18 @@ public:
     if (mHasUnsupportedFeatures) {
       mBuilder.Restore();
       return false;
     }
     mBuilder.ClearSave();
     return true;
   }
 
+  wr::RenderRoot GetRenderRoot() { return mResources->GetRenderRoot(); }
+
   wr::FontInstanceFlags GetWRGlyphFlags() const { return mWRGlyphFlags; }
   void SetWRGlyphFlags(wr::FontInstanceFlags aFlags) { mWRGlyphFlags = aFlags; }
 
   class AutoRestoreWRGlyphFlags
   {
   public:
     ~AutoRestoreWRGlyphFlags()
     {
--- a/layout/generic/nsFrame.cpp
+++ b/layout/generic/nsFrame.cpp
@@ -11014,18 +11014,30 @@ nsIFrame::SetParent(nsContainerFrame* aP
   }
 }
 
 void
 nsIFrame::CreateOwnLayerIfNeeded(nsDisplayListBuilder* aBuilder,
                                  nsDisplayList* aList,
                                  bool* aCreatedContainerItem)
 {
+
   if (GetContent() &&
       GetContent()->IsXULElement() &&
+      gfxVars::UseWebRender() &&
+      gfxPrefs::WebRenderSplitRenderRoots() &&
+      GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::webrendercontent)) {
+    MOZ_ASSERT(!XRE_IsContentProcess());
+    aList->AppendToTop(
+      MakeDisplayItem<nsDisplayRenderRoot>(aBuilder, this, aList, aBuilder->CurrentActiveScrolledRoot()));
+    if (aCreatedContainerItem) {
+      *aCreatedContainerItem = true;
+    }
+  } else if (GetContent() &&
+      GetContent()->IsXULElement() &&
       GetContent()->AsElement()->HasAttr(kNameSpaceID_None, nsGkAtoms::layer)) {
     aList->AppendToTop(
         MakeDisplayItem<nsDisplayOwnLayer>(aBuilder, this, aList, aBuilder->CurrentActiveScrolledRoot()));
     if (aCreatedContainerItem) {
       *aCreatedContainerItem = true;
     }
   }
 }
--- a/layout/generic/nsHTMLCanvasFrame.cpp
+++ b/layout/generic/nsHTMLCanvasFrame.cpp
@@ -135,26 +135,28 @@ public:
     element->HandlePrintCallback(mFrame->PresContext()->Type());
 
     switch(element->GetCurrentContextType()) {
       case CanvasContextType::Canvas2D:
       case CanvasContextType::WebGL1:
       case CanvasContextType::WebGL2:
       {
         bool isRecycled;
-        RefPtr<WebRenderCanvasData> canvasData =
-          aManager->CommandBuilder().CreateOrRecycleWebRenderUserData<WebRenderCanvasData>(this, &isRecycled);
+        RefPtr<WebRenderCanvasData> canvasData = aManager->CommandBuilder()
+          .CreateOrRecycleWebRenderUserData<WebRenderCanvasData>(this,
+                                                                 aBuilder.GetRenderRoot(),
+                                                                 &isRecycled);
         nsHTMLCanvasFrame* canvasFrame = static_cast<nsHTMLCanvasFrame*>(mFrame);
         if (!canvasFrame->UpdateWebRenderCanvasData(aDisplayListBuilder, canvasData)) {
           return true;
         }
         WebRenderCanvasRendererAsync* data =
           static_cast<WebRenderCanvasRendererAsync*>(canvasData->GetCanvasRenderer());
         MOZ_ASSERT(data);
-        data->UpdateCompositableClient();
+        data->UpdateCompositableClient(aBuilder.GetRenderRoot());
 
         // Push IFrame for async image pipeline.
         // XXX Remove this once partial display list update is supported.
 
         nsIntSize canvasSizeInPx = data->GetSize();
         IntrinsicSize intrinsicSize = IntrinsicSizeFromCanvasSize(canvasSizeInPx);
         nsSize intrinsicRatio = IntrinsicRatioFromCanvasSize(canvasSizeInPx);
 
@@ -188,17 +190,18 @@ public:
         LayoutDeviceRect scBounds(LayoutDevicePoint(0, 0), bounds.Size());
         wr::ImageRendering filter = wr::ToImageRendering(nsLayoutUtils::GetSamplingFilterForFrame(mFrame));
         wr::MixBlendMode mixBlendMode = wr::MixBlendMode::Normal;
         aManager->WrBridge()->AddWebRenderParentCommand(OpUpdateAsyncImagePipeline(data->GetPipelineId().value(),
                                                                                    scBounds,
                                                                                    scTransform,
                                                                                    scaleToSize,
                                                                                    filter,
-                                                                                   mixBlendMode));
+                                                                                   mixBlendMode),
+                                                        aBuilder.GetRenderRoot());
         break;
       }
       case CanvasContextType::ImageBitmap:
       {
         // TODO: Support ImageBitmap
         break;
       }
       case CanvasContextType::NoContext:
--- a/layout/painting/RetainedDisplayListBuilder.cpp
+++ b/layout/painting/RetainedDisplayListBuilder.cpp
@@ -332,17 +332,17 @@ public:
 
         if (aNewItem->GetChildren()) {
           Maybe<const ActiveScrolledRoot*> containerASRForChildren;
           if (mBuilder->MergeDisplayLists(aNewItem->GetChildren(),
                                           oldItem->GetChildren(),
                                           destItem->GetChildren(),
                                           containerASRForChildren,
                                           aNewItem->GetPerFrameKey())) {
-            destItem->InvalidateCachedChildInfo();
+            destItem->InvalidateCachedChildInfo(mBuilder->Builder());
             mResultIsModified = true;
           }
           UpdateASR(destItem, containerASRForChildren);
           destItem->UpdateBounds(mBuilder->Builder());
         }
 
         AutoTArray<MergedListIndex, 2> directPredecessors =
           ProcessPredecessorsOfOldNode(oldIndex);
@@ -484,16 +484,17 @@ public:
 
   MergedListIndex AddNewNode(
     nsDisplayItem* aItem,
     const Maybe<OldListIndex>& aOldIndex,
     Span<const MergedListIndex> aDirectPredecessors,
     const Maybe<MergedListIndex>& aExtraDirectPredecessor)
   {
     UpdateContainerASR(aItem);
+    aItem->NotifyUsed(mBuilder->Builder());
 
 #ifdef MOZ_DIAGNOSTIC_ASSERT_ENABLED
     nsIFrame::DisplayItemArray* items =
       aItem->Frame()->GetProperty(nsIFrame::DisplayItems());
     for (nsDisplayItem* i : *items) {
       if (i->Frame() == aItem->Frame() &&
           i->GetPerFrameKey() == aItem->GetPerFrameKey()) {
         MOZ_DIAGNOSTIC_ASSERT(!i->mMergedItem);
@@ -521,17 +522,17 @@ public:
       if (item->GetChildren()) {
         Maybe<const ActiveScrolledRoot*> containerASRForChildren;
         nsDisplayList empty;
         if (mBuilder->MergeDisplayLists(&empty,
                                         item->GetChildren(),
                                         item->GetChildren(),
                                         containerASRForChildren,
                                         item->GetPerFrameKey())) {
-          item->InvalidateCachedChildInfo();
+          item->InvalidateCachedChildInfo(mBuilder->Builder());
           mResultIsModified = true;
         }
         UpdateASR(item, containerASRForChildren);
         item->UpdateBounds(mBuilder->Builder());
       }
       if (item->GetType() == DisplayItemType::TYPE_SUBDOCUMENT) {
         mBuilder->IncrementSubDocPresShellPaintCount(item);
       }
--- a/layout/painting/nsDisplayItemTypesList.h
+++ b/layout/painting/nsDisplayItemTypesList.h
@@ -44,16 +44,17 @@ DECLARE_DISPLAY_ITEM_TYPE(OPACITY, TYPE_
 DECLARE_DISPLAY_ITEM_TYPE(OPTION_EVENT_GRABBER, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(OUTLINE, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(OWN_LAYER, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(PLUGIN, 0)
 DECLARE_DISPLAY_ITEM_TYPE(PLUGIN_READBACK, 0)
 DECLARE_DISPLAY_ITEM_TYPE(PRINT_PLUGIN, 0)
 DECLARE_DISPLAY_ITEM_TYPE(RANGE_FOCUS_RING, 0)
 DECLARE_DISPLAY_ITEM_TYPE(REMOTE, TYPE_RENDERS_NO_IMAGES)
+DECLARE_DISPLAY_ITEM_TYPE(RENDER_ROOT, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(RESOLUTION, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SCROLL_INFO_LAYER, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SELECTION_OVERLAY, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SOLID_COLOR_REGION, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(SUBDOCUMENT, TYPE_RENDERS_NO_IMAGES)
 DECLARE_DISPLAY_ITEM_TYPE(MASK, 0)
 DECLARE_DISPLAY_ITEM_TYPE(FILTER, TYPE_RENDERS_NO_IMAGES)
--- a/layout/painting/nsDisplayList.cpp
+++ b/layout/painting/nsDisplayList.cpp
@@ -731,38 +731,40 @@ AddAnimationsForProperty(nsIFrame* aFram
   }
 }
 
 static uint64_t
 AddAnimationsForWebRender(
   nsDisplayItem* aItem,
   nsCSSPropertyID aProperty,
   mozilla::layers::WebRenderLayerManager* aManager,
-  nsDisplayListBuilder* aDisplayListBuilder)
+  nsDisplayListBuilder* aDisplayListBuilder,
+  wr::RenderRoot aRenderRoot)
 {
   RefPtr<WebRenderAnimationData> animationData =
     aManager->CommandBuilder()
-      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem);
+      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(aItem,
+                                                                aRenderRoot);
   AnimationInfo& animationInfo = animationData->GetAnimationInfo();
   AddAnimationsForProperty(aItem->Frame(),
                            aDisplayListBuilder,
                            aItem,
                            aProperty,
                            animationInfo,
                            Send::Immediate,
                            layers::LayersBackend::LAYERS_WR);
   animationInfo.StartPendingAnimations(aManager->GetAnimationReadyTime());
 
   // Note that animationsId can be 0 (uninitialized in AnimationInfo) if there
   // are no active animations.
   uint64_t animationsId = animationInfo.GetCompositorAnimationsId();
   if (!animationInfo.GetAnimations().IsEmpty()) {
     OpAddCompositorAnimations anim(
       CompositorAnimations(animationInfo.GetAnimations(), animationsId));
-    aManager->WrBridge()->AddWebRenderParentCommand(anim);
+    aManager->WrBridge()->AddWebRenderParentCommand(anim, aRenderRoot);
     aManager->AddActiveCompositorAnimationId(animationsId);
   } else if (animationsId) {
     aManager->AddCompositorAnimationsIdForDiscard(animationsId);
     animationsId = 0;
   }
 
   return animationsId;
 }
@@ -1057,16 +1059,17 @@ nsDisplayListBuilder::nsDisplayListBuild
                                            bool aRetainingDisplayList)
   : mReferenceFrame(aReferenceFrame)
   , mIgnoreScrollFrame(nullptr)
   , mCurrentTableItem(nullptr)
   , mCurrentActiveScrolledRoot(nullptr)
   , mCurrentContainerASR(nullptr)
   , mCurrentFrame(aReferenceFrame)
   , mCurrentReferenceFrame(aReferenceFrame)
+  , mNeedsContentRectDisplayListBuild(false)
   , mRootAGR(AnimatedGeometryRoot::CreateAGRForFrame(aReferenceFrame,
                                                      nullptr,
                                                      true,
                                                      aRetainingDisplayList))
   , mCurrentAGR(mRootAGR)
   , mUsedAGRBudget(0)
   , mDirtyRect(-1, -1, -1, -1)
   , mGlassDisplayItem(nullptr)
@@ -1141,16 +1144,18 @@ nsDisplayListBuilder::BeginFrame()
   mCurrentAGR = mRootAGR;
   mFrameToAnimatedGeometryRootMap.Put(mReferenceFrame, mRootAGR);
 
   mIsPaintingToWindow = false;
   mIgnoreSuppression = false;
   mInTransform = false;
   mInFilter = false;
   mSyncDecodeImages = false;
+  mNeedsContentRectDisplayListBuild = false;
+  mContentRenderRootRect = LayoutDeviceRect();
 }
 
 void
 nsDisplayListBuilder::EndFrame()
 {
   NS_ASSERTION(!mInInvalidSubtree,
                "Someone forgot to cleanup mInInvalidSubtree!");
   mFrameToAnimatedGeometryRootMap.Clear();
@@ -3580,22 +3585,46 @@ nsDisplaySolidColor::CreateWebRenderComm
   mozilla::wr::DisplayListBuilder& aBuilder,
   mozilla::wr::IpcResourceUpdateQueue& aResources,
   const StackingContextHelper& aSc,
   mozilla::layers::WebRenderLayerManager* aManager,
   nsDisplayListBuilder* aDisplayListBuilder)
 {
   LayoutDeviceRect bounds = LayoutDeviceRect::FromAppUnits(
     mBounds, mFrame->PresContext()->AppUnitsPerDevPixel());
-  wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(bounds);
-
-  aBuilder.PushRect(roundedRect,
-                    roundedRect,
-                    !BackfaceIsHidden(),
-                    wr::ToColorF(ToDeviceColor(mColor)));
+
+  LayoutDeviceRect contentRect = aDisplayListBuilder->GetContentRenderRootRect();
+
+  if (bounds.Intersects(contentRect) && aBuilder.GetRenderRoot() != wr::RenderRoot::Content) {
+    MOZ_ASSERT(bounds.x == contentRect.x);
+    wr::LayoutRect contentRoundedRect =
+      wr::ToRoundedLayoutRect(bounds.Intersect(contentRect));
+
+    aBuilder.SubBuilder().PushRect(contentRoundedRect,
+                                   contentRoundedRect,
+                                   !BackfaceIsHidden(),
+                                   wr::ToColorF(ToDeviceColor(mColor)));
+
+    bounds = LayoutDeviceRect(bounds.x, bounds.y,
+                              bounds.Width(), contentRect.y - bounds.y);
+    wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(bounds);
+
+    aBuilder.PushRect(roundedRect,
+                      roundedRect,
+                      !BackfaceIsHidden(),
+                      wr::ToColorF(ToDeviceColor(mColor)));
+
+  } else {
+    wr::LayoutRect roundedRect = wr::ToRoundedLayoutRect(bounds);
+
+    aBuilder.PushRect(roundedRect,
+                      roundedRect,
+                      !BackfaceIsHidden(),
+                      wr::ToColorF(ToDeviceColor(mColor)));
+  }
 
   return true;
 }
 
 nsRect
 nsDisplaySolidColorRegion::GetBounds(nsDisplayListBuilder* aBuilder,
                                      bool* aSnap) const
 {
@@ -6781,17 +6810,18 @@ nsDisplayOpacity::CreateWebRenderCommand
   mozilla::layers::WebRenderLayerManager* aManager,
   nsDisplayListBuilder* aDisplayListBuilder)
 {
   float* opacityForSC = &mOpacity;
 
   uint64_t animationsId = AddAnimationsForWebRender(this,
                                                     eCSSProperty_opacity,
                                                     aManager,
-                                                    aDisplayListBuilder);
+                                                    aDisplayListBuilder,
+                                                    aBuilder.GetRenderRoot());
   wr::WrAnimationProperty prop {
     wr::WrAnimationType::Opacity,
     animationsId,
   };
 
   nsTArray<mozilla::wr::WrFilterOp> filters;
   StackingContextHelper sc(aSc,
                            GetActiveScrolledRoot(),
@@ -7125,17 +7155,18 @@ nsDisplayOwnLayer::CreateWebRenderComman
       aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
   }
 
   // APZ is enabled and this is a scroll thumb, so we need to create and
   // set an animation id. That way APZ can move this scrollthumb around as
   // needed.
   RefPtr<WebRenderAnimationData> animationData =
     aManager->CommandBuilder()
-      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this);
+      .CreateOrRecycleWebRenderUserData<WebRenderAnimationData>(this,
+                                                                aBuilder.GetRenderRoot());
   AnimationInfo& animationInfo = animationData->GetAnimationInfo();
   animationInfo.EnsureAnimationsId();
   mWrAnimationId = animationInfo.GetCompositorAnimationsId();
 
   wr::WrAnimationProperty prop;
   prop.id = mWrAnimationId;
   prop.effect_type = wr::WrAnimationType::Transform;
 
@@ -7181,16 +7212,110 @@ void
 nsDisplayOwnLayer::WriteDebugInfo(std::stringstream& aStream)
 {
   aStream << nsPrintfCString(" (flags 0x%x) (scrolltarget %" PRIu64 ")",
                              (int)mFlags,
                              mScrollbarData.mTargetViewId)
                .get();
 }
 
+nsDisplayRenderRoot::nsDisplayRenderRoot(
+  nsDisplayListBuilder* aBuilder,
+  nsIFrame* aFrame,
+  nsDisplayList* aList,
+  const ActiveScrolledRoot* aActiveScrolledRoot)
+  : nsDisplayWrapList(aBuilder,
+                      aFrame,
+                      aList,
+                      aActiveScrolledRoot,
+                      true)
+  , mSkippedWRCommandBuild(false)
+  , mBuiltWRCommands(false)
+{
+  MOZ_ASSERT(gfxPrefs::WebRenderSplitRenderRoots());
+  mozilla::LayoutDeviceRect rect =
+    mozilla::LayoutDeviceRect::FromAppUnits(mFrame->GetRect(),
+                                            mFrame->PresContext()->AppUnitsPerDevPixel());
+  aBuilder->ExpandContentRenderRootRect(rect);
+  MOZ_COUNT_CTOR(nsDisplayRenderRoot);
+}
+
+void
+nsDisplayRenderRoot::Destroy(nsDisplayListBuilder* aBuilder)
+{
+  if (mBuiltWRCommands && aBuilder) {
+    aBuilder->SetNeedsContentRectDisplayListBuild();
+  }
+  nsDisplayWrapList::Destroy(aBuilder); 
+}
+
+void
+nsDisplayRenderRoot::NotifyUsed(nsDisplayListBuilder* aBuilder)
+{
+  mozilla::LayoutDeviceRect rect =
+    mozilla::LayoutDeviceRect::FromAppUnits(mFrame->GetRect(),
+                                            mFrame->PresContext()->AppUnitsPerDevPixel());
+  aBuilder->ExpandContentRenderRootRect(rect);
+  nsDisplayWrapList::SetReused(aBuilder);
+}
+
+void
+nsDisplayRenderRoot::InvalidateCachedChildInfo(nsDisplayListBuilder* aBuilder)
+{
+  if (mBuiltWRCommands && aBuilder) {
+    aBuilder->SetNeedsContentRectDisplayListBuild();
+    mBuiltWRCommands = false;
+    mSkippedWRCommandBuild = false;
+  }
+}
+
+bool
+nsDisplayRenderRoot::UpdateScrollData(
+  mozilla::layers::WebRenderScrollData* aData,
+  mozilla::layers::WebRenderLayerScrollData* aLayerData)
+{
+  if (aData) {
+    if (mSkippedWRCommandBuild) {
+      aData->SetSkippedContentRect();
+    }
+    aLayerData->SetRenderRoot(wr::RenderRoot::Content);
+  }
+  return true;
+}
+
+bool
+nsDisplayRenderRoot::CreateWebRenderCommands(
+  mozilla::wr::DisplayListBuilder& aBuilder,
+  mozilla::wr::IpcResourceUpdateQueue& aResources,
+  const StackingContextHelper& aSc,
+  WebRenderLayerManager* aManager,
+  nsDisplayListBuilder* aDisplayListBuilder)
+{
+  if (aDisplayListBuilder->GetNeedsContentRectDisplayListBuild() ||
+      !mBuiltWRCommands) {
+    aBuilder.SetSendContentRectDisplayList();
+    if (aBuilder.GetRenderRoot() == wr::RenderRoot::Content) {
+      nsDisplayWrapList::CreateWebRenderCommands(
+        aBuilder, aResources, aSc, aManager, aDisplayListBuilder);
+    } else {
+      wr::DisplayListBuilder& contentRectBuilder = aBuilder.SubBuilder();
+      wr::IpcResourceUpdateQueue& contentRectResources = aResources.SubQueue();
+      const StackingContextHelper& contentRectSc = aSc.SubContextHelper();
+
+      nsDisplayWrapList::CreateWebRenderCommands(
+        contentRectBuilder, contentRectResources, contentRectSc, aManager, aDisplayListBuilder); 
+    }
+    mBuiltWRCommands = true;
+    mSkippedWRCommandBuild = false;
+  } else {
+    mSkippedWRCommandBuild = true;
+  }
+  return true;
+}
+
 nsDisplaySubDocument::nsDisplaySubDocument(nsDisplayListBuilder* aBuilder,
                                            nsIFrame* aFrame,
                                            nsSubDocumentFrame* aSubDocFrame,
                                            nsDisplayList* aList,
                                            nsDisplayOwnLayerFlags aFlags)
   : nsDisplayOwnLayer(aBuilder,
                       aFrame,
                       aList,
@@ -8844,17 +8969,18 @@ nsDisplayTransform::CreateWebRenderComma
     // doesn't turn this stacking context into a reference frame, as it
     // affects positioning. Bug 1345577 tracks a better fix.
     transformForSC = nullptr;
   }
 
   uint64_t animationsId = AddAnimationsForWebRender(this,
                                                     eCSSProperty_transform,
                                                     aManager,
-                                                    aDisplayListBuilder);
+                                                    aDisplayListBuilder,
+                                                    aBuilder.GetRenderRoot());
   wr::WrAnimationProperty prop {
     wr::WrAnimationType::Transform,
     animationsId,
   };
 
   nsTArray<mozilla::wr::WrFilterOp> filters;
   Maybe<nsDisplayTransform*> deferredTransformItem;
   if (!mFrame->HasPerspective()) {
--- a/layout/painting/nsDisplayList.h
+++ b/layout/painting/nsDisplayList.h
@@ -401,16 +401,17 @@ enum class nsDisplayListBuilderMode : ui
  * coordinate system for all display list items. Some of the parameters are
  * available from the prescontext/presshell, but we copy them into the builder
  * for faster/more convenient access.
  */
 class nsDisplayListBuilder
 {
   typedef mozilla::LayoutDeviceIntRect LayoutDeviceIntRect;
   typedef mozilla::LayoutDeviceIntRegion LayoutDeviceIntRegion;
+  typedef mozilla::LayoutDeviceRect LayoutDeviceRect;
 
   /**
    * This manages status of a 3d context to collect visible rects of
    * descendants and passing a dirty rect.
    *
    * Since some transforms maybe singular, passing visible rects or
    * the dirty rect level by level from parent to children may get a
    * wrong result, being different from the result of appling with
@@ -977,16 +978,37 @@ public:
 
   /**
    * Subtracts aRegion from *aVisibleRegion. We avoid letting
    * aVisibleRegion become overcomplex by simplifying it if necessary.
    */
   void SubtractFromVisibleRegion(nsRegion* aVisibleRegion,
                                  const nsRegion& aRegion);
 
+
+  void SetNeedsContentRectDisplayListBuild()
+  {
+    mNeedsContentRectDisplayListBuild = true;