Bug 1441308 - Core renderroot splitting changes r=kats,sotaro
authorDoug Thayer <dothayer@mozilla.com>
Fri, 22 Mar 2019 18:28:42 +0000
changeset 465755 96da9d241051
parent 465754 8cefa694f811
child 465756 e36670b15dd6
push id35746
push usershindli@mozilla.com
push dateSat, 23 Mar 2019 09:46:24 +0000
treeherdermozilla-central@02b7484f316b [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats, sotaro
bugs1441308
milestone68.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1441308 - Core renderroot splitting changes r=kats,sotaro This is a large patch that contains all of the core changes for renderroot splitting. Differential Revision: https://phabricator.services.mozilla.com/D20701
browser/base/content/browser.xul
browser/components/extensions/ExtensionPopups.jsm
dom/ipc/TabChild.cpp
dom/ipc/TabChild.h
dom/ipc/TabParent.cpp
gfx/layers/AnimationHelper.cpp
gfx/layers/AnimationHelper.h
gfx/layers/LayerMetricsWrapper.h
gfx/layers/Layers.cpp
gfx/layers/Layers.h
gfx/layers/LayersLogging.cpp
gfx/layers/LayersLogging.h
gfx/layers/ShareableCanvasRenderer.cpp
gfx/layers/ShareableCanvasRenderer.h
gfx/layers/UpdateImageHelper.h
gfx/layers/apz/public/APZSampler.h
gfx/layers/apz/public/APZTypes.h
gfx/layers/apz/public/APZUpdater.h
gfx/layers/apz/public/CompositorController.h
gfx/layers/apz/public/IAPZCTreeManager.h
gfx/layers/apz/src/APZCTreeManager.cpp
gfx/layers/apz/src/APZCTreeManager.h
gfx/layers/apz/src/APZSampler.cpp
gfx/layers/apz/src/APZUpdater.cpp
gfx/layers/apz/src/AsyncPanZoomController.cpp
gfx/layers/apz/src/AsyncPanZoomController.h
gfx/layers/apz/src/FocusTarget.cpp
gfx/layers/apz/src/FocusTarget.h
gfx/layers/apz/test/gtest/APZCBasicTester.h
gfx/layers/apz/test/gtest/APZTestCommon.h
gfx/layers/apz/test/gtest/TestBasic.cpp
gfx/layers/apz/util/APZCCallbackHelper.cpp
gfx/layers/apz/util/APZCCallbackHelper.h
gfx/layers/apz/util/ChromeProcessController.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/APZCTreeManagerChild.cpp
gfx/layers/ipc/APZCTreeManagerChild.h
gfx/layers/ipc/APZCTreeManagerParent.cpp
gfx/layers/ipc/APZCTreeManagerParent.h
gfx/layers/ipc/CompositableForwarder.h
gfx/layers/ipc/CompositorBridgeParent.cpp
gfx/layers/ipc/CompositorBridgeParent.h
gfx/layers/ipc/ContentCompositorBridgeParent.cpp
gfx/layers/ipc/ContentCompositorBridgeParent.h
gfx/layers/ipc/ImageBridgeChild.cpp
gfx/layers/ipc/ImageBridgeChild.h
gfx/layers/ipc/LayerTransactionParent.cpp
gfx/layers/ipc/LayerTransactionParent.h
gfx/layers/ipc/LayersMessageUtils.h
gfx/layers/ipc/PAPZCTreeManager.ipdl
gfx/layers/ipc/PLayerTransaction.ipdl
gfx/layers/ipc/PWebRenderBridge.ipdl
gfx/layers/ipc/ShadowLayers.cpp
gfx/layers/ipc/ShadowLayers.h
gfx/layers/moz.build
gfx/layers/wr/AsyncImagePipelineManager.cpp
gfx/layers/wr/AsyncImagePipelineManager.h
gfx/layers/wr/IpcResourceUpdateQueue.cpp
gfx/layers/wr/IpcResourceUpdateQueue.h
gfx/layers/wr/RenderRootBoundary.h
gfx/layers/wr/RenderRootStateManager.cpp
gfx/layers/wr/RenderRootStateManager.h
gfx/layers/wr/RenderRootTypes.cpp
gfx/layers/wr/RenderRootTypes.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/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/WebRenderScrollDataWrapper.h
gfx/layers/wr/WebRenderUserData.cpp
gfx/layers/wr/WebRenderUserData.h
gfx/thebes/gfxFontMissingGlyphs.cpp
gfx/thebes/gfxUtils.cpp
gfx/thebes/gfxUtils.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.cpp
gfx/webrender_bindings/WebRenderTypes.h
gfx/webrender_bindings/src/bindings.rs
gfx/webrender_bindings/webrender_ffi.h
gfx/wr/webrender/src/gpu_cache.rs
gfx/wr/webrender/src/render_backend.rs
gfx/wr/webrender/src/renderer.rs
gfx/wr/webrender/src/scene_builder.rs
gfx/wr/webrender/src/texture_cache.rs
gfx/wr/webrender_api/src/api.rs
layout/generic/TextDrawTarget.h
layout/generic/nsFrame.cpp
layout/generic/nsGfxScrollFrame.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
modules/libpref/init/all.js
testing/marionette/reftest.xul
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
xpcom/ds/StaticAtoms.py
xpcom/ds/nsTArray.h
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -691,17 +691,17 @@
                 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" renderroot="content">
       <hbox id="statuspanel-inner">
         <label id="statuspanel-label"
                role="status"
                aria-live="off"
                flex="1"
                crop="end"/>
       </hbox>
     </hbox>
@@ -1326,17 +1326,17 @@
         <searchbar id="searchbar" flex="1"/>
         <toolbartabstop/>
       </toolbaritem>
     </toolbarpalette>
   </toolbox>
 
   <hbox id="fullscr-toggler" hidden="true"/>
 
-  <deck id="content-deck" flex="1">
+  <deck id="content-deck" flex="1" renderroot="content">
     <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"/>
@@ -1362,17 +1362,17 @@
                      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" renderroot="content">
     <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>
@@ -1381,24 +1381,23 @@
 #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" renderroot="content">
     <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" renderroot="content">
     <!-- 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("renderroot", "content");
 
     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/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -27,16 +27,17 @@
 #include "mozilla/dom/MessageManagerBinding.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/Nullable.h"
 #include "mozilla/dom/PaymentRequestChild.h"
 #include "mozilla/dom/PBrowser.h"
 #include "mozilla/dom/WindowProxyHolder.h"
 #include "mozilla/dom/BrowserBridgeChild.h"
 #include "mozilla/gfx/CrossProcessPaint.h"
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/IMEStateManager.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/layers/APZChild.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
 #include "mozilla/layers/APZCTreeManagerChild.h"
 #include "mozilla/layers/APZEventState.h"
 #include "mozilla/layers/ContentProcessController.h"
 #include "mozilla/layers/CompositorBridgeChild.h"
@@ -473,17 +474,17 @@ void TabChild::ContentReceivedInputBlock
                                          bool aPreventDefault) const {
   if (mApzcTreeManager) {
     mApzcTreeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
   }
 }
 
 void TabChild::SetTargetAPZC(
     uint64_t aInputBlockId,
-    const nsTArray<ScrollableLayerGuid>& aTargets) const {
+    const nsTArray<SLGuidAndRenderRoot>& aTargets) const {
   if (mApzcTreeManager) {
     mApzcTreeManager->SetTargetAPZC(aInputBlockId, aTargets);
   }
 }
 
 void TabChild::SetAllowedTouchBehavior(
     uint64_t aInputBlockId,
     const nsTArray<TouchBehaviorFlags>& aTargets) const {
@@ -494,18 +495,18 @@ void TabChild::SetAllowedTouchBehavior(
 
 bool TabChild::DoUpdateZoomConstraints(
     const uint32_t& aPresShellId, const ViewID& aViewId,
     const Maybe<ZoomConstraints>& aConstraints) {
   if (!mApzcTreeManager || mDestroyed) {
     return false;
   }
 
-  ScrollableLayerGuid guid =
-      ScrollableLayerGuid(mLayersId, aPresShellId, aViewId);
+  SLGuidAndRenderRoot guid = SLGuidAndRenderRoot(
+      mLayersId, aPresShellId, aViewId, gfxUtils::GetContentRenderRoot());
 
   mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints);
   return true;
 }
 
 nsresult TabChild::Init(mozIDOMWindowProxy* aParent) {
   if (!mTabGroup) {
     mTabGroup = TabGroup::GetFromActor(this);
@@ -1233,17 +1234,18 @@ void TabChild::HandleDoubleTap(const CSS
   // the guid of any scroll frame), but the zoom-to-rect operation must be
   // performed by the root content scroll frame, so query its identifiers
   // for the SendZoomToRect() call rather than using the ones from |aGuid|.
   uint32_t presShellId;
   ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
           document->GetDocumentElement(), &presShellId, &viewId) &&
       mApzcTreeManager) {
-    ScrollableLayerGuid guid(mLayersId, presShellId, viewId);
+    SLGuidAndRenderRoot guid(mLayersId, presShellId, viewId,
+                             gfxUtils::GetContentRenderRoot());
 
     mApzcTreeManager->ZoomToRect(guid, zoomToRect, DEFAULT_BEHAVIOR);
   }
 }
 
 mozilla::ipc::IPCResult TabChild::RecvHandleTap(
     const GeckoContentController::TapType& aType,
     const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
@@ -1319,28 +1321,30 @@ bool TabChild::NotifyAPZStateChange(
         mozilla::services::GetObserverService();
     observerService->NotifyObservers(nullptr, "APZ:TransformEnd", nullptr);
   }
   return true;
 }
 
 void TabChild::StartScrollbarDrag(
     const layers::AsyncDragMetrics& aDragMetrics) {
-  ScrollableLayerGuid guid(mLayersId, aDragMetrics.mPresShellId,
-                           aDragMetrics.mViewId);
+  SLGuidAndRenderRoot guid(mLayersId, aDragMetrics.mPresShellId,
+                           aDragMetrics.mViewId,
+                           gfxUtils::GetContentRenderRoot());
 
   if (mApzcTreeManager) {
     mApzcTreeManager->StartScrollbarDrag(guid, aDragMetrics);
   }
 }
 
 void TabChild::ZoomToRect(const uint32_t& aPresShellId,
                           const ScrollableLayerGuid::ViewID& aViewId,
                           const CSSRect& aRect, const uint32_t& aFlags) {
-  ScrollableLayerGuid guid(mLayersId, aPresShellId, aViewId);
+  SLGuidAndRenderRoot guid(mLayersId, aPresShellId, aViewId,
+                           gfxUtils::GetContentRenderRoot());
 
   if (mApzcTreeManager) {
     mApzcTreeManager->ZoomToRect(guid, aRect, aFlags);
   }
 }
 
 mozilla::ipc::IPCResult TabChild::RecvActivate() {
   MOZ_ASSERT(mWebBrowser);
--- a/dom/ipc/TabChild.h
+++ b/dom/ipc/TabChild.h
@@ -573,17 +573,17 @@ class TabChild final : public TabChildBa
 
   // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
   void DoFakeShow(const ShowInfo& aShowInfo);
 
   void ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
                                  uint64_t aInputBlockId,
                                  bool aPreventDefault) const;
   void SetTargetAPZC(uint64_t aInputBlockId,
-                     const nsTArray<ScrollableLayerGuid>& aTargets) const;
+                     const nsTArray<layers::SLGuidAndRenderRoot>& aTargets) const;
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   mozilla::ipc::IPCResult RecvHandleTap(
       const layers::GeckoContentController::TapType& aType,
       const LayoutDevicePoint& aPoint, const Modifiers& aModifiers,
       const ScrollableLayerGuid& aGuid, const uint64_t& aInputBlockId) override;
 
   MOZ_CAN_RUN_SCRIPT_BOUNDARY
   mozilla::ipc::IPCResult RecvNormalPriorityHandleTap(
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -86,16 +86,17 @@
 #include "TabChild.h"
 #include "LoadContext.h"
 #include "nsNetCID.h"
 #include "nsIAuthInformation.h"
 #include "nsIAuthPromptCallback.h"
 #include "nsAuthInformationHolder.h"
 #include "nsICancelable.h"
 #include "gfxPrefs.h"
+#include "gfxUtils.h"
 #include "nsILoginManagerPrompter.h"
 #include "nsPIWindowRoot.h"
 #include "nsIAuthPrompt2.h"
 #include "gfxDrawable.h"
 #include "ImageOps.h"
 #include "UnitTransforms.h"
 #include <algorithm>
 #include "mozilla/NullPrincipal.h"
@@ -3434,17 +3435,18 @@ TabParent::StartApzAutoscroll(float aAnc
     *aOutRetval = false;
     return NS_OK;
   }
 
   bool success = false;
   if (mRenderFrame.IsInitialized()) {
     layers::LayersId layersId = mRenderFrame.GetLayersId();
     if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
-      ScrollableLayerGuid guid{layersId, aPresShellId, aScrollId};
+      SLGuidAndRenderRoot guid(layersId, aPresShellId, aScrollId,
+                               gfxUtils::GetContentRenderRoot());
 
       // The anchor coordinates that are passed in are relative to the origin
       // of the screen, but we are sending them to APZ which only knows about
       // coordinates relative to the widget, so convert them accordingly.
       CSSPoint anchorCss{aAnchorX, aAnchorY};
       LayoutDeviceIntPoint anchor =
           RoundedToInt(anchorCss * widget->GetDefaultScale());
       anchor -= widget->WidgetToScreenOffset();
@@ -3463,17 +3465,19 @@ NS_IMETHODIMP
 TabParent::StopApzAutoscroll(nsViewID aScrollId, uint32_t aPresShellId) {
   if (!AsyncPanZoomEnabled()) {
     return NS_OK;
   }
 
   if (mRenderFrame.IsInitialized()) {
     layers::LayersId layersId = mRenderFrame.GetLayersId();
     if (nsCOMPtr<nsIWidget> widget = GetWidget()) {
-      ScrollableLayerGuid guid{layersId, aPresShellId, aScrollId};
+      SLGuidAndRenderRoot guid(layersId, aPresShellId, aScrollId,
+                               gfxUtils::GetContentRenderRoot());
+
       widget->StopAsyncAutoscroll(guid);
     }
   }
   return NS_OK;
 }
 
 ShowInfo TabParent::GetShowInfo() {
   TryCacheDPIAndScale();
--- a/gfx/layers/AnimationHelper.cpp
+++ b/gfx/layers/AnimationHelper.cpp
@@ -21,23 +21,25 @@
 namespace mozilla {
 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);
 }
 
@@ -129,20 +131,22 @@ void CompositorAnimationStorage::SetAnim
 
 nsTArray<PropertyAnimationGroup>* CompositorAnimationStorage::GetAnimations(
     const uint64_t& aId) const {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mAnimations.Get(aId);
 }
 
 void CompositorAnimationStorage::SetAnimations(uint64_t aId,
-                                               const AnimationArray& aValue) {
+                                               const AnimationArray& aValue,
+                                               wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   mAnimations.Put(aId, new nsTArray<PropertyAnimationGroup>(
                            AnimationHelper::ExtractAnimations(aValue)));
+  mAnimationRenderRoots.Put(aId, aRenderRoot);
 }
 
 enum class CanSkipCompose {
   IfPossible,
   No,
 };
 static AnimationHelper::SampleResult SampleAnimationForProperty(
     TimeStamp aPreviousFrameTime, TimeStamp aCurrentFrameTime,
--- a/gfx/layers/AnimationHelper.h
+++ b/gfx/layers/AnimationHelper.h
@@ -3,19 +3,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/. */
 
 #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/TimeStamp.h"               // for TimeStamp
+#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 dom {
 enum class CompositeOperation : uint8_t;
@@ -128,16 +129,18 @@ struct AnimatedValue {
 // 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, nsTArray<PropertyAnimationGroup>>
       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
    */
   void SetAnimatedValue(uint64_t aId, gfx::Matrix4x4&& aTransformInDevSpace,
@@ -173,44 +176,50 @@ class CompositorAnimationStorage final {
     return mAnimatedValues.ConstIter();
   }
 
   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
    */
   nsTArray<PropertyAnimationGroup>* GetAnimations(const uint64_t& aId) const;
 
   /**
    * Return the iterator of animations table
    */
   AnimationsTable::Iterator ConstAnimationsTableIter() const {
     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/LayerMetricsWrapper.h
+++ b/gfx/layers/LayerMetricsWrapper.h
@@ -326,16 +326,18 @@ class MOZ_STACK_CLASS LayerMetricsWrappe
 
     if (AtBottomLayer()) {
       return mLayer->AsRefLayer() ? Some(mLayer->AsRefLayer()->GetReferentId())
                                   : Nothing();
     }
     return Nothing();
   }
 
+  Maybe<wr::RenderRoot> GetReferentRenderRoot() const { return Nothing(); }
+
   Maybe<ParentLayerIntRect> GetClipRect() const {
     MOZ_ASSERT(IsValid());
 
     Maybe<ParentLayerIntRect> result;
 
     // The layer can have a clip rect and a scrolled clip, which are considered
     // to apply only to the bottommost LayerMetricsWrapper.
     // TODO: These actually apply in a different coordinate space than the
--- a/gfx/layers/Layers.cpp
+++ b/gfx/layers/Layers.cpp
@@ -2229,38 +2229,50 @@ void LayerManager::DumpPacket(layerscope
 
 /*static*/
 bool LayerManager::IsLogEnabled() {
   return MOZ_LOG_TEST(GetLog(), LogLevel::Debug);
 }
 
 bool LayerManager::SetPendingScrollUpdateForNextTransaction(
     ScrollableLayerGuid::ViewID aScrollId,
-    const ScrollUpdateInfo& aUpdateInfo) {
+    const ScrollUpdateInfo& aUpdateInfo,
+    wr::RenderRoot aRenderRoot) {
   Layer* withPendingTransform = DepthFirstSearch<ForwardIterator>(
       GetRoot(), [](Layer* aLayer) { return aLayer->HasPendingTransform(); });
   if (withPendingTransform) {
     return false;
   }
 
-  mPendingScrollUpdates[aScrollId] = aUpdateInfo;
+  // If this is called on a LayerManager that's not a WebRenderLayerManager,
+  // then we don't actually need the aRenderRoot information. We force it to
+  // RenderRoot::Default so that we can make assumptions in
+  // GetPendingScrollInfoUpdate.
+  wr::RenderRoot renderRoot = (GetBackendType() == LayersBackend::LAYERS_WR)
+    ? aRenderRoot : wr::RenderRoot::Default;
+  mPendingScrollUpdates[renderRoot][aScrollId] = aUpdateInfo;
   return true;
 }
 
 Maybe<ScrollUpdateInfo> LayerManager::GetPendingScrollInfoUpdate(
     ScrollableLayerGuid::ViewID aScrollId) {
-  auto it = mPendingScrollUpdates.find(aScrollId);
-  if (it != mPendingScrollUpdates.end()) {
+  // This never gets called for WebRenderLayerManager, so we assume that all
+  // pending scroll info updates are stored under the default RenderRoot.
+  MOZ_ASSERT(GetBackendType() != LayersBackend::LAYERS_WR);
+  auto it = mPendingScrollUpdates[wr::RenderRoot::Default].find(aScrollId);
+  if (it != mPendingScrollUpdates[wr::RenderRoot::Default].end()) {
     return Some(it->second);
   }
   return Nothing();
 }
 
 void LayerManager::ClearPendingScrollInfoUpdate() {
-  mPendingScrollUpdates.clear();
+  for (auto renderRoot : wr::kRenderRoots) {
+    mPendingScrollUpdates[renderRoot].clear();
+  }
 }
 
 void PrintInfo(std::stringstream& aStream, HostLayer* aLayerComposite) {
   if (!aLayerComposite) {
     return;
   }
   if (const Maybe<ParentLayerIntRect>& clipRect =
           aLayerComposite->GetShadowClipRect()) {
--- a/gfx/layers/Layers.h
+++ b/gfx/layers/Layers.h
@@ -786,23 +786,24 @@ class LayerManager : public FrameRecorde
  public:
   /*
    * Methods to store/get/clear a "pending scroll info update" object on a
    * per-scrollid basis. This is used for empty transactions that push over
    * scroll position updates to the APZ code.
    */
   virtual bool SetPendingScrollUpdateForNextTransaction(
       ScrollableLayerGuid::ViewID aScrollId,
-      const ScrollUpdateInfo& aUpdateInfo);
+      const ScrollUpdateInfo& aUpdateInfo,
+      wr::RenderRoot aRenderRoot);
   Maybe<ScrollUpdateInfo> GetPendingScrollInfoUpdate(
       ScrollableLayerGuid::ViewID aScrollId);
   void ClearPendingScrollInfoUpdate();
 
  protected:
-  ScrollUpdatesMap mPendingScrollUpdates;
+  wr::RenderRootArray<ScrollUpdatesMap> mPendingScrollUpdates;
 };
 
 /**
  * A Layer represents anything that can be rendered onto a destination
  * surface.
  */
 class Layer {
   NS_INLINE_DECL_REFCOUNTING(Layer)
--- a/gfx/layers/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -253,16 +253,23 @@ void AppendToString(std::stringstream& a
                     const char* pfx, const char* sfx) {
   aStream << pfx
           << nsPrintfCString("{ l=0x%" PRIx64 ", p=%u, v=%" PRIu64 " }",
                              uint64_t(s.mLayersId), s.mPresShellId, s.mScrollId)
                  .get()
           << sfx;
 }
 
+void AppendToString(std::stringstream& aStream, const SLGuidAndRenderRoot& s,
+                    const char* pfx, const char* sfx) {
+  aStream << pfx << "{ ";
+  AppendToString(aStream, s.mScrollableLayerGuid, "s=");
+  aStream << nsPrintfCString(", r=%d }", (int)s.mRenderRoot).get() << sfx;
+}
+
 void AppendToString(std::stringstream& aStream, const ZoomConstraints& z,
                     const char* pfx, const char* sfx) {
   aStream << pfx
           << nsPrintfCString("{ z=%d dt=%d min=%f max=%f }", z.mAllowZoom,
                              z.mAllowDoubleTapZoom, z.mMinZoom.scale,
                              z.mMaxZoom.scale)
                  .get()
           << sfx;
--- a/gfx/layers/LayersLogging.h
+++ b/gfx/layers/LayersLogging.h
@@ -7,16 +7,17 @@
 #ifndef GFX_LAYERSLOGGING_H
 #define GFX_LAYERSLOGGING_H
 
 #include "FrameMetrics.h"             // for FrameMetrics
 #include "mozilla/gfx/Matrix.h"       // for Matrix4x4
 #include "mozilla/gfx/Point.h"        // for IntSize, etc
 #include "mozilla/gfx/TiledRegion.h"  // for TiledRegion
 #include "mozilla/gfx/Types.h"        // for SamplingFilter, SurfaceFormat
+#include "mozilla/layers/APZTypes.h"  // for SLGuidAndRenderRoot
 #include "mozilla/layers/CompositorTypes.h"  // for TextureFlags
 #include "mozilla/layers/WebRenderLayersLogging.h"
 #include "mozilla/layers/ZoomConstraints.h"
 #include "nsAString.h"
 #include "nsPrintfCString.h"  // for nsPrintfCString
 #include "nsRegion.h"         // for nsRegion, nsIntRegion
 #include "nscore.h"           // for nsACString, etc
 
@@ -176,16 +177,19 @@ void AppendToString(std::stringstream& a
 
 void AppendToString(std::stringstream& aStream, const FrameMetrics& m,
                     const char* pfx = "", const char* sfx = "",
                     bool detailed = false);
 
 void AppendToString(std::stringstream& aStream, const ScrollableLayerGuid& s,
                     const char* pfx = "", const char* sfx = "");
 
+void AppendToString(std::stringstream& aStream, const SLGuidAndRenderRoot& s,
+                    const char* pfx = "", const char* sfx = "");
+
 void AppendToString(std::stringstream& aStream, const ZoomConstraints& z,
                     const char* pfx = "", const char* sfx = "");
 
 template <class T>
 void AppendToString(std::stringstream& aStream,
                     const mozilla::gfx::MarginTyped<T>& m, const char* pfx = "",
                     const char* sfx = "") {
   aStream << pfx;
--- a/gfx/layers/ShareableCanvasRenderer.cpp
+++ b/gfx/layers/ShareableCanvasRenderer.cpp
@@ -172,17 +172,18 @@ CanvasClient::CanvasClientType Shareable
   }
 
   if (mGLContext) {
     return CanvasClient::CanvasClientTypeShSurf;
   }
   return CanvasClient::CanvasClientSurface;
 }
 
-void ShareableCanvasRenderer::UpdateCompositableClient() {
+void ShareableCanvasRenderer::UpdateCompositableClient(
+    wr::RenderRoot aRenderRoot) {
   if (!CreateCompositable()) {
     return;
   }
 
   if (mCanvasClient && mAsyncRenderer) {
     mCanvasClient->UpdateAsync(mAsyncRenderer);
   }
 
@@ -192,20 +193,22 @@ void ShareableCanvasRenderer::UpdateComp
   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,18 @@ class ShareableCanvasRenderer : public C
 
   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
@@ -47,30 +47,31 @@ class UpdateImageHelper {
   already_AddRefed<gfx::DrawTarget> GetDrawTarget() {
     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,
+                                     Some(aRenderRoot));
   }
 
  private:
   RefPtr<ImageContainer> mImageContainer;
   RefPtr<ImageClient> mImageClient;
   gfx::IntSize mImageSize;
   RefPtr<TextureClient> mTexture;
   bool mIsLocked;
--- a/gfx/layers/apz/public/APZSampler.h
+++ b/gfx/layers/apz/public/APZSampler.h
@@ -54,20 +54,22 @@ class APZSampler {
   /**
    * This function is invoked from rust on the render backend thread when it
    * is created. It effectively tells the APZSampler "the current thread is
    * the sampler thread for this window id" and allows APZSampler to remember
    * which thread it is.
    */
   static void SetSamplerThread(const wr::WrWindowId& aWindowId);
   static void SampleForWebRender(const wr::WrWindowId& aWindowId,
-                                 wr::Transaction* aTxn);
+                                 wr::Transaction* aTxn,
+                                 const wr::DocumentId& aRenderRootId);
 
   void SetSampleTime(const TimeStamp& aSampleTime);
-  void SampleForWebRender(wr::TransactionWrapper& aTxn);
+  void SampleForWebRender(wr::TransactionWrapper& aTxn,
+                          wr::RenderRoot aRenderRoot);
 
   bool SampleAnimations(const LayerMetricsWrapper& aLayer,
                         const TimeStamp& aSampleTime);
 
   /**
    * Compute the updated shadow transform for a scroll thumb layer that
    * reflects async scrolling of the associated scroll frame.
    *
new file mode 100644
--- /dev/null
+++ b/gfx/layers/apz/public/APZTypes.h
@@ -0,0 +1,122 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_layers_APZTypes_h
+#define mozilla_layers_APZTypes_h
+
+#include "LayersTypes.h"
+#include "mozilla/layers/ScrollableLayerGuid.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+
+namespace layers {
+
+// This struct provides a way to identify a specific "render root" within a
+// specific layer subtree. With WebRender enabled, parts of a layer subtree
+// may get split into different WebRender "documents" (aka "render roots"),
+// and APZ often needs to know which "render root" a particular message is
+// targeted at, so that it can use the correct WebRender document id when
+// interfacing with WebRender. For codepaths that are never used with
+// WebRender, the static WrRootId::NonWebRender constructor can be used to
+// obtain an instance of this struct.
+struct WRRootId {
+  LayersId mLayersId;
+  wr::RenderRoot mRenderRoot;
+
+  WRRootId() = default;
+
+  static WRRootId NonWebRender(LayersId aLayersId) {
+    return WRRootId(aLayersId, wr::RenderRoot::Default);
+  }
+
+  WRRootId(LayersId aLayersId, wr::RenderRoot aRenderRoot)
+      : mLayersId(aLayersId), mRenderRoot(aRenderRoot) {}
+
+  WRRootId(wr::PipelineId aLayersId, wr::DocumentId aRenderRootId)
+      : mLayersId(AsLayersId(aLayersId)),
+        mRenderRoot(RenderRootFromId(aRenderRootId)) {}
+
+  bool operator==(const WRRootId& aOther) const {
+    return mRenderRoot == aOther.mRenderRoot && mLayersId == aOther.mLayersId;
+  }
+
+  bool operator!=(const WRRootId& aOther) const { return !(*this == aOther); }
+
+  bool IsValid() const { return mLayersId.IsValid(); }
+
+  // Helper struct that allow this class to be used as a key in
+  // std::unordered_map like so:
+  //   std::unordered_map<WRRootId, ValueType, WRRootId::HashFn> myMap;
+  struct HashFn {
+    std::size_t operator()(const WRRootId& aKey) const {
+      return HashGeneric((uint64_t)aKey.mLayersId, (uint8_t)aKey.mRenderRoot);
+    }
+  };
+};
+
+// This struct provides a way to select which APZUpdater queue a particular
+// message is associated with. A layers subtree may have multiple render roots
+// that are treated independently within WebRender, and each message that goes
+// into the APZUpdater queue may deal with one or more of these render roots
+// within a given layers subtree. This structure allows representing these
+// relationships, and allows the APZUpdater to ensure that the ordering
+// dependencies between messages are preserved and messages get processed in
+// the correct order relative to each other.
+struct UpdaterQueueSelector {
+  LayersId mLayersId;
+  wr::RenderRootSet mRenderRoots;
+
+  UpdaterQueueSelector() = default;
+
+  explicit UpdaterQueueSelector(LayersId aLayersId) : mLayersId(aLayersId) {}
+
+  UpdaterQueueSelector(LayersId aLayersId, wr::RenderRoot aRenderRoot)
+      : mLayersId(aLayersId), mRenderRoots(aRenderRoot) {}
+
+  explicit UpdaterQueueSelector(const WRRootId& aNodeId)
+      : mLayersId(aNodeId.mLayersId), mRenderRoots(aNodeId.mRenderRoot) {}
+};
+
+// This a simple structure that wraps a ScrollableLayerGuid and a RenderRoot.
+// It is needed on codepaths shared with WebRender, where we need to propagate
+// the RenderRoot information along with the ScrollableLayerGuid (as each
+// scrollable frame belongs to exactly one RenderRoot, and APZ needs to record
+// that information on the APZC instances).
+struct SLGuidAndRenderRoot {
+  ScrollableLayerGuid mScrollableLayerGuid;
+  wr::RenderRoot mRenderRoot;
+
+  // needed for IPDL, but shouldn't be used otherwise!
+  SLGuidAndRenderRoot() : mRenderRoot(wr::RenderRoot::Default) {}
+
+  SLGuidAndRenderRoot(LayersId aLayersId, uint32_t aPresShellId,
+                      ScrollableLayerGuid::ViewID aScrollId,
+                      wr::RenderRoot aRenderRoot)
+      : mScrollableLayerGuid(aLayersId, aPresShellId, aScrollId),
+        mRenderRoot(aRenderRoot) {}
+
+  SLGuidAndRenderRoot(const ScrollableLayerGuid& other,
+                      wr::RenderRoot aRenderRoot)
+      : mScrollableLayerGuid(other), mRenderRoot(aRenderRoot) {}
+
+  WRRootId GetWRRootId() const {
+    return WRRootId(mScrollableLayerGuid.mLayersId, mRenderRoot);
+  }
+};
+
+template <int LogLevel>
+gfx::Log<LogLevel>& operator<<(gfx::Log<LogLevel>& log,
+                               const SLGuidAndRenderRoot& aGuid) {
+  return log << '(' << aGuid.mScrollableLayerGuid << ','
+             << (int)aGuid.mRenderRoot << ')';
+}
+
+}  // namespace layers
+
+}  // namespace mozilla
+
+#endif /* mozilla_layers_APZTypes_h */
--- a/gfx/layers/apz/public/APZUpdater.h
+++ b/gfx/layers/apz/public/APZUpdater.h
@@ -7,30 +7,27 @@
 #ifndef mozilla_layers_APZUpdater_h
 #define mozilla_layers_APZUpdater_h
 
 #include <deque>
 #include <unordered_map>
 
 #include "base/platform_thread.h"  // for PlatformThreadId
 #include "LayersTypes.h"
+#include "APZTypes.h"
 #include "mozilla/layers/APZTestData.h"
 #include "mozilla/layers/WebRenderScrollData.h"
 #include "mozilla/StaticMutex.h"
 #include "mozilla/StaticPtr.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 #include "nsThreadUtils.h"
 #include "Units.h"
 
 namespace mozilla {
 
-namespace wr {
-struct WrWindowId;
-}  // namespace wr
-
 namespace layers {
 
 class APZCTreeManager;
 class FocusTarget;
 class Layer;
 class WebRenderScrollData;
 
 /**
@@ -58,144 +55,152 @@ class APZUpdater {
   static void SetUpdaterThread(const wr::WrWindowId& aWindowId);
   static void PrepareForSceneSwap(const wr::WrWindowId& aWindowId);
   static void CompleteSceneSwap(const wr::WrWindowId& aWindowId,
                                 const wr::WrPipelineInfo& aInfo);
   static void ProcessPendingTasks(const wr::WrWindowId& aWindowId);
 
   void ClearTree(LayersId aRootLayersId);
   void UpdateFocusState(LayersId aRootLayerTreeId,
-                        LayersId aOriginatingLayersId,
+                        WRRootId aOriginatingWrRootId,
                         const FocusTarget& aFocusTarget);
   void UpdateHitTestingTree(LayersId aRootLayerTreeId, Layer* aRoot,
                             bool aIsFirstPaint, LayersId aOriginatingLayersId,
                             uint32_t aPaintSequenceNumber);
   /**
    * This should be called (in the WR-enabled case) when the compositor receives
    * a new WebRenderScrollData for a layers id. The |aScrollData| parameter is
    * the scroll data for |aOriginatingLayersId| and |aEpoch| is the
    * corresponding epoch for the transaction that transferred the scroll data.
    * This function will store the new scroll data and update the focus state and
    * hit-testing tree.
    */
-  void UpdateScrollDataAndTreeState(LayersId aRootLayerTreeId,
-                                    LayersId aOriginatingLayersId,
+  void UpdateScrollDataAndTreeState(WRRootId aRootLayerTreeId,
+                                    WRRootId aOriginatingWrRootId,
                                     const wr::Epoch& aEpoch,
                                     WebRenderScrollData&& aScrollData);
   /**
    * This is called in the WR-enabled case when we get an empty transaction that
    * has some scroll offset updates (from paint-skipped scrolling on the content
    * side). This function will update the stored scroll offsets and the
    * hit-testing tree.
    */
-  void UpdateScrollOffsets(LayersId aRootLayerTreeId,
-                           LayersId aOriginatingLayersId,
+  void UpdateScrollOffsets(WRRootId aRootLayerTreeId,
+                           WRRootId aOriginatingWrRootId,
                            ScrollUpdatesMap&& aUpdates,
                            uint32_t aPaintSequenceNumber);
 
-  void NotifyLayerTreeAdopted(LayersId aLayersId,
+  void NotifyLayerTreeAdopted(WRRootId aWrRootId,
                               const RefPtr<APZUpdater>& aOldUpdater);
-  void NotifyLayerTreeRemoved(LayersId aLayersId);
+  void NotifyLayerTreeRemoved(WRRootId aWrRootId);
 
-  bool GetAPZTestData(LayersId aLayersId, APZTestData* aOutData);
+  bool GetAPZTestData(WRRootId aWrRootId, APZTestData* aOutData);
 
-  void SetTestAsyncScrollOffset(LayersId aLayersId,
+  void SetTestAsyncScrollOffset(WRRootId aWrRootId,
                                 const ScrollableLayerGuid::ViewID& aScrollId,
                                 const CSSPoint& aOffset);
-  void SetTestAsyncZoom(LayersId aLayersId,
+  void SetTestAsyncZoom(WRRootId aWrRootId,
                         const ScrollableLayerGuid::ViewID& aScrollId,
                         const LayerToParentLayerScale& aZoom);
 
   // This can only be called on the updater thread.
-  const WebRenderScrollData* GetScrollData(LayersId aLayersId) const;
+  const WebRenderScrollData* GetScrollData(WRRootId aWrRootId) const;
 
   /**
    * This can be used to assert that the current thread is the
    * updater thread (which samples the async transform).
    * This does nothing if thread assertions are disabled.
    */
   void AssertOnUpdaterThread() const;
 
   /**
    * Runs the given task on the APZ "updater thread" for this APZUpdater. If
    * this function is called from the updater thread itself then the task is
    * run immediately without getting queued.
-   * The layers id argument should be the id of the layer tree that is
-   * requesting this task to be run. Conceptually each layer tree has a separate
-   * task queue, so that if one layer tree is blocked waiting for a scene build
-   * then tasks for the other layer trees can still be processed.
+   *
+   * Conceptually each (layers tree, render root) tuple has a separate task
+   * queue. (In the case where WebRender is disabled, the render root is
+   * always the default render root). This makes it so that even if one
+   * (layers tree, render root) is blocked waiting for a scene build in
+   * WebRender, other tasks can still be processed. However, there may be
+   * tasks that are tied to multiple render roots within a given layers tree,
+   * and which would therefore block on all the associated (layers tree,
+   * render root) queues. The aSelector argument allows expressing the set of
+   * render roots the task is tied to so that this ordering dependency can be
+   * respected.
    */
-  void RunOnUpdaterThread(LayersId aLayersId, already_AddRefed<Runnable> aTask);
+  void RunOnUpdaterThread(UpdaterQueueSelector aSelector,
+                          already_AddRefed<Runnable> aTask);
 
   /**
    * Returns true if currently on the APZUpdater's "updater thread".
    */
   bool IsUpdaterThread() const;
 
   /**
    * Dispatches the given task to the APZ "controller thread", but does it
    * *from* the updater thread. That is, if the thread on which this function is
    * called is not the updater thread, the task is first dispatched to the
    * updater thread. When the updater thread runs it (or if this is called
    * directly on the updater thread), that is when the task gets dispatched to
    * the controller thread. The controller thread then actually runs the task.
-   * The layers id argument should be the id of the layer tree that is
-   * requesting this task to be run; in most cases this will probably just be
-   * the root layers id of the compositor.
+   *
+   * See the RunOnUpdaterThread method for details on the aSelector argument.
    */
-  void RunOnControllerThread(LayersId aLayersId,
+  void RunOnControllerThread(UpdaterQueueSelector aSelector,
                              already_AddRefed<Runnable> aTask);
 
  protected:
   virtual ~APZUpdater();
 
   bool UsingWebRenderUpdaterThread() const;
   static already_AddRefed<APZUpdater> GetUpdater(
       const wr::WrWindowId& aWindowId);
 
   void ProcessQueue();
 
  private:
   RefPtr<APZCTreeManager> mApz;
   bool mIsUsingWebRender;
 
-  // Map from layers id to WebRenderScrollData. This can only be touched on
+  // Map from WRRoot id to WebRenderScrollData. This can only be touched on
   // the updater thread.
-  std::unordered_map<LayersId, WebRenderScrollData, LayersId::HashFn>
+  std::unordered_map<WRRootId, WebRenderScrollData, WRRootId::HashFn>
       mScrollData;
 
-  // Stores epoch state for a particular layers id. This structure is only
+  // Stores epoch state for a particular WRRoot 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;
-    // True if and only if the layers id is the root layers id for the
+    // True if and only if the WRRoot id is the root WRRoot 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
-    // has been built. A "visible" layers id is one that is attached to the full
+    // Whether or not the state for this WRRoot id is such that it blocks
+    // processing of tasks. This happens if the root tree or a "visible"
+    // render root has scroll data for an epoch newer than what has been
+    // built. A "visible" render root is one that is attached to the full
     // layer tree (i.e. there is a chain of reflayer items from the root layer
-    // tree to the relevant layer subtree. This is not always the case; for
-    // instance a content process may send the compositor layers for a document
-    // before the chrome has attached the remote iframe to the root document.
-    // Since WR only builds pipelines for "visible" layers ids, |mBuilt| being
-    // populated means that the layers id is "visible".
+    // tree to the relevant layer subtree) on a WR document whose scene has
+    // been built. This is not always the case; for instance a content process
+    // may send the compositor layers for a document before the chrome has
+    // attached the remote iframe to the root UI document. Since WR only
+    // builds pipelines for "visible" render roots, |mBuilt| being populated
+    // means that the render root is "visible".
     bool IsBlocked() const;
   };
 
-  // Map from layers id to epoch state.
+  // Map from WRRoot id to epoch state.
   // This data structure can only be touched on the updater thread.
-  std::unordered_map<LayersId, EpochState, LayersId::HashFn> mEpochData;
+  std::unordered_map<WRRootId, EpochState, WRRootId::HashFn> mEpochData;
 
   // Used to manage the mapping from a WR window id to APZUpdater. These are
   // only used if WebRender is enabled. Both sWindowIdMap and mWindowId should
   // only be used while holding the sWindowIdLock. Note that we use a
   // StaticAutoPtr wrapper on sWindowIdMap to avoid a static initializer for the
   // unordered_map. This also avoids the initializer/memory allocation in cases
   // where we're not using WebRender.
   static StaticMutex sWindowIdLock;
@@ -206,31 +211,37 @@ class APZUpdater {
   mutable Mutex mThreadIdLock;
   // If WebRender and async scene building are enabled, this holds the thread id
   // of the scene builder thread (which is the updater thread) for the
   // compositor associated with this APZUpdater instance. It may be populated
   // even if async scene building is not enabled, but in that case we don't
   // care about the contents.
   Maybe<PlatformThreadId> mUpdaterThreadId;
 
-  // Helper struct that pairs each queued runnable with the layers id that it
-  // is associated with. This allows us to easily implement the conceptual
-  // separation of mUpdaterQueue into independent queues per layers id.
+  // Helper struct that pairs each queued runnable with the layers id and render
+  // roots that it is associated with. This allows us to easily implement the
+  // conceptual separation of mUpdaterQueue into independent queues per (layers
+  // id, render root) pair. Note that when the UpdaterQueueSelector has multiple
+  // render roots, the task blocks on *all* of the queues for the (layers
+  // id, render root) pairs.
   struct QueuedTask {
-    LayersId mLayersId;
+    UpdaterQueueSelector mSelector;
     RefPtr<Runnable> mRunnable;
   };
 
   // Lock used to protect mUpdaterQueue
   Mutex mQueueLock;
-  // Holds a queue of tasks to be run on the updater thread,
-  // when the updater thread is a WebRender thread, since it won't have a
-  // message loop we can dispatch to. Note that although this is a single queue
-  // it is conceptually separated into multiple ones, one per layers id. Tasks
-  // for a given layers id will always run in FIFO order, but there is no
-  // guaranteed ordering for tasks with different layers ids.
+  // Holds a queue of tasks to be run on the updater thread, when the updater
+  // thread is a WebRender thread, since it is conceptually separated into
+  // multiple ones, one per (layers id, render root). Tasks for a given
+  // conceptual queue will always run in FIFO order, and tasks that are tied
+  // to multiple queues (by virtue of having multiple render roots in their
+  // UpdaterQueueSelector) can cause one queue to be blocked on another in
+  // order to preserve FIFO ordering. In the common case, though, where there
+  // is exactly one render root per task, there is no guaranteed ordering for
+  // tasks with different render roots.
   std::deque<QueuedTask> mUpdaterQueue;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif  // mozilla_layers_APZUpdater_h
--- a/gfx/layers/apz/public/CompositorController.h
+++ b/gfx/layers/apz/public/CompositorController.h
@@ -3,25 +3,28 @@
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_layers_CompositorController_h
 #define mozilla_layers_CompositorController_h
 
 #include "nsISupportsImpl.h"  // for NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
+#include "mozilla/Maybe.h"
+#include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 class CompositorController {
  public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
-  virtual void ScheduleRenderOnCompositorThread() = 0;
+  virtual void ScheduleRenderOnCompositorThread(
+      const Maybe<wr::RenderRoot>& aRenderRootid = Nothing()) = 0;
   virtual void ScheduleHideAllPluginWindows() = 0;
   virtual void ScheduleShowAllPluginWindows() = 0;
 
  protected:
   virtual ~CompositorController() {}
 };
 
 }  // namespace layers
--- a/gfx/layers/apz/public/IAPZCTreeManager.h
+++ b/gfx/layers/apz/public/IAPZCTreeManager.h
@@ -50,17 +50,17 @@ class IAPZCTreeManager {
   virtual void SetKeyboardMap(const KeyboardMap& aKeyboardMap) = 0;
 
   /**
    * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
    * in. The actual animation is done on the sampler thread after being set
    * up. |aRect| must be given in CSS pixels, relative to the document.
    * |aFlags| is a combination of the ZoomToRectBehavior enum values.
    */
-  virtual void ZoomToRect(const ScrollableLayerGuid& aGuid,
+  virtual void ZoomToRect(const SLGuidAndRenderRoot& aGuid,
                           const CSSRect& aRect,
                           const uint32_t aFlags = DEFAULT_BEHAVIOR) = 0;
 
   /**
    * If we have touch listeners, this should always be called when we know
    * definitively whether or not content has preventDefaulted any touch events
    * that have come in. If |aPreventDefault| is true, any touch events in the
    * queue will be discarded. This function must be called on the controller
@@ -76,48 +76,48 @@ class IAPZCTreeManager {
    * but is safe to call for all input blocks. This function should always be
    * invoked on the controller thread.
    * The different elements in the array of targets correspond to the targets
    * for the different touch points. In the case where the touch point has no
    * target, or the target is not a scrollable frame, the target's |mScrollId|
    * should be set to ScrollableLayerGuid::NULL_SCROLL_ID.
    */
   virtual void SetTargetAPZC(uint64_t aInputBlockId,
-                             const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
+                             const nsTArray<SLGuidAndRenderRoot>& aTargets) = 0;
 
   /**
    * Updates any zoom constraints contained in the <meta name="viewport"> tag.
    * If the |aConstraints| is Nothing() then previously-provided constraints for
    * the given |aGuid| are cleared.
    */
   virtual void UpdateZoomConstraints(
-      const ScrollableLayerGuid& aGuid,
+      const SLGuidAndRenderRoot& aGuid,
       const Maybe<ZoomConstraints>& aConstraints) = 0;
 
   virtual void SetDPI(float aDpiValue) = 0;
 
   /**
    * Sets allowed touch behavior values for current touch-session for specific
    * input block (determined by aInputBlock).
    * Should be invoked by the widget. Each value of the aValues arrays
    * corresponds to the different touch point that is currently active.
    * Must be called after receiving the TOUCH_START event that starts the
    * touch-session.
    * This must be called on the controller thread.
    */
   virtual void SetAllowedTouchBehavior(
       uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) = 0;
 
-  virtual void StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+  virtual void StartScrollbarDrag(const SLGuidAndRenderRoot& aGuid,
                                   const AsyncDragMetrics& aDragMetrics) = 0;
 
-  virtual bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
+  virtual bool StartAutoscroll(const SLGuidAndRenderRoot& aGuid,
                                const ScreenPoint& aAnchorLocation) = 0;
 
-  virtual void StopAutoscroll(const ScrollableLayerGuid& aGuid) = 0;
+  virtual void StopAutoscroll(const SLGuidAndRenderRoot& aGuid) = 0;
 
   /**
    * Function used to disable LongTap gestures.
    *
    * On slow running tests, drags and touch events can be misinterpreted
    * as a long tap. This allows tests to disable long tap gesture detection.
    */
   virtual void SetLongTapEnabled(bool aTapGestureEnabled) = 0;
--- a/gfx/layers/apz/src/APZCTreeManager.cpp
+++ b/gfx/layers/apz/src/APZCTreeManager.cpp
@@ -70,36 +70,36 @@ using mozilla::gfx::CompositorHitTestInf
 typedef mozilla::gfx::Point Point;
 typedef mozilla::gfx::Point4D Point4D;
 typedef mozilla::gfx::Matrix4x4 Matrix4x4;
 
 typedef CompositorBridgeParent::LayerTreeState LayerTreeState;
 
 struct APZCTreeManager::TreeBuildingState {
   TreeBuildingState(LayersId aRootLayersId, bool aIsFirstPaint,
-                    LayersId aOriginatingLayersId, APZTestData* aTestData,
+                    WRRootId aOriginatingWrRootId, APZTestData* aTestData,
                     uint32_t aPaintSequence)
       : mIsFirstPaint(aIsFirstPaint),
-        mOriginatingLayersId(aOriginatingLayersId),
+        mOriginatingWrRootId(aOriginatingWrRootId),
         mPaintLogger(aTestData, aPaintSequence) {
     CompositorBridgeParent::CallWithIndirectShadowTree(
         aRootLayersId, [this](LayerTreeState& aState) -> void {
           mCompositorController = aState.GetCompositorController();
           mInProcessSharingController = aState.InProcessSharingController();
         });
   }
 
   typedef std::unordered_map<AsyncPanZoomController*, gfx::Matrix4x4>
       DeferredTransformMap;
 
   // State that doesn't change as we recurse in the tree building
   RefPtr<CompositorController> mCompositorController;
   RefPtr<MetricsSharingController> mInProcessSharingController;
   const bool mIsFirstPaint;
-  const LayersId mOriginatingLayersId;
+  const WRRootId mOriginatingWrRootId;
   const APZPaintLogHelper mPaintLogger;
 
   // State that is updated as we perform the tree build
 
   // A list of nodes that need to be destroyed at the end of the tree building.
   // This is initialized with all nodes in the old tree, and nodes are removed
   // from it as we reuse them in the new tree.
   nsTArray<RefPtr<HitTestingTreeNode>> mNodesToDestroy;
@@ -326,19 +326,20 @@ void APZCTreeManager::NotifyLayerTreeRem
 
   {  // scope lock
     MutexAutoLock lock(mTestDataLock);
     mTestData.erase(aLayersId);
   }
 }
 
 AsyncPanZoomController* APZCTreeManager::NewAPZCInstance(
-    LayersId aLayersId, GeckoContentController* aController) {
+    LayersId aLayersId, GeckoContentController* aController,
+    wr::RenderRoot aRenderRoot) {
   return new AsyncPanZoomController(
-      aLayersId, this, mInputQueue, aController,
+      aLayersId, this, mInputQueue, aController, aRenderRoot,
       AsyncPanZoomController::USE_GESTURE_DETECTOR);
 }
 
 TimeStamp APZCTreeManager::GetFrameTime() { return TimeStamp::Now(); }
 
 void APZCTreeManager::SetAllowedTouchBehavior(
     uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) {
   APZThreadUtils::AssertOnControllerThread();
@@ -346,33 +347,33 @@ void APZCTreeManager::SetAllowedTouchBeh
   mInputQueue->SetAllowedTouchBehavior(aInputBlockId, aValues);
 }
 
 template <class ScrollNode>
 void  // ScrollNode is a LayerMetricsWrapper or a WebRenderScrollDataWrapper
 APZCTreeManager::UpdateHitTestingTreeImpl(LayersId aRootLayerTreeId,
                                           const ScrollNode& aRoot,
                                           bool aIsFirstPaint,
-                                          LayersId aOriginatingLayersId,
+                                          WRRootId aOriginatingWrRootId,
                                           uint32_t aPaintSequenceNumber) {
   RecursiveMutexAutoLock lock(mTreeLock);
 
   // For testing purposes, we log some data to the APZTestData associated with
   // the layers id that originated this update.
   APZTestData* testData = nullptr;
   if (gfxPrefs::APZTestLoggingEnabled()) {
     MutexAutoLock lock(mTestDataLock);
     UniquePtr<APZTestData> ptr = MakeUnique<APZTestData>();
     auto result =
-        mTestData.insert(std::make_pair(aOriginatingLayersId, std::move(ptr)));
+        mTestData.insert(std::make_pair(aOriginatingWrRootId.mLayersId, std::move(ptr)));
     testData = result.first->second.get();
     testData->StartNewPaint(aPaintSequenceNumber);
   }
 
-  TreeBuildingState state(aRootLayerTreeId, aIsFirstPaint, aOriginatingLayersId,
+  TreeBuildingState state(aRootLayerTreeId, aIsFirstPaint, aOriginatingWrRootId,
                           testData, aPaintSequenceNumber);
 
   // We do this business with collecting the entire tree into an array because
   // otherwise it's very hard to determine which APZC instances need to be
   // destroyed. In the worst case, there are two scenarios: (a) a layer with an
   // APZC is removed from the layer tree and (b) a layer with an APZC is moved
   // in the layer tree from one place to a completely different place. In
   // scenario (a) we would want to destroy the APZC while walking the layer tree
@@ -395,16 +396,17 @@ APZCTreeManager::UpdateHitTestingTreeImp
   bool haveRootContentOutsideAsyncZoomContainer = false;
 
   if (aRoot) {
     std::stack<gfx::TreeAutoIndent<LOG_DEFAULT>> indents;
     std::stack<AncestorTransform> ancestorTransforms;
     HitTestingTreeNode* parent = nullptr;
     HitTestingTreeNode* next = nullptr;
     LayersId layersId = aRootLayerTreeId;
+    wr::RenderRoot renderRoot = wr::RenderRoot::Default;
     ancestorTransforms.push(AncestorTransform());
     state.mParentHasPerspective.push(false);
 
     mApzcTreeLog << "[start]\n";
     mTreeLock.AssertCurrentThreadIn();
 
     ForEachNode<ReverseIterator>(
         aRoot,
@@ -421,17 +423,17 @@ APZCTreeManager::UpdateHitTestingTreeImp
 
           if (aLayerMetrics.Metrics().IsRootContent() &&
               asyncZoomContainerNestingDepth == 0) {
             haveRootContentOutsideAsyncZoomContainer = true;
           }
 
           HitTestingTreeNode* node = PrepareNodeForLayer(
               lock, aLayerMetrics, aLayerMetrics.Metrics(), layersId,
-              ancestorTransforms.top(), parent, next, state);
+              ancestorTransforms.top(), parent, next, state, renderRoot);
           MOZ_ASSERT(node);
           AsyncPanZoomController* apzc = node->GetApzc();
           aLayerMetrics.SetApzc(apzc);
 
           // GetScrollbarAnimationId is only set when webrender is enabled,
           // which limits the extra thumb mapping work to the webrender-enabled
           // case where it is needed.
           // Note also that when webrender is enabled, a "valid" animation id
@@ -464,20 +466,24 @@ APZCTreeManager::UpdateHitTestingTreeImp
           ancestorTransforms.push(currentTransform);
 
           // Note that |node| at this point will not have any children,
           // otherwise we we would have to set next to node->GetFirstChild().
           MOZ_ASSERT(!node->GetFirstChild());
           parent = node;
           next = nullptr;
 
-          // Update the layersId if we have a new one
+          // Update the layersId or renderroot if we have a new one
           if (Maybe<LayersId> newLayersId = aLayerMetrics.GetReferentId()) {
             layersId = *newLayersId;
           }
+          if (Maybe<wr::RenderRoot> newRenderRoot =
+                  aLayerMetrics.GetReferentRenderRoot()) {
+            renderRoot = *newRenderRoot;
+          }
 
           indents.push(gfx::TreeAutoIndent<LOG_DEFAULT>(mApzcTreeLog));
           state.mParentHasPerspective.push(
               aLayerMetrics.TransformIsPerspective());
         },
         [&](ScrollNode aLayerMetrics) {
           if (aLayerMetrics.IsAsyncZoomContainer()) {
             --asyncZoomContainerNestingDepth;
@@ -598,39 +604,44 @@ void APZCTreeManager::UpdateFocusState(L
 void APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
                                            Layer* aRoot, bool aIsFirstPaint,
                                            LayersId aOriginatingLayersId,
                                            uint32_t aPaintSequenceNumber) {
   AssertOnUpdaterThread();
 
   LayerMetricsWrapper root(aRoot);
   UpdateHitTestingTreeImpl(aRootLayerTreeId, root, aIsFirstPaint,
-                           aOriginatingLayersId, aPaintSequenceNumber);
+                           WRRootId::NonWebRender(aOriginatingLayersId), aPaintSequenceNumber);
 }
 
 void APZCTreeManager::UpdateHitTestingTree(
     LayersId aRootLayerTreeId, const WebRenderScrollDataWrapper& aScrollWrapper,
-    bool aIsFirstPaint, LayersId aOriginatingLayersId,
+    bool aIsFirstPaint, WRRootId aOriginatingWrRootId,
     uint32_t aPaintSequenceNumber) {
   AssertOnUpdaterThread();
 
   UpdateHitTestingTreeImpl(aRootLayerTreeId, aScrollWrapper, aIsFirstPaint,
-                           aOriginatingLayersId, aPaintSequenceNumber);
+                           aOriginatingWrRootId, aPaintSequenceNumber);
 }
 
 void APZCTreeManager::SampleForWebRender(wr::TransactionWrapper& aTxn,
-                                         const TimeStamp& aSampleTime) {
+                                         const TimeStamp& aSampleTime,
+                                         wr::RenderRoot aRenderRoot) {
   AssertOnSamplerThread();
   MutexAutoLock lock(mMapLock);
 
   nsTArray<wr::WrTransformProperty> transforms;
 
   // Sample async transforms on scrollable layers.
   for (const auto& mapping : mApzcMap) {
     AsyncPanZoomController* apzc = mapping.second;
+    if (apzc->GetRenderRoot() != aRenderRoot) {
+      // If this APZC belongs to a different render root, skip over it
+      continue;
+    }
 
     // Apply any additional async scrolling for testing purposes (used for
     // reftest-async-scroll and reftest-async-zoom).
     AutoApplyAsyncTestAttributes testAttributeApplier(apzc);
 
     ParentLayerPoint layerTranslation =
         apzc->GetCurrentAsyncTransform(AsyncPanZoomController::eForCompositing)
             .mTranslation;
@@ -663,16 +674,20 @@ void APZCTreeManager::SampleForWebRender
       // It could be that |info| is a scrollthumb for content which didn't
       // have an APZC, for example if the content isn't layerized. Regardless,
       // we can't async-scroll it so we don't need to worry about putting it
       // in mScrollThumbInfo.
       continue;
     }
     AsyncPanZoomController* scrollTargetApzc = it->second;
     MOZ_ASSERT(scrollTargetApzc);
+    if (scrollTargetApzc->GetRenderRoot() != aRenderRoot) {
+      // If this APZC belongs to a different render root, skip over it
+      continue;
+    }
     LayerToParentLayerMatrix4x4 transform =
         scrollTargetApzc->CallWithLastContentPaintMetrics(
             [&](const FrameMetrics& aMetrics) {
               return ComputeTransformForScrollThumb(
                   info.mThumbTransform * AsyncTransformMatrix(),
                   info.mTargetTransform.ToUnknownMatrix(), scrollTargetApzc,
                   aMetrics, info.mThumbData, info.mTargetIsAncestor, nullptr);
             });
@@ -683,16 +698,20 @@ void APZCTreeManager::SampleForWebRender
 
   // Advance animations. It's important that this happens after
   // sampling all async transforms, because AdvanceAnimations() updates
   // the effective scroll offset to the value it should have for the *next*
   // composite after this one (if the APZ frame delay is enabled).
   bool activeAnimations = false;
   for (const auto& mapping : mApzcMap) {
     AsyncPanZoomController* apzc = mapping.second;
+    if (apzc->GetRenderRoot() != aRenderRoot) {
+      // If this APZC belongs to a different render root, skip over it
+      continue;
+    }
     activeAnimations |= apzc->AdvanceAnimations(aSampleTime);
   }
   if (activeAnimations) {
     RefPtr<CompositorController> controller;
     CompositorBridgeParent::CallWithIndirectShadowTree(
         mRootLayersId, [&](LayerTreeState& aState) -> void {
           controller = aState.GetCompositorController();
         });
@@ -791,54 +810,57 @@ static EventRegionsOverride GetEventRegi
     MOZ_ASSERT(aLayer.GetReferentId());
   }
   if (aParent) {
     result |= aParent->GetEventRegionsOverride();
   }
   return result;
 }
 
-void APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+void APZCTreeManager::StartScrollbarDrag(const SLGuidAndRenderRoot& aGuid,
                                          const AsyncDragMetrics& aDragMetrics) {
   APZThreadUtils::AssertOnControllerThread();
 
-  RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  RefPtr<AsyncPanZoomController> apzc =
+      GetTargetAPZC(aGuid.mScrollableLayerGuid);
   if (!apzc) {
-    NotifyScrollbarDragRejected(aGuid);
+    NotifyScrollbarDragRejected(aGuid.mScrollableLayerGuid);
     return;
   }
 
   uint64_t inputBlockId = aDragMetrics.mDragStartSequenceNumber;
   mInputQueue->ConfirmDragBlock(inputBlockId, apzc, aDragMetrics);
 }
 
-bool APZCTreeManager::StartAutoscroll(const ScrollableLayerGuid& aGuid,
+bool APZCTreeManager::StartAutoscroll(const SLGuidAndRenderRoot& aGuid,
                                       const ScreenPoint& aAnchorLocation) {
   APZThreadUtils::AssertOnControllerThread();
 
-  RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  RefPtr<AsyncPanZoomController> apzc =
+      GetTargetAPZC(aGuid.mScrollableLayerGuid);
   if (!apzc) {
     if (XRE_IsGPUProcess()) {
       // If we're in the compositor process, the "return false" will be
       // ignored because the query comes over the PAPZCTreeManager protocol
       // via an async message. In this case, send an explicit rejection
       // message to content.
-      NotifyAutoscrollRejected(aGuid);
+      NotifyAutoscrollRejected(aGuid.mScrollableLayerGuid);
     }
     return false;
   }
 
   apzc->StartAutoscroll(aAnchorLocation);
   return true;
 }
 
-void APZCTreeManager::StopAutoscroll(const ScrollableLayerGuid& aGuid) {
+void APZCTreeManager::StopAutoscroll(const SLGuidAndRenderRoot& aGuid) {
   APZThreadUtils::AssertOnControllerThread();
 
-  if (RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid)) {
+  if (RefPtr<AsyncPanZoomController> apzc =
+          GetTargetAPZC(aGuid.mScrollableLayerGuid)) {
     apzc->StopAutoscroll();
   }
 }
 
 void APZCTreeManager::NotifyScrollbarDragInitiated(
     uint64_t aDragBlockId, const ScrollableLayerGuid& aGuid,
     ScrollDirection aDirection) const {
   RefPtr<GeckoContentController> controller =
@@ -866,17 +888,18 @@ void APZCTreeManager::NotifyAutoscrollRe
   controller->NotifyAsyncAutoscrollRejected(aGuid.mScrollId);
 }
 
 template <class ScrollNode>
 HitTestingTreeNode* APZCTreeManager::PrepareNodeForLayer(
     const RecursiveMutexAutoLock& aProofOfTreeLock, const ScrollNode& aLayer,
     const FrameMetrics& aMetrics, LayersId aLayersId,
     const AncestorTransform& aAncestorTransform, HitTestingTreeNode* aParent,
-    HitTestingTreeNode* aNextSibling, TreeBuildingState& aState) {
+    HitTestingTreeNode* aNextSibling, TreeBuildingState& aState,
+    wr::RenderRoot aRenderRoot) {
   bool needsApzc = true;
   if (!aMetrics.IsScrollable()) {
     needsApzc = false;
   }
 
   // XXX: As a future optimization we can probably stick these things on the
   // TreeBuildingState, and update them as we change layers id during the
   // traversal
@@ -984,17 +1007,17 @@ HitTestingTreeNode* APZCTreeManager::Pre
     }
 
     // The APZC we get off the layer may have been destroyed previously if the
     // layer was inactive or omitted from the layer tree for whatever reason
     // from a layers update. If it later comes back it will have a reference to
     // a destroyed APZC and so we need to throw that out and make a new one.
     bool newApzc = (apzc == nullptr || apzc->IsDestroyed());
     if (newApzc) {
-      apzc = NewAPZCInstance(aLayersId, geckoContentController);
+      apzc = NewAPZCInstance(aLayersId, geckoContentController, aRenderRoot);
       apzc->SetCompositorController(aState.mCompositorController.get());
       if (crossProcessSharingController) {
         apzc->SetMetricsSharingController(crossProcessSharingController);
       } else {
         apzc->SetMetricsSharingController(
             aState.mInProcessSharingController.get());
       }
       MOZ_ASSERT(node == nullptr);
@@ -1015,17 +1038,17 @@ HitTestingTreeNode* APZCTreeManager::Pre
       aState.mZoomAnimationId = Nothing();
     }
 
     APZCTM_LOG(
         "Using APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64 "\n",
         apzc, aLayer.GetLayer(), uint64_t(aLayersId), aMetrics.GetScrollId());
 
     apzc->NotifyLayersUpdated(aLayer.Metadata(), aState.mIsFirstPaint,
-                              aLayersId == aState.mOriginatingLayersId);
+                              WRRootId(aLayersId, aRenderRoot) == aState.mOriginatingWrRootId);
 
     // Since this is the first time we are encountering an APZC with this guid,
     // the node holding it must be the primary holder. It may be newly-created
     // or not, depending on whether it went through the newApzc branch above.
     MOZ_ASSERT(node->IsPrimaryHolder() && node->GetApzc() &&
                node->GetApzc()->Matches(guid));
 
     Maybe<ParentLayerIntRegion> clipRegion =
@@ -1043,17 +1066,17 @@ HitTestingTreeNode* APZCTreeManager::Pre
     AttachNodeToTree(node, aParent, aNextSibling);
 
     // For testing, log the parent scroll id of every APZC that has a
     // parent. This allows test code to reconstruct the APZC tree.
     // Note that we currently only do this for APZCs in the layer tree
     // that originated the update, because the only identifying information
     // we are logging about APZCs is the scroll id, and otherwise we could
     // confuse APZCs from different layer trees with the same scroll id.
-    if (aLayersId == aState.mOriginatingLayersId) {
+    if (aLayersId == aState.mOriginatingWrRootId.mLayersId) {
       if (apzc->HasNoParentWithSameLayersId()) {
         aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
                                         "hasNoParentWithSameLayersId", true);
       } else {
         MOZ_ASSERT(apzc->GetParent());
         aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
                                         "parentScrollId",
                                         apzc->GetParent()->GetGuid().mScrollId);
@@ -2071,85 +2094,88 @@ void APZCTreeManager::ProcessDynamicTool
 }
 
 void APZCTreeManager::SetKeyboardMap(const KeyboardMap& aKeyboardMap) {
   APZThreadUtils::AssertOnControllerThread();
 
   mKeyboardMap = aKeyboardMap;
 }
 
-void APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
+void APZCTreeManager::ZoomToRect(const SLGuidAndRenderRoot& aGuid,
                                  const CSSRect& aRect, const uint32_t aFlags) {
   // We could probably move this to run on the updater thread if needed, but
   // either way we should restrict it to a single thread. For now let's use the
   // controller thread.
   APZThreadUtils::AssertOnControllerThread();
 
-  RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
+  RefPtr<AsyncPanZoomController> apzc =
+      GetTargetAPZC(aGuid.mScrollableLayerGuid);
   if (apzc) {
     apzc->ZoomToRect(aRect, aFlags);
   }
 }
 
 void APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId,
                                                 bool aPreventDefault) {
   APZThreadUtils::AssertOnControllerThread();
 
   mInputQueue->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
 }
 
 void APZCTreeManager::SetTargetAPZC(
-    uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) {
+    uint64_t aInputBlockId, const nsTArray<SLGuidAndRenderRoot>& aTargets) {
   APZThreadUtils::AssertOnControllerThread();
 
   RefPtr<AsyncPanZoomController> target = nullptr;
   if (aTargets.Length() > 0) {
-    target = GetTargetAPZC(aTargets[0]);
+    target = GetTargetAPZC(aTargets[0].mScrollableLayerGuid);
   }
   for (size_t i = 1; i < aTargets.Length(); i++) {
-    RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]);
+    RefPtr<AsyncPanZoomController> apzc =
+        GetTargetAPZC(aTargets[i].mScrollableLayerGuid);
     target = GetZoomableTarget(target, apzc);
   }
   mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
 }
 
 void APZCTreeManager::UpdateZoomConstraints(
-    const ScrollableLayerGuid& aGuid,
+    const SLGuidAndRenderRoot& aGuid,
     const Maybe<ZoomConstraints>& aConstraints) {
   if (!GetUpdater()->IsUpdaterThread()) {
     // This can happen if we're in the UI process and got a call directly from
     // nsBaseWidget or from a content process over PAPZCTreeManager. In that
     // case we get this call on the compositor thread, which may be different
     // from the updater thread. It can also happen in the GPU process if that is
     // enabled, since the call will go over PAPZCTreeManager and arrive on the
     // compositor thread in the GPU process.
     GetUpdater()->RunOnUpdaterThread(
-        aGuid.mLayersId,
-        NewRunnableMethod<ScrollableLayerGuid, Maybe<ZoomConstraints>>(
+        UpdaterQueueSelector(aGuid.GetWRRootId()),
+        NewRunnableMethod<SLGuidAndRenderRoot, Maybe<ZoomConstraints>>(
             "APZCTreeManager::UpdateZoomConstraints", this,
             &APZCTreeManager::UpdateZoomConstraints, aGuid, aConstraints));
     return;
   }
 
   AssertOnUpdaterThread();
 
+  ScrollableLayerGuid guid = aGuid.mScrollableLayerGuid;
   RecursiveMutexAutoLock lock(mTreeLock);
-  RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
+  RefPtr<HitTestingTreeNode> node = GetTargetNode(guid, nullptr);
   MOZ_ASSERT(!node || node->GetApzc());  // any node returned must have an APZC
 
   // Propagate the zoom constraints down to the subtree, stopping at APZCs
   // which have their own zoom constraints or are in a different layers id.
   if (aConstraints) {
     APZCTM_LOG("Recording constraints %s for guid %s\n",
                Stringify(aConstraints.value()).c_str(),
-               Stringify(aGuid).c_str());
-    mZoomConstraints[aGuid] = aConstraints.ref();
+               Stringify(guid).c_str());
+    mZoomConstraints[guid] = aConstraints.ref();
   } else {
-    APZCTM_LOG("Removing constraints for guid %s\n", Stringify(aGuid).c_str());
-    mZoomConstraints.erase(aGuid);
+    APZCTM_LOG("Removing constraints for guid %s\n", Stringify(guid).c_str());
+    mZoomConstraints.erase(guid);
   }
   if (node && aConstraints) {
     ForEachNode<ReverseIterator>(
         node.get(), [&aConstraints, &node, this](HitTestingTreeNode* aNode) {
           if (aNode != node) {
             if (AsyncPanZoomController* childApzc = aNode->GetApzc()) {
               // We can have subtrees with their own zoom constraints or
               // separate layers id - leave these alone.
@@ -2194,23 +2220,16 @@ void APZCTreeManager::FlushRepaintsToCle
   ForEachNode<ReverseIterator>(mRootNode.get(), [](HitTestingTreeNode* aNode) {
     if (aNode->IsPrimaryHolder()) {
       MOZ_ASSERT(aNode->GetApzc());
       aNode->GetApzc()->FlushRepaintForNewInputBlock();
     }
   });
 }
 
-void APZCTreeManager::CancelAnimation(const ScrollableLayerGuid& aGuid) {
-  RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
-  if (apzc) {
-    apzc->CancelAnimation();
-  }
-}
-
 void APZCTreeManager::AdjustScrollForSurfaceShift(const ScreenPoint& aShift) {
   RecursiveMutexAutoLock lock(mTreeLock);
   RefPtr<AsyncPanZoomController> apzc = FindRootContentOrRootApzc();
   if (apzc) {
     apzc->AdjustScrollForSurfaceShift(aShift);
   }
 }
 
@@ -2519,17 +2538,17 @@ already_AddRefed<AsyncPanZoomController>
 
 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetAPZCAtPointWR(
     const ScreenPoint& aHitTestPoint, CompositorHitTestInfo* aOutHitResult,
     LayersId* aOutLayersId, 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;
@@ -3166,22 +3185,36 @@ LayerToParentLayerMatrix4x4 APZCTreeMana
                 scrollTargetNode->IsAncestorOf(aNode), nullptr);
           });
     }
   }
   // Otherwise, the node does not have an async transform.
   return aNode->GetTransform() * AsyncTransformMatrix();
 }
 
-already_AddRefed<wr::WebRenderAPI> APZCTreeManager::GetWebRenderAPI() const {
+already_AddRefed<wr::WebRenderAPI> APZCTreeManager::GetWebRenderAPI(
+    wr::RenderRoot aRenderRoot) const {
   RefPtr<wr::WebRenderAPI> api;
   CompositorBridgeParent::CallWithIndirectShadowTree(
       mRootLayersId, [&](LayerTreeState& aState) -> void {
         if (aState.mWrBridge) {
-          api = aState.mWrBridge->GetWebRenderAPI();
+          api = aState.mWrBridge->GetWebRenderAPI(aRenderRoot);
+        }
+      });
+  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();
 }
 
 /*static*/
 already_AddRefed<GeckoContentController> APZCTreeManager::GetContentController(
     LayersId aLayersId) {
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -14,24 +14,24 @@
 #include "mozilla/Assertions.h"  // for MOZ_ASSERT_HELPER2
 #include "mozilla/gfx/CompositorHitTestInfo.h"
 #include "mozilla/gfx/Logging.h"              // for gfx::TreeLog
 #include "mozilla/gfx/Matrix.h"               // for Matrix4x4
 #include "mozilla/layers/APZInputBridge.h"    // for APZInputBridge
 #include "mozilla/layers/APZTestData.h"       // for APZTestData
 #include "mozilla/layers/IAPZCTreeManager.h"  // for IAPZCTreeManager
 #include "mozilla/layers/LayersTypes.h"
-#include "mozilla/layers/KeyboardMap.h"      // for KeyboardMap
-#include "mozilla/layers/TouchCounter.h"     // for TouchCounter
+#include "mozilla/layers/KeyboardMap.h"   // for KeyboardMap
+#include "mozilla/layers/TouchCounter.h"  // for TouchCounter
 #include "mozilla/layers/ZoomConstraints.h"  // for ZoomConstraints
-#include "mozilla/RecursiveMutex.h"          // for RecursiveMutex
-#include "mozilla/RefPtr.h"                  // for RefPtr
-#include "mozilla/TimeStamp.h"               // for mozilla::TimeStamp
-#include "mozilla/UniquePtr.h"               // for UniquePtr
-#include "nsCOMPtr.h"                        // for already_AddRefed
+#include "mozilla/RecursiveMutex.h"       // for RecursiveMutex
+#include "mozilla/RefPtr.h"               // for RefPtr
+#include "mozilla/TimeStamp.h"            // for mozilla::TimeStamp
+#include "mozilla/UniquePtr.h"            // for UniquePtr
+#include "nsCOMPtr.h"                     // for already_AddRefed
 
 #if defined(MOZ_WIDGET_ANDROID)
 #  include "mozilla/layers/AndroidDynamicToolbarAnimator.h"
 #endif  // defined(MOZ_WIDGET_ANDROID)
 
 namespace mozilla {
 class MultiTouchInput;
 
@@ -204,29 +204,34 @@ class APZCTreeManager : public IAPZCTree
   /**
    * Same as the above UpdateHitTestingTree, except slightly modified to take
    * the scrolling data passed over PWebRenderBridge instead of the raw layer
    * tree. This version is used when WebRender is enabled because we don't have
    * shadow layers in that scenario.
    */
   void UpdateHitTestingTree(LayersId aRootLayerTreeId,
                             const WebRenderScrollDataWrapper& aScrollWrapper,
-                            bool aIsFirstPaint, LayersId aOriginatingLayersId,
+                            bool aIsFirstPaint, WRRootId aOriginatingWrRootId,
                             uint32_t aPaintSequenceNumber);
 
   /**
    * Called when webrender is enabled, from the sampler thread. This function
    * populates the provided transaction with any async scroll offsets needed.
    * It also advances APZ animations to the specified sample time, and requests
    * another composite if there are still active animations.
    * In effect it is the webrender equivalent of (part of) the code in
    * AsyncCompositionManager.
+   * In the WebRender world a single "layer tree" might get split into multiple
+   * render roots; the aRenderRoot argument indicates which render root we are
+   * sampling in this call. The transaction should only be updated with samples
+   * from APZC instances in that render root.
    */
   void SampleForWebRender(wr::TransactionWrapper& aTxn,
-                          const TimeStamp& aSampleTime);
+                          const TimeStamp& aSampleTime,
+                          wr::RenderRoot aRenderRoot);
 
   /**
    * General handler for incoming input events. Manipulates the frame metrics
    * based on what type of input it is. For example, a PinchGestureEvent will
    * cause scaling. This should only be called externally to this class, and
    * must be called on the controller thread.
    *
    * This function transforms |aEvent| to have its coordinates in DOM space.
@@ -267,17 +272,17 @@ class APZCTreeManager : public IAPZCTree
   void SetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
 
   /**
    * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom
    * in. The actual animation is done on the sampler thread after being set
    * up. |aRect| must be given in CSS pixels, relative to the document.
    * |aFlags| is a combination of the ZoomToRectBehavior enum values.
    */
-  void ZoomToRect(const ScrollableLayerGuid& aGuid, const CSSRect& aRect,
+  void ZoomToRect(const SLGuidAndRenderRoot& aGuid, const CSSRect& aRect,
                   const uint32_t aFlags = DEFAULT_BEHAVIOR) override;
 
   /**
    * If we have touch listeners, this should always be called when we know
    * definitively whether or not content has preventDefaulted any touch events
    * that have come in. If |aPreventDefault| is true, any touch events in the
    * queue will be discarded. This function must be called on the controller
    * thread.
@@ -299,33 +304,28 @@ class APZCTreeManager : public IAPZCTree
    *       and StartScrollbarDrag() will be called, and the calls may happen
    *       in either order. That's fine - whichever arrives first will confirm
    *       the block, and StartScrollbarDrag() will fill in the drag metrics.
    *       If the block is confirmed before we have drag metrics, some events
    *       in the drag block may be handled as no-ops until the drag metrics
    *       arrive.
    */
   void SetTargetAPZC(uint64_t aInputBlockId,
-                     const nsTArray<ScrollableLayerGuid>& aTargets) override;
+                     const nsTArray<SLGuidAndRenderRoot>& aTargets) override;
 
   /**
    * Updates any zoom constraints contained in the <meta name="viewport"> tag.
    * If the |aConstraints| is Nothing() then previously-provided constraints for
    * the given |aGuid| are cleared.
    */
   void UpdateZoomConstraints(
-      const ScrollableLayerGuid& aGuid,
+      const SLGuidAndRenderRoot& aGuid,
       const Maybe<ZoomConstraints>& aConstraints) override;
 
   /**
-   * Cancels any currently running animation.
-   */
-  void CancelAnimation(const ScrollableLayerGuid& aGuid);
-
-  /**
    * Adjusts the root APZC to compensate for a shift in the surface. See the
    * documentation on AsyncPanZoomController::AdjustScrollForSurfaceShift for
    * some more details. This is only currently needed due to surface shifts
    * caused by the dynamic toolbar on Android.
    */
   void AdjustScrollForSurfaceShift(const ScreenPoint& aShift);
 
   /**
@@ -458,23 +458,23 @@ class APZCTreeManager : public IAPZCTree
    * |aHandoffState.mVelocity| that was not consumed by APZCs in the
    * handoff chain doing flings.
    * The caller can use this value to determine whether it should consume
    * the excess velocity by going into overscroll.
    */
   ParentLayerPoint DispatchFling(AsyncPanZoomController* aApzc,
                                  const FlingHandoffState& aHandoffState);
 
-  void StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+  void StartScrollbarDrag(const SLGuidAndRenderRoot& aGuid,
                           const AsyncDragMetrics& aDragMetrics) override;
 
-  bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
+  bool StartAutoscroll(const SLGuidAndRenderRoot& aGuid,
                        const ScreenPoint& aAnchorLocation) override;
 
-  void StopAutoscroll(const ScrollableLayerGuid& aGuid) override;
+  void StopAutoscroll(const SLGuidAndRenderRoot& aGuid) override;
 
   /*
    * Build the chain of APZCs that will handle overscroll for a pan starting at
    * |aInitialTarget|.
    */
   RefPtr<const OverscrollHandoffChain> BuildOverscrollHandoffChain(
       const RefPtr<AsyncPanZoomController>& aInitialTarget);
 
@@ -544,20 +544,30 @@ class APZCTreeManager : public IAPZCTree
    */
   static void FlushApzRepaints(LayersId aLayersId);
 
   // 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;
+  // Returns a pointer to the WebRenderAPI this APZCTreeManager is for, for
+  // the provided RenderRoot (since an APZCTreeManager can cover multiple
+  // RenderRoots). This might be null (for example, if WebRender is not
+  // enabled).
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI(
+      wr::RenderRoot aRenderRoot) const;
+
+  // Returns a pointer to the root WebRenderAPI for the RenderRoot that owns
+  // the given point. For example, if aPoint is in the content area and
+  // RenderRoot splitting is enabled, this will return the WebRenderAPI for
+  // the Content RenderRoot.
+  // This might be null (for example, if WebRender is not enabled).
+  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;
 
@@ -566,17 +576,18 @@ class APZCTreeManager : public IAPZCTree
   // functions to the world.
  private:
   friend class APZUpdater;
   void LockTree();
   void UnlockTree();
 
   // Protected hooks for gtests subclass
   virtual AsyncPanZoomController* NewAPZCInstance(
-      LayersId aLayersId, GeckoContentController* aController);
+      LayersId aLayersId, GeckoContentController* aController,
+      wr::RenderRoot aRenderRoot);
 
  public:
   // Public hooks for gtests subclass
   virtual TimeStamp GetFrameTime();
 
  public:
   /* Some helper functions to find an APZC given some identifying input. These
      functions lock the tree of APZCs while they find the right one, and then
@@ -620,17 +631,17 @@ class APZCTreeManager : public IAPZCTree
  private:
   typedef bool (*GuidComparator)(const ScrollableLayerGuid&,
                                  const ScrollableLayerGuid&);
 
   /* Helpers */
   template <class ScrollNode>
   void UpdateHitTestingTreeImpl(LayersId aRootLayerTreeId,
                                 const ScrollNode& aRoot, bool aIsFirstPaint,
-                                LayersId aOriginatingLayersId,
+                                WRRootId aOriginatingWrRootId,
                                 uint32_t aPaintSequenceNumber);
 
   void AttachNodeToTree(HitTestingTreeNode* aNode, HitTestingTreeNode* aParent,
                         HitTestingTreeNode* aNextSibling);
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(
       const ScrollableLayerGuid& aGuid);
   already_AddRefed<HitTestingTreeNode> GetTargetNode(
       const ScrollableLayerGuid& aGuid, GuidComparator aComparator) const;
@@ -723,17 +734,18 @@ class APZCTreeManager : public IAPZCTree
   already_AddRefed<HitTestingTreeNode> RecycleOrCreateNode(
       const RecursiveMutexAutoLock& aProofOfTreeLock, TreeBuildingState& aState,
       AsyncPanZoomController* aApzc, LayersId aLayersId);
   template <class ScrollNode>
   HitTestingTreeNode* PrepareNodeForLayer(
       const RecursiveMutexAutoLock& aProofOfTreeLock, const ScrollNode& aLayer,
       const FrameMetrics& aMetrics, LayersId aLayersId,
       const AncestorTransform& aAncestorTransform, HitTestingTreeNode* aParent,
-      HitTestingTreeNode* aNextSibling, TreeBuildingState& aState);
+      HitTestingTreeNode* aNextSibling, TreeBuildingState& aState,
+      wr::RenderRoot aRenderRoot);
 
   template <class ScrollNode>
   void PrintAPZCInfo(const ScrollNode& aLayer,
                      const AsyncPanZoomController* apzc);
 
   void NotifyScrollbarDragInitiated(uint64_t aDragBlockId,
                                     const ScrollableLayerGuid& aGuid,
                                     ScrollDirection aDirection) const;
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -61,42 +61,44 @@ void APZSampler::SetSamplerThread(const 
   if (RefPtr<APZSampler> sampler = GetSampler(aWindowId)) {
     MutexAutoLock lock(sampler->mThreadIdLock);
     sampler->mSamplerThreadId = Some(PlatformThread::CurrentId());
   }
 }
 
 /*static*/
 void APZSampler::SampleForWebRender(const wr::WrWindowId& aWindowId,
-                                    wr::Transaction* aTransaction) {
+                                    wr::Transaction* aTransaction,
+                                    const wr::DocumentId& aRenderRootId) {
   if (RefPtr<APZSampler> sampler = GetSampler(aWindowId)) {
     wr::TransactionWrapper txn(aTransaction);
-    sampler->SampleForWebRender(txn);
+    sampler->SampleForWebRender(txn, wr::RenderRootFromId(aRenderRootId));
   }
 }
 
 void APZSampler::SetSampleTime(const TimeStamp& aSampleTime) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MutexAutoLock lock(mSampleTimeLock);
   mSampleTime = aSampleTime;
 }
 
-void APZSampler::SampleForWebRender(wr::TransactionWrapper& aTxn) {
+void APZSampler::SampleForWebRender(wr::TransactionWrapper& aTxn,
+                                    wr::RenderRoot aRenderRoot) {
   AssertOnSamplerThread();
   TimeStamp sampleTime;
   {  // scope lock
     MutexAutoLock lock(mSampleTimeLock);
 
     // If mSampleTime is null we're in a startup phase where the
     // WebRenderBridgeParent hasn't yet provided us with a sample time.
     // If we're that early there probably aren't any APZ animations happening
     // anyway, so using Timestamp::Now() should be fine.
     sampleTime = mSampleTime.IsNull() ? TimeStamp::Now() : mSampleTime;
   }
-  mApz->SampleForWebRender(aTxn, sampleTime);
+  mApz->SampleForWebRender(aTxn, sampleTime, aRenderRoot);
 }
 
 bool APZSampler::SampleAnimations(const LayerMetricsWrapper& aLayer,
                                   const TimeStamp& aSampleTime) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnSamplerThread();
 
   // TODO: eventually we can drop the aLayer argument and just walk the APZ
@@ -265,13 +267,15 @@ already_AddRefed<APZSampler> APZSampler:
 }  // namespace layers
 }  // namespace mozilla
 
 void apz_register_sampler(mozilla::wr::WrWindowId aWindowId) {
   mozilla::layers::APZSampler::SetSamplerThread(aWindowId);
 }
 
 void apz_sample_transforms(mozilla::wr::WrWindowId aWindowId,
-                           mozilla::wr::Transaction* aTransaction) {
-  mozilla::layers::APZSampler::SampleForWebRender(aWindowId, aTransaction);
+                           mozilla::wr::Transaction* aTransaction,
+                           mozilla::wr::DocumentId aDocumentId) {
+  mozilla::layers::APZSampler::SampleForWebRender(aWindowId, aTransaction,
+                                                  aDocumentId);
 }
 
 void apz_deregister_sampler(mozilla::wr::WrWindowId aWindowId) {}
--- a/gfx/layers/apz/src/APZUpdater.cpp
+++ b/gfx/layers/apz/src/APZUpdater.cpp
@@ -84,26 +84,28 @@ void APZUpdater::CompleteSceneSwap(const
     // This should only happen in cases where PrepareForSceneSwap also got a
     // null updater. No updater-thread tasks get run between PrepareForSceneSwap
     // and this function, so there is no opportunity for the updater mapping
     // to have gotten removed from sWindowIdMap in between the two calls.
     return;
   }
 
   for (uintptr_t i = 0; i < aInfo.removed_pipelines.length; i++) {
-    LayersId layersId = wr::AsLayersId(aInfo.removed_pipelines.data[i]);
+    WRRootId layersId = WRRootId(aInfo.removed_pipelines.data[i].pipeline_id,
+                                 aInfo.removed_pipelines.data[i].document_id);
     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();
   }
   for (uintptr_t i = 0; i < aInfo.epochs.length; i++) {
-    LayersId layersId = wr::AsLayersId(aInfo.epochs.data[i].pipeline_id);
+    WRRootId layersId = WRRootId(aInfo.epochs.data[i].pipeline_id,
+                                 aInfo.epochs.data[i].document_id);
     updater->mEpochData[layersId].mBuilt = Some(aInfo.epochs.data[i].epoch);
   }
 
   // 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
@@ -126,206 +128,215 @@ void APZUpdater::ProcessPendingTasks(con
   if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
     updater->ProcessQueue();
   }
 }
 
 void APZUpdater::ClearTree(LayersId aRootLayersId) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
-  RunOnUpdaterThread(aRootLayersId,
-                     NS_NewRunnableFunction("APZUpdater::ClearTree", [=]() {
-                       self->mApz->ClearTree();
+  RunOnUpdaterThread(
+      UpdaterQueueSelector(aRootLayersId, wr::RenderRoot::Default),
+      NS_NewRunnableFunction("APZUpdater::ClearTree", [=]() {
+        self->mApz->ClearTree();
 
-                       // Once ClearTree is called on the APZCTreeManager, we
-                       // are in a shutdown phase. After this point it's ok if
-                       // WebRender cannot get a hold of the updater via the
-                       // window id, and it's a good point to remove the mapping
-                       // and avoid leaving a dangling pointer to this object.
-                       StaticMutexAutoLock lock(sWindowIdLock);
-                       if (self->mWindowId) {
-                         MOZ_ASSERT(sWindowIdMap);
-                         sWindowIdMap->erase(wr::AsUint64(*(self->mWindowId)));
-                       }
-                     }));
+        // Once ClearTree is called on the APZCTreeManager, we
+        // are in a shutdown phase. After this point it's ok if
+        // WebRender cannot get a hold of the updater via the
+        // window id, and it's a good point to remove the mapping
+        // and avoid leaving a dangling pointer to this object.
+        StaticMutexAutoLock lock(sWindowIdLock);
+        if (self->mWindowId) {
+          MOZ_ASSERT(sWindowIdMap);
+          sWindowIdMap->erase(wr::AsUint64(*(self->mWindowId)));
+        }
+      }));
 }
 
 void APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId,
-                                  LayersId aOriginatingLayersId,
+                                  WRRootId aOriginatingWrRootId,
                                   const FocusTarget& aFocusTarget) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RunOnUpdaterThread(aOriginatingLayersId,
+  UpdaterQueueSelector selector(aOriginatingWrRootId);
+  if (aFocusTarget.mData.is<FocusTarget::ScrollTargets>()) {
+    const FocusTarget::ScrollTargets& targets = aFocusTarget.mData.as<FocusTarget::ScrollTargets>();
+    selector.mRenderRoots += targets.mHorizontalRenderRoot;
+    selector.mRenderRoots += targets.mVerticalRenderRoot;
+  }
+  RunOnUpdaterThread(selector,
                      NewRunnableMethod<LayersId, LayersId, FocusTarget>(
                          "APZUpdater::UpdateFocusState", mApz,
                          &APZCTreeManager::UpdateFocusState, aRootLayerTreeId,
-                         aOriginatingLayersId, aFocusTarget));
+                         aOriginatingWrRootId.mLayersId, aFocusTarget));
 }
 
 void APZUpdater::UpdateHitTestingTree(LayersId aRootLayerTreeId, Layer* aRoot,
                                       bool aIsFirstPaint,
-                                      LayersId aOriginatingLayersId,
+                                      LayersId aOriginatingWrRootId,
                                       uint32_t aPaintSequenceNumber) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnUpdaterThread();
   mApz->UpdateHitTestingTree(aRootLayerTreeId, aRoot, aIsFirstPaint,
-                             aOriginatingLayersId, aPaintSequenceNumber);
+                             aOriginatingWrRootId, aPaintSequenceNumber);
 }
 
 void APZUpdater::UpdateScrollDataAndTreeState(
-    LayersId aRootLayerTreeId, LayersId aOriginatingLayersId,
+    WRRootId aRootLayerTreeId, WRRootId aOriginatingWrRootId,
     const wr::Epoch& aEpoch, WebRenderScrollData&& aScrollData) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
   // Insert an epoch requirement update into the queue, so that
   // tasks inserted into the queue after this point only get executed
   // once the epoch requirement is satisfied. In particular, the
   // UpdateHitTestingTree call below needs to wait until the epoch requirement
   // is satisfied, which is why it is a separate task in the queue.
   RunOnUpdaterThread(
-      aOriginatingLayersId,
+      UpdaterQueueSelector(aOriginatingWrRootId),
       NS_NewRunnableFunction("APZUpdater::UpdateEpochRequirement", [=]() {
-        if (aRootLayerTreeId == aOriginatingLayersId) {
-          self->mEpochData[aOriginatingLayersId].mIsRoot = true;
+        if (aRootLayerTreeId == aOriginatingWrRootId) {
+          self->mEpochData[aOriginatingWrRootId].mIsRoot = true;
         }
-        self->mEpochData[aOriginatingLayersId].mRequired = aEpoch;
+        self->mEpochData[aOriginatingWrRootId].mRequired = aEpoch;
       }));
   RunOnUpdaterThread(
-      aOriginatingLayersId,
+      UpdaterQueueSelector(aOriginatingWrRootId),
       NS_NewRunnableFunction(
           "APZUpdater::UpdateHitTestingTree",
           [=, aScrollData = std::move(aScrollData)]() {
-            self->mApz->UpdateFocusState(aRootLayerTreeId, aOriginatingLayersId,
-                                         aScrollData.GetFocusTarget());
-
-            self->mScrollData[aOriginatingLayersId] = aScrollData;
+            self->mScrollData[aOriginatingWrRootId] = aScrollData;
             auto root = self->mScrollData.find(aRootLayerTreeId);
             if (root == self->mScrollData.end()) {
               return;
             }
             self->mApz->UpdateHitTestingTree(
-                aRootLayerTreeId,
-                WebRenderScrollDataWrapper(*self, &(root->second)),
-                aScrollData.IsFirstPaint(), aOriginatingLayersId,
+                aRootLayerTreeId.mLayersId,
+                WebRenderScrollDataWrapper(*self, aRootLayerTreeId,
+                                           &(root->second)),
+                aScrollData.IsFirstPaint(), aOriginatingWrRootId,
                 aScrollData.GetPaintSequenceNumber());
           }));
 }
 
-void APZUpdater::UpdateScrollOffsets(LayersId aRootLayerTreeId,
-                                     LayersId aOriginatingLayersId,
+void APZUpdater::UpdateScrollOffsets(WRRootId aRootLayerTreeId,
+                                     WRRootId aOriginatingWrRootId,
                                      ScrollUpdatesMap&& aUpdates,
                                      uint32_t aPaintSequenceNumber) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
-  RunOnUpdaterThread(
-      aOriginatingLayersId,
-      NS_NewRunnableFunction(
-          "APZUpdater::UpdateScrollOffsets",
-          [=, updates = std::move(aUpdates)]() {
-            self->mScrollData[aOriginatingLayersId].ApplyUpdates(
-                updates, aPaintSequenceNumber);
-            auto root = self->mScrollData.find(aRootLayerTreeId);
-            if (root == self->mScrollData.end()) {
-              return;
-            }
-            self->mApz->UpdateHitTestingTree(
-                aRootLayerTreeId,
-                WebRenderScrollDataWrapper(*self, &(root->second)),
-                /*isFirstPaint*/ false, aOriginatingLayersId,
-                aPaintSequenceNumber);
-          }));
+  RunOnUpdaterThread(UpdaterQueueSelector(aOriginatingWrRootId),
+                     NS_NewRunnableFunction(
+                         "APZUpdater::UpdateScrollOffsets",
+                         [=, updates = std::move(aUpdates)]() {
+                           self->mScrollData[aOriginatingWrRootId].ApplyUpdates(
+                               updates, aPaintSequenceNumber);
+                           auto root = self->mScrollData.find(aRootLayerTreeId);
+                           if (root == self->mScrollData.end()) {
+                             return;
+                           }
+                           self->mApz->UpdateHitTestingTree(
+                               aRootLayerTreeId.mLayersId,
+                               WebRenderScrollDataWrapper(
+                                   *self, aRootLayerTreeId, &(root->second)),
+                               /*isFirstPaint*/ false,
+                               aOriginatingWrRootId,
+                               aPaintSequenceNumber);
+                         }));
 }
 
-void APZUpdater::NotifyLayerTreeAdopted(LayersId aLayersId,
+void APZUpdater::NotifyLayerTreeAdopted(WRRootId aWrRootId,
                                         const RefPtr<APZUpdater>& aOldUpdater) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-  RunOnUpdaterThread(aLayersId,
-                     NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
-                         "APZUpdater::NotifyLayerTreeAdopted", mApz,
-                         &APZCTreeManager::NotifyLayerTreeAdopted, aLayersId,
-                         aOldUpdater ? aOldUpdater->mApz : nullptr));
+  RunOnUpdaterThread(
+      UpdaterQueueSelector(aWrRootId),
+      NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
+          "APZUpdater::NotifyLayerTreeAdopted", mApz,
+          &APZCTreeManager::NotifyLayerTreeAdopted, aWrRootId.mLayersId,
+          aOldUpdater ? aOldUpdater->mApz : nullptr));
 }
 
-void APZUpdater::NotifyLayerTreeRemoved(LayersId aLayersId) {
+void APZUpdater::NotifyLayerTreeRemoved(WRRootId aWrRootId) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
   RunOnUpdaterThread(
-      aLayersId,
+      UpdaterQueueSelector(aWrRootId),
       NS_NewRunnableFunction("APZUpdater::NotifyLayerTreeRemoved", [=]() {
-        self->mEpochData.erase(aLayersId);
-        self->mScrollData.erase(aLayersId);
-        self->mApz->NotifyLayerTreeRemoved(aLayersId);
+        self->mEpochData.erase(aWrRootId);
+        self->mScrollData.erase(aWrRootId);
+        self->mApz->NotifyLayerTreeRemoved(aWrRootId.mLayersId);
       }));
 }
 
-bool APZUpdater::GetAPZTestData(LayersId aLayersId, APZTestData* aOutData) {
+bool APZUpdater::GetAPZTestData(WRRootId aWrRootId, APZTestData* aOutData) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   RefPtr<APZCTreeManager> apz = mApz;
   bool ret = false;
   SynchronousTask waiter("APZUpdater::GetAPZTestData");
   RunOnUpdaterThread(
-      aLayersId, NS_NewRunnableFunction("APZUpdater::GetAPZTestData", [&]() {
+      UpdaterQueueSelector(aWrRootId),
+      NS_NewRunnableFunction("APZUpdater::GetAPZTestData", [&]() {
         AutoCompleteTask notifier(&waiter);
-        ret = apz->GetAPZTestData(aLayersId, aOutData);
+        ret = apz->GetAPZTestData(aWrRootId.mLayersId, aOutData);
       }));
 
   // Wait until the task posted above has run and populated aOutData and ret
   waiter.Wait();
 
   return ret;
 }
 
 void APZUpdater::SetTestAsyncScrollOffset(
-    LayersId aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+    WRRootId aWrRootId, const ScrollableLayerGuid::ViewID& aScrollId,
     const CSSPoint& aOffset) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZCTreeManager> apz = mApz;
   RunOnUpdaterThread(
-      aLayersId,
+      UpdaterQueueSelector(aWrRootId),
       NS_NewRunnableFunction("APZUpdater::SetTestAsyncScrollOffset", [=]() {
         RefPtr<AsyncPanZoomController> apzc =
-            apz->GetTargetAPZC(aLayersId, aScrollId);
+            apz->GetTargetAPZC(aWrRootId.mLayersId, aScrollId);
         if (apzc) {
           apzc->SetTestAsyncScrollOffset(aOffset);
         } else {
           NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
         }
       }));
 }
 
-void APZUpdater::SetTestAsyncZoom(LayersId aLayersId,
+void APZUpdater::SetTestAsyncZoom(WRRootId aWrRootId,
                                   const ScrollableLayerGuid::ViewID& aScrollId,
                                   const LayerToParentLayerScale& aZoom) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZCTreeManager> apz = mApz;
   RunOnUpdaterThread(
-      aLayersId, NS_NewRunnableFunction("APZUpdater::SetTestAsyncZoom", [=]() {
+      UpdaterQueueSelector(aWrRootId),
+      NS_NewRunnableFunction("APZUpdater::SetTestAsyncZoom", [=]() {
         RefPtr<AsyncPanZoomController> apzc =
-            apz->GetTargetAPZC(aLayersId, aScrollId);
+            apz->GetTargetAPZC(aWrRootId.mLayersId, aScrollId);
         if (apzc) {
           apzc->SetTestAsyncZoom(aZoom);
         } else {
           NS_WARNING("Unable to find APZC in SetTestAsyncZoom");
         }
       }));
 }
 
-const WebRenderScrollData* APZUpdater::GetScrollData(LayersId aLayersId) const {
+const WebRenderScrollData* APZUpdater::GetScrollData(WRRootId aWrRootId) const {
   AssertOnUpdaterThread();
-  auto it = mScrollData.find(aLayersId);
+  auto it = mScrollData.find(aWrRootId);
   return (it == mScrollData.end() ? nullptr : &(it->second));
 }
 
 void APZUpdater::AssertOnUpdaterThread() const {
   if (APZThreadUtils::GetThreadAssertionsEnabled()) {
     MOZ_ASSERT(IsUpdaterThread());
   }
 }
 
-void APZUpdater::RunOnUpdaterThread(LayersId aLayersId,
+void APZUpdater::RunOnUpdaterThread(UpdaterQueueSelector aSelector,
                                     already_AddRefed<Runnable> aTask) {
   RefPtr<Runnable> task = aTask;
 
   // In the scenario where UsingWebRenderUpdaterThread() is true, this function
   // might get called early (before mUpdaterThreadId is set). In that case
   // IsUpdaterThread() will return false and we'll queue the task onto
   // mUpdaterQueue. This is fine; the task is still guaranteed to run (barring
   // catastrophic failure) because the WakeSceneBuilder call will still trigger
@@ -340,32 +351,71 @@ void APZUpdater::RunOnUpdaterThread(Laye
     // If the updater thread is a WebRender thread, and we're not on it
     // right now, save the task in the queue. We will run tasks from the queue
     // during the callback from the updater thread, which we trigger by the
     // call to WakeSceneBuilder.
 
     bool sendWakeMessage = true;
     {  // scope lock
       MutexAutoLock lock(mQueueLock);
+      wr::RenderRootSet alreadyWoken;
+
+      // What we're doing here is trying to avoid sending redundant
+      // WakeSceneBuilder messages. If another task exists with our
+      // layersId/renderRoots combination, then we know that either that message
+      // is going to be processed soon, or it's blocked on an epoch update, and
+      // in both of those cases sending a WakeSceneBuilder message won't do
+      // anything, and our message is destined to be blocked behind it. However,
+      // imagine the following queue (assume everything as the same layersId):
+      //
+      // q[0]    (A)
+      // q[1]    (A,B)
+      // q[2] -> (B,C) // This is what we want to put in the queue
+      //
+      // We could go two routes in this case: (I) elide the WakeSceneBuilder
+      // message if *any* of our renderRoots are already present in q, since we
+      // won't go until they're all unblocked, or (II) elide it only if *all* of
+      // our renderRoots are already present in q.
+      //
+      // If we go with (I), then if A needs an epoch update, adding (B,C)
+      // wouldn't send a WakeSceneBuilder message because (A,B) is in the queue
+      // (intersecting on B). But since (A,B) is only blocked on an epoch update
+      // for A, (B,C) *could* run if we sent a wake message. Thus, (I) means:
+      // - Fewer spurious WakeSceneBuilder messages
+      // - Potential for unnecessarily blocking certain messages
+      //
+      // If we went with (II), then (B,C) would run as early as possible, but we
+      // would send WakeSceneBuilder messages for all three items in the queue.
+      // Thus, (II) means:
+      // - More spurious WakeSceneBuilderMessages
+      // - Potential for violating people's ordering assumptions (i.e., that
+      // q[2] would run
+      //   after q[1])
+      //
+      // We're electing to go with option (II), but we might revisit it if/when
+      // we have more than two documents. (Because it doesn't matter much before
+      // then.)
       for (const auto& queuedTask : mUpdaterQueue) {
-        if (queuedTask.mLayersId == aLayersId) {
-          // If there's already a task in the queue with this layers id, then
-          // we must have previously sent a WakeSceneBuilder message (when
-          // adding the first task with this layers id to the queue). Either
-          // that hasn't been fully processed yet, or the layers id is blocked
-          // waiting for an epoch - in either case there's no point in sending
-          // another WakeSceneBuilder message.
-          sendWakeMessage = false;
+        if (queuedTask.mSelector.mLayersId == aSelector.mLayersId) {
+          alreadyWoken +=
+              (queuedTask.mSelector.mRenderRoots & aSelector.mRenderRoots);
           break;
         }
       }
-      mUpdaterQueue.push_back(QueuedTask{aLayersId, task});
+      if (alreadyWoken == aSelector.mRenderRoots) {
+        sendWakeMessage = false;
+      }
+      mUpdaterQueue.push_back(QueuedTask{aSelector, task});
     }
     if (sendWakeMessage) {
-      RefPtr<wr::WebRenderAPI> api = mApz->GetWebRenderAPI();
+      // All the RenderRoots share a single scene builder thread, so we can
+      // just send the message to the default RenderRoot's API instead of
+      // sending one for each unwoken RenderRoot.
+      RefPtr<wr::WebRenderAPI> api =
+          mApz->GetWebRenderAPI(wr::RenderRoot::Default);
       if (api) {
         api->WakeSceneBuilder();
       } else {
         // Not sure if this can happen, but it might be possible. If it does,
         // the task is in the queue, but if we didn't get a WebRenderAPI it
         // might never run, or it might run later if we manage to get a
         // WebRenderAPI later. For now let's just emit a warning, this can
         // probably be upgraded to an assert later.
@@ -390,23 +440,23 @@ bool APZUpdater::IsUpdaterThread() const
     // C++ code on it, and this function is only ever invoked from C++ code),
     // so return false in that scenario.
     MutexAutoLock lock(mThreadIdLock);
     return mUpdaterThreadId && PlatformThread::CurrentId() == *mUpdaterThreadId;
   }
   return CompositorThreadHolder::IsInCompositorThread();
 }
 
-void APZUpdater::RunOnControllerThread(LayersId aLayersId,
+void APZUpdater::RunOnControllerThread(UpdaterQueueSelector aSelector,
                                        already_AddRefed<Runnable> aTask) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   RefPtr<Runnable> task = aTask;
 
-  RunOnUpdaterThread(aLayersId,
+  RunOnUpdaterThread(aSelector,
                      NewRunnableFunction("APZUpdater::RunOnControllerThread",
                                          &APZThreadUtils::RunOnControllerThread,
                                          std::move(task)));
 }
 
 bool APZUpdater::UsingWebRenderUpdaterThread() const {
   return mIsUsingWebRender;
 }
@@ -445,24 +495,32 @@ void APZUpdater::ProcessQueue() {
         std::swap(mUpdaterQueue, blockedTasks);
         break;
       }
       task = mUpdaterQueue.front();
       mUpdaterQueue.pop_front();
     }
 
     // We check the task to see if it is blocked. Note that while this
-    // ProcessQueue function is executing, a particular layers is cannot go
+    // ProcessQueue function is executing, a particular WRRoot id cannot go
     // from blocked to unblocked, because only CompleteSceneSwap can unblock
-    // a layers id, and that also runs on the updater thread. If somehow
-    // a layers id gets unblocked while we're processing the queue, then it
+    // a WRRoot id, and that also runs on the updater thread. If somehow
+    // a WRRoot id gets unblocked while we're processing the queue, then it
     // might result in tasks getting executed out of order.
 
-    auto it = mEpochData.find(task.mLayersId);
-    if (it != mEpochData.end() && it->second.IsBlocked()) {
+    bool blocked = false;
+    for (wr::RenderRoot root : task.mSelector.mRenderRoots) {
+      WRRootId selector = WRRootId(task.mSelector.mLayersId, root);
+      auto it = mEpochData.find(selector);
+      if (it != mEpochData.end() && it->second.IsBlocked()) {
+        blocked = true;
+        break;
+      }
+    }
+    if (blocked) {
       // If this task is blocked, put it into the blockedTasks queue that
       // we will replace mUpdaterQueue with
       blockedTasks.push_back(task);
     } else {
       // Run and discard the task
       task.mRunnable->Run();
     }
   }
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -790,18 +790,20 @@ void AsyncPanZoomController::InitializeG
   gIsHighMemSystem = sysmem >= threshold;
 
   PlatformSpecificState::InitializeGlobalState();
 }
 
 AsyncPanZoomController::AsyncPanZoomController(
     LayersId aLayersId, APZCTreeManager* aTreeManager,
     const RefPtr<InputQueue>& aInputQueue,
-    GeckoContentController* aGeckoContentController, GestureBehavior aGestures)
+    GeckoContentController* aGeckoContentController, wr::RenderRoot aRenderRoot,
+    GestureBehavior aGestures)
     : mLayersId(aLayersId),
+      mRenderRoot(aRenderRoot),
       mGeckoContentController(aGeckoContentController),
       mRefPtrMonitor("RefPtrMonitor"),
       // mTreeManager must be initialized before GetFrameTime() is called
       mTreeManager(aTreeManager),
       mRecursiveMutex("AsyncPanZoomController"),
       mLastContentPaintMetrics(mLastContentPaintMetadata.GetMetrics()),
       mX(this),
       mY(this),
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -41,16 +41,17 @@ class SharedMemoryBasic;
 
 }  // namespace ipc
 
 namespace layers {
 
 class AsyncDragMetrics;
 class APZCTreeManager;
 struct ScrollableLayerGuid;
+struct SLGuidAndRenderRoot;
 class CompositorController;
 class MetricsSharingController;
 class GestureEventListener;
 struct AsyncTransform;
 class AsyncPanZoomAnimation;
 class StackScrollerFlingAnimation;
 template <typename FlingPhysics>
 class GenericFlingAnimation;
@@ -189,16 +190,17 @@ class AsyncPanZoomController {
    * tap has to be to the first tap in order to be counted as part of a
    * multi-tap gesture (double-tap or one-touch-pinch).
    */
   ScreenCoord GetSecondTapTolerance() const;
 
   AsyncPanZoomController(LayersId aLayersId, APZCTreeManager* aTreeManager,
                          const RefPtr<InputQueue>& aInputQueue,
                          GeckoContentController* aController,
+                         wr::RenderRoot aRenderRoot,
                          GestureBehavior aGestures = DEFAULT_GESTURES);
 
   // --------------------------------------------------------------------------
   // These methods must only be called on the gecko thread.
   //
 
   /**
    * Read the various prefs and do any global initialization for all APZC
@@ -891,16 +893,17 @@ class AsyncPanZoomController {
   nsEventStatus GenerateSingleTap(GeckoContentController::TapType aType,
                                   const ScreenIntPoint& aPoint,
                                   mozilla::Modifiers aModifiers);
 
   // Common processing at the end of a touch block.
   void OnTouchEndOrCancel();
 
   LayersId mLayersId;
+  wr::RenderRoot mRenderRoot;
   RefPtr<CompositorController> mCompositorController;
   RefPtr<MetricsSharingController> mMetricsSharingController;
 
   /* Access to the following two fields is protected by the mRefPtrMonitor,
      since they are accessed on the UI thread but can be cleared on the
      updater thread. */
   RefPtr<GeckoContentController> mGeckoContentController;
   RefPtr<GestureEventListener> mGestureEventListener;
@@ -1593,16 +1596,18 @@ class AsyncPanZoomController {
   }
 
   bool GetAsyncTransformAppliedToContent() const {
     return mAsyncTransformAppliedToContent;
   }
 
   LayersId GetLayersId() const { return mLayersId; }
 
+  wr::RenderRoot GetRenderRoot() const { return mRenderRoot; }
+
  private:
   // Extra offset to add to the async scroll position for testing
   CSSPoint mTestAsyncScrollOffset;
   // Extra zoom to include in the aync zoom for testing
   LayerToParentLayerScale mTestAsyncZoom;
   // Flag to track whether or not the APZ transform is not used. This
   // flag is recomputed for every composition frame.
   bool mAsyncTransformAppliedToContent : 1;
--- a/gfx/layers/apz/src/FocusTarget.cpp
+++ b/gfx/layers/apz/src/FocusTarget.cpp
@@ -216,16 +216,29 @@ FocusTarget::FocusTarget(nsIPresShell* a
       presShell->GetScrollableFrameToScrollForContent(selectedContent.get(),
                                                       nsIPresShell::eVertical);
 
   // We might have the globally focused element for scrolling. Gather a ViewID
   // for the horizontal and vertical scroll targets of this element.
   ScrollTargets target;
   target.mHorizontal = nsLayoutUtils::FindIDForScrollableFrame(horizontal);
   target.mVertical = nsLayoutUtils::FindIDForScrollableFrame(vertical);
+  if (XRE_IsContentProcess()) {
+    target.mHorizontalRenderRoot = gfxUtils::GetContentRenderRoot();
+    target.mVerticalRenderRoot = gfxUtils::GetContentRenderRoot();
+  } else {
+    target.mHorizontalRenderRoot =
+        horizontal ? gfxUtils::RecursivelyGetRenderRootForFrame(
+                         horizontal->GetScrolledFrame())
+                   : wr::RenderRoot::Default;
+    target.mVerticalRenderRoot =
+        vertical ? gfxUtils::RecursivelyGetRenderRootForFrame(
+                       vertical->GetScrolledFrame())
+                 : wr::RenderRoot::Default;
+  }
   mData = AsVariant(target);
 
   FT_LOG("Creating scroll target with seq=%" PRIu64 ", kl=%d, h=%" PRIu64
          ", v=%" PRIu64 "\n",
          aFocusSequenceNumber, mFocusHasKeyEventListeners, target.mHorizontal,
          target.mVertical);
 }
 
--- a/gfx/layers/apz/src/FocusTarget.h
+++ b/gfx/layers/apz/src/FocusTarget.h
@@ -6,16 +6,17 @@
 
 #ifndef mozilla_layers_FocusTarget_h
 #define mozilla_layers_FocusTarget_h
 
 #include <stdint.h>  // for int32_t, uint32_t
 
 #include "mozilla/DefineEnum.h"                  // for MOZ_DEFINE_ENUM
 #include "mozilla/layers/ScrollableLayerGuid.h"  // for ViewID
+#include "mozilla/webrender/WebRenderTypes.h"    // for RenderRoot
 #include "mozilla/Variant.h"                     // for Variant
 
 class nsIPresShell;
 
 namespace mozilla {
 namespace layers {
 
 /**
@@ -23,20 +24,31 @@ namespace layers {
  * element of a document and the scrollable frames to use when keyboard
  * scrolling it. It is created on the main thread at paint-time, but is then
  * passed over IPC to the compositor/APZ code.
  */
 class FocusTarget final {
  public:
   struct ScrollTargets {
     ScrollableLayerGuid::ViewID mHorizontal;
+    wr::RenderRoot mHorizontalRenderRoot;
     ScrollableLayerGuid::ViewID mVertical;
+    wr::RenderRoot mVerticalRenderRoot;
 
     bool operator==(const ScrollTargets& aRhs) const {
-      return mHorizontal == aRhs.mHorizontal && mVertical == aRhs.mVertical;
+      bool ret =
+          (mHorizontal == aRhs.mHorizontal && mVertical == aRhs.mVertical);
+      if (ret) {
+        // The render root is a function of where the scrollable frame is in
+        // the DOM/layout tree, so if the ViewIDs match then the render roots
+        // should also match.
+        MOZ_ASSERT(mHorizontalRenderRoot == aRhs.mHorizontalRenderRoot &&
+                   mVerticalRenderRoot == aRhs.mVerticalRenderRoot);
+      }
+      return ret;
     }
   };
 
   // We need this to represent the case where mData has no focus target data
   // because we can't have an empty variant
   struct NoFocusTarget {
     bool operator==(const NoFocusTarget& aRhs) const { return true; }
   };
--- a/gfx/layers/apz/test/gtest/APZCBasicTester.h
+++ b/gfx/layers/apz/test/gtest/APZCBasicTester.h
@@ -28,17 +28,19 @@ class APZCBasicTester : public APZCTeste
     gfxPrefs::GetSingleton();
     APZThreadUtils::SetThreadAssertionsEnabled(false);
     APZThreadUtils::SetControllerThread(MessageLoop::current());
 
     tm = new TestAPZCTreeManager(mcc);
     updater = new APZUpdater(tm, false);
     sampler = new APZSampler(tm, false);
     apzc =
-        new TestAsyncPanZoomController(LayersId{0}, mcc, tm, mGestureBehavior);
+        new TestAsyncPanZoomController(LayersId{0}, mcc, tm,
+                                       wr::RenderRoot::Default,
+                                       mGestureBehavior);
     apzc->SetFrameMetrics(TestFrameMetrics());
     apzc->GetScrollMetadata().SetIsLayersIdRoot(true);
   }
 
   /**
    * Get the APZC's scroll range in CSS pixels.
    */
   CSSRect GetScrollRange() const {
--- a/gfx/layers/apz/test/gtest/APZTestCommon.h
+++ b/gfx/layers/apz/test/gtest/APZTestCommon.h
@@ -216,34 +216,48 @@ class TestAPZCTreeManager : public APZCT
   void ClearContentController() { mcc = nullptr; }
 
   /**
    * This function is not currently implemented.
    * See bug 1468804 for more information.
    **/
   void CancelAnimation() { EXPECT_TRUE(false); }
 
+  using APZCTreeManager::SetTargetAPZC;  // silence clang warning about overload
+  void SetTargetAPZC(uint64_t aInputBlockId,
+                     const nsTArray<ScrollableLayerGuid>& aTargets) {
+    nsTArray<SLGuidAndRenderRoot> wrapped;
+    for (const ScrollableLayerGuid& target : aTargets) {
+      wrapped.AppendElement(
+          SLGuidAndRenderRoot(target, wr::RenderRoot::Default));
+    }
+    this->SetTargetAPZC(aInputBlockId, wrapped);
+  }
+
  protected:
-  AsyncPanZoomController* NewAPZCInstance(
-      LayersId aLayersId, GeckoContentController* aController) override;
+  AsyncPanZoomController* NewAPZCInstance(LayersId aLayersId,
+                                          GeckoContentController* aController,
+                                          wr::RenderRoot aRenderRoot) override;
 
   TimeStamp GetFrameTime() override { return mcc->Time(); }
 
  private:
   RefPtr<MockContentControllerDelayed> mcc;
 };
 
 class TestAsyncPanZoomController : public AsyncPanZoomController {
  public:
   TestAsyncPanZoomController(LayersId aLayersId,
                              MockContentControllerDelayed* aMcc,
                              TestAPZCTreeManager* aTreeManager,
+                             wr::RenderRoot aRenderRoot,
                              GestureBehavior aBehavior = DEFAULT_GESTURES)
       : AsyncPanZoomController(aLayersId, aTreeManager,
-                               aTreeManager->GetInputQueue(), aMcc, aBehavior),
+                               aTreeManager->GetInputQueue(), aMcc, aRenderRoot,
+                               aBehavior),
         mWaitForMainThread(false),
         mcc(aMcc) {}
 
   nsEventStatus ReceiveInputEvent(const InputData& aEvent,
                                   ScrollableLayerGuid* aDummy,
                                   uint64_t* aOutInputBlockId) {
     // This is a function whose signature matches exactly the ReceiveInputEvent
     // on APZCTreeManager. This allows us to templates for functions like
@@ -868,21 +882,23 @@ void APZCTesterBase::PinchWithPinchInput
   nsEventStatus expectedStatus = aShouldTriggerPinch
                                      ? nsEventStatus_eConsumeNoDefault
                                      : nsEventStatus_eIgnore;
   EXPECT_EQ(expectedStatus, statuses[0]);
   EXPECT_EQ(expectedStatus, statuses[1]);
 }
 
 AsyncPanZoomController* TestAPZCTreeManager::NewAPZCInstance(
-    LayersId aLayersId, GeckoContentController* aController) {
+    LayersId aLayersId, GeckoContentController* aController,
+    wr::RenderRoot aRenderRoot) {
   MockContentControllerDelayed* mcc =
       static_cast<MockContentControllerDelayed*>(aController);
   return new TestAsyncPanZoomController(
-      aLayersId, mcc, this, AsyncPanZoomController::USE_GESTURE_DETECTOR);
+      aLayersId, mcc, this, aRenderRoot,
+      AsyncPanZoomController::USE_GESTURE_DETECTOR);
 }
 
 inline FrameMetrics TestFrameMetrics() {
   FrameMetrics fm;
 
   fm.SetDisplayPort(CSSRect(0, 0, 10, 10));
   fm.SetCompositionBounds(ParentLayerRect(0, 0, 10, 10));
   fm.SetCriticalDisplayPort(CSSRect(0, 0, 10, 10));
--- a/gfx/layers/apz/test/gtest/TestBasic.cpp
+++ b/gfx/layers/apz/test/gtest/TestBasic.cpp
@@ -54,17 +54,18 @@ TEST_F(APZCBasicTester, ComplexTransform
   // CSS transforms, the two layers are the same size in screen
   // pixels.
   //
   // The screen itself is 24x24 in screen pixels (therefore 4x4 in
   // CSS pixels). The displayport is 1 extra CSS pixel on all
   // sides.
 
   RefPtr<TestAsyncPanZoomController> childApzc =
-      new TestAsyncPanZoomController(LayersId{0}, mcc, tm);
+      new TestAsyncPanZoomController(LayersId{0}, mcc, tm,
+                                     wr::RenderRoot::Default);
 
   const char* layerTreeSyntax = "c(c)";
   // LayerID                     0 1
   nsIntRegion layerVisibleRegion[] = {
       nsIntRegion(IntRect(0, 0, 300, 300)),
       nsIntRegion(IntRect(0, 0, 150, 300)),
   };
   Matrix4x4 transforms[] = {
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -661,19 +661,20 @@ namespace {
 using FrameForPointOption = nsLayoutUtils::FrameForPointOption;
 
 // Determine the scrollable target frame for the given point and add it to
 // the target list. If the frame doesn't have a displayport, set one.
 // Return whether or not a displayport was set.
 static bool PrepareForSetTargetAPZCNotification(
     nsIWidget* aWidget, const ScrollableLayerGuid& aGuid, nsIFrame* aRootFrame,
     const LayoutDeviceIntPoint& aRefPoint,
-    nsTArray<ScrollableLayerGuid>* aTargets) {
-  ScrollableLayerGuid guid(aGuid.mLayersId, 0,
-                           ScrollableLayerGuid::NULL_SCROLL_ID);
+    nsTArray<SLGuidAndRenderRoot>* aTargets) {
+  SLGuidAndRenderRoot guid(aGuid.mLayersId, 0,
+                           ScrollableLayerGuid::NULL_SCROLL_ID,
+                           wr::RenderRoot::Default);
   nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
       aWidget, aRefPoint, aRootFrame);
   EnumSet<FrameForPointOption> options;
   if (nsLayoutUtils::AllowZoomingForDocument(
           aRootFrame->PresShell()->GetDocument())) {
     // If zooming is enabled, we need IgnoreRootScrollFrame for correct
     // hit testing. Otherwise, don't use it because it interferes with
     // hit testing for some purposes such as scrollbar dragging (this will
@@ -686,28 +687,35 @@ static bool PrepareForSetTargetAPZCNotif
       target ? nsLayoutUtils::GetAsyncScrollableAncestorFrame(target)
              : aRootFrame->PresShell()->GetRootScrollFrameAsScrollable();
 
   // Assuming that if there's no scrollAncestor, there's already a displayPort.
   nsCOMPtr<dom::Element> dpElement =
       scrollAncestor ? GetDisplayportElementFor(scrollAncestor)
                      : GetRootDocumentElementFor(aWidget);
 
+  if (XRE_IsContentProcess()) {
+    guid.mRenderRoot = gfxUtils::GetContentRenderRoot();
+  } else {
+    guid.mRenderRoot = gfxUtils::RecursivelyGetRenderRootForElement(dpElement);
+  }
+
 #ifdef APZCCH_LOGGING
   nsAutoString dpElementDesc;
   if (dpElement) {
     dpElement->Describe(dpElementDesc);
   }
   APZCCH_LOG("For event at %s found scrollable element %p (%s)\n",
              Stringify(aRefPoint).c_str(), dpElement.get(),
              NS_LossyConvertUTF16toASCII(dpElementDesc).get());
 #endif
 
   bool guidIsValid = APZCCallbackHelper::GetOrCreateScrollIdentifiers(
-      dpElement, &(guid.mPresShellId), &(guid.mScrollId));
+      dpElement, &(guid.mScrollableLayerGuid.mPresShellId),
+      &(guid.mScrollableLayerGuid.mScrollId));
   aTargets->AppendElement(guid);
 
   if (!guidIsValid || nsLayoutUtils::HasDisplayPort(dpElement)) {
     return false;
   }
 
   if (!scrollAncestor) {
     // This can happen if the document element gets swapped out after
@@ -731,17 +739,17 @@ static bool PrepareForSetTargetAPZCNotif
   nsLayoutUtils::SetZeroMarginDisplayPortOnAsyncScrollableAncestors(
       frame, nsLayoutUtils::RepaintMode::Repaint);
 
   return true;
 }
 
 static void SendLayersDependentApzcTargetConfirmation(
     nsIPresShell* aShell, uint64_t aInputBlockId,
-    const nsTArray<ScrollableLayerGuid>& aTargets) {
+    const nsTArray<SLGuidAndRenderRoot>& aTargets) {
   LayerManager* lm = aShell->GetLayerManager();
   if (!lm) {
     return;
   }
 
   if (WebRenderLayerManager* wrlm = lm->AsWebRenderLayerManager()) {
     if (WebRenderBridgeChild* wrbc = wrlm->WrBridge()) {
       wrbc->SendSetConfirmedTargetAPZC(aInputBlockId, aTargets);
@@ -761,17 +769,17 @@ static void SendLayersDependentApzcTarge
 
   shadow->SendSetConfirmedTargetAPZC(aInputBlockId, aTargets);
 }
 
 }  // namespace
 
 DisplayportSetListener::DisplayportSetListener(
     nsIWidget* aWidget, nsIPresShell* aPresShell, const uint64_t& aInputBlockId,
-    const nsTArray<ScrollableLayerGuid>& aTargets)
+    const nsTArray<SLGuidAndRenderRoot>& aTargets)
     : mWidget(aWidget),
       mPresShell(aPresShell),
       mInputBlockId(aInputBlockId),
       mTargets(aTargets) {}
 
 DisplayportSetListener::~DisplayportSetListener() {}
 
 bool DisplayportSetListener::Register() {
@@ -830,17 +838,17 @@ APZCCallbackHelper::SendSetTargetAPZCNot
     return nullptr;
   }
   sLastTargetAPZCNotificationInputBlock = aInputBlockId;
   if (nsIPresShell* shell = aDocument->GetShell()) {
     if (nsIFrame* rootFrame = shell->GetRootFrame()) {
       rootFrame = UpdateRootFrameForTouchTargetDocument(rootFrame);
 
       bool waitForRefresh = false;
-      nsTArray<ScrollableLayerGuid> targets;
+      nsTArray<SLGuidAndRenderRoot> targets;
 
       if (const WidgetTouchEvent* touchEvent = aEvent.AsTouchEvent()) {
         for (size_t i = 0; i < touchEvent->mTouches.Length(); i++) {
           waitForRefresh |= PrepareForSetTargetAPZCNotification(
               aWidget, aGuid, rootFrame, touchEvent->mTouches[i]->mRefPoint,
               &targets);
         }
       } else if (const WidgetWheelEvent* wheelEvent = aEvent.AsWheelEvent()) {
--- a/gfx/layers/apz/util/APZCCallbackHelper.h
+++ b/gfx/layers/apz/util/APZCCallbackHelper.h
@@ -32,26 +32,26 @@ namespace layers {
 typedef std::function<void(uint64_t, const nsTArray<TouchBehaviorFlags>&)>
     SetAllowedTouchBehaviorCallback;
 
 /* Refer to documentation on SendSetTargetAPZCNotification for this class */
 class DisplayportSetListener : public nsAPostRefreshObserver {
  public:
   DisplayportSetListener(nsIWidget* aWidget, nsIPresShell* aPresShell,
                          const uint64_t& aInputBlockId,
-                         const nsTArray<ScrollableLayerGuid>& aTargets);
+                         const nsTArray<SLGuidAndRenderRoot>& aTargets);
   virtual ~DisplayportSetListener();
   bool Register();
   void DidRefresh() override;
 
  private:
   RefPtr<nsIWidget> mWidget;
   RefPtr<nsIPresShell> mPresShell;
   uint64_t mInputBlockId;
-  nsTArray<ScrollableLayerGuid> mTargets;
+  nsTArray<SLGuidAndRenderRoot> mTargets;
 };
 
 /* This class contains some helper methods that facilitate implementing the
    GeckoContentController callback interface required by the
    AsyncPanZoomController. Since different platforms need to implement this
    interface in similar-but- not-quite-the-same ways, this utility class
    provides some helpful methods to hold code that can be shared across the
    different platform implementations.
--- a/gfx/layers/apz/util/ChromeProcessController.cpp
+++ b/gfx/layers/apz/util/ChromeProcessController.cpp
@@ -146,20 +146,21 @@ void ChromeProcessController::HandleDoub
   CSSPoint point(aPoint.x / resolution, aPoint.y / resolution);
   CSSRect zoomToRect = CalculateRectToZoomTo(document, point);
 
   uint32_t presShellId;
   ScrollableLayerGuid::ViewID viewId;
   if (APZCCallbackHelper::GetOrCreateScrollIdentifiers(
           document->GetDocumentElement(), &presShellId, &viewId)) {
     APZThreadUtils::RunOnControllerThread(
-        NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
+        NewRunnableMethod<SLGuidAndRenderRoot, CSSRect, uint32_t>(
             "IAPZCTreeManager::ZoomToRect", mAPZCTreeManager,
             &IAPZCTreeManager::ZoomToRect,
-            ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId),
+            SLGuidAndRenderRoot(aGuid.mLayersId, presShellId, viewId,
+                                wr::RenderRoot::Default),
             zoomToRect, ZoomToRectBehavior::DEFAULT_BEHAVIOR));
   }
 }
 
 void ChromeProcessController::HandleTap(
     TapType aType, const mozilla::LayoutDevicePoint& aPoint,
     Modifiers aModifiers, const ScrollableLayerGuid& aGuid,
     uint64_t aInputBlockId) {
--- a/gfx/layers/client/CanvasClient.cpp
+++ b/gfx/layers/client/CanvasClient.cpp
@@ -58,17 +58,18 @@ void CanvasClientBridge::UpdateAsync(Asy
     return;
   }
 
   static_cast<ShadowLayerForwarder*>(GetForwarder())
       ->AttachAsyncCompositable(asyncID, mLayer);
   mAsyncHandle = asyncID;
 }
 
-void CanvasClient2D::UpdateFromTexture(TextureClient* aTexture) {
+void CanvasClient2D::UpdateFromTexture(TextureClient* aTexture,
+                                       wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(aTexture);
 
   if (!aTexture->IsSharedWithCompositor()) {
     if (!AddTextureClient(aTexture)) {
       return;
     }
   }
 
@@ -77,25 +78,26 @@ void CanvasClient2D::UpdateFromTexture(T
   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, Some(aRenderRoot));
   aTexture->SyncWithObject(GetForwarder()->GetSyncObject());
 }
 
 void CanvasClient2D::Update(gfx::IntSize aSize,
-                            ShareableCanvasRenderer* aCanvasRenderer) {
+                            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) {
@@ -145,17 +147,17 @@ void CanvasClient2D::Update(gfx::IntSize
   }
 
   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, Some(aRenderRoot));
     mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
   }
 
   mBackBuffer.swap(mFrontBuffer);
 }
 
 already_AddRefed<TextureClient> CanvasClient2D::CreateTextureClientForCanvas(
     gfx::SurfaceFormat aFormat, gfx::IntSize aSize, TextureFlags aFlags,
@@ -353,18 +355,19 @@ static already_AddRefed<SharedSurfaceTex
 
   destSurf->ProducerAcquire();
   SharedSurface::ProdCopy(src, dest->Surf(), factory);
   destSurf->ProducerRelease();
 
   return dest.forget();
 }
 
-void CanvasClientSharedSurface::Update(
-    gfx::IntSize aSize, ShareableCanvasRenderer* aCanvasRenderer) {
+void CanvasClientSharedSurface::Update(gfx::IntSize aSize,
+                                       ShareableCanvasRenderer* aCanvasRenderer,
+                                       wr::RenderRoot aRenderRoot) {
   Renderer renderer;
   renderer.construct<ShareableCanvasRenderer*>(aCanvasRenderer);
   UpdateRenderer(aSize, renderer);
 }
 
 void CanvasClientSharedSurface::UpdateAsync(AsyncCanvasRenderer* aRenderer) {
   Renderer renderer;
   renderer.construct<AsyncCanvasRenderer*>(aRenderer);
@@ -463,17 +466,17 @@ void CanvasClientSharedSurface::UpdateRe
         << "Failed to allocate a TextureClient for SharedSurface Canvas. Size: "
         << aSize;
     return;
   }
 
   mNewFront = newFront;
 }
 
-void CanvasClientSharedSurface::Updated() {
+void CanvasClientSharedSurface::Updated(wr::RenderRoot aRenderRoot) {
   if (!mNewFront) {
     return;
   }
 
   auto forwarder = GetForwarder();
 
   mFront = mNewFront;
   mNewFront = nullptr;
@@ -483,17 +486,17 @@ void 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, Some(aRenderRoot));
 }
 
 void CanvasClientSharedSurface::OnDetach() { ClearSurfaces(); }
 
 void CanvasClientSharedSurface::ClearSurfaces() {
   if (mFront) {
     mFront->CancelWaitForRecycle();
   }
--- a/gfx/layers/client/CanvasClient.h
+++ b/gfx/layers/client/CanvasClient.h
@@ -58,28 +58,30 @@ class CanvasClient : public Compositable
     mTextureFlags = aFlags;
   }
 
   virtual ~CanvasClient() {}
 
   virtual void Clear(){};
 
   virtual void Update(gfx::IntSize aSize,
-                      ShareableCanvasRenderer* aCanvasRenderer) = 0;
+                      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 {
@@ -91,19 +93,21 @@ class CanvasClient2D : public CanvasClie
     return TextureInfo(CompositableType::IMAGE, mTextureFlags);
   }
 
   virtual void Clear() override {
     mBackBuffer = mFrontBuffer = mBufferProviderTexture = nullptr;
   }
 
   virtual void Update(gfx::IntSize aSize,
-                      ShareableCanvasRenderer* aCanvasRenderer) override;
+                      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 { mBackBuffer = mFrontBuffer = nullptr; }
 
  private:
@@ -140,22 +144,23 @@ class CanvasClientSharedSurface : public
 
   virtual TextureInfo GetTextureInfo() const override {
     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
@@ -166,17 +171,18 @@ class CanvasClientBridge final : public 
                      TextureFlags aFlags)
       : CanvasClient(aLayerForwarder, aFlags), mLayer(nullptr) {}
 
   TextureInfo GetTextureInfo() const override {
     return TextureInfo(CompositableType::IMAGE);
   }
 
   virtual void Update(gfx::IntSize aSize,
-                      ShareableCanvasRenderer* aCanvasRenderer) override {}
+                      ShareableCanvasRenderer* aCanvasRenderer,
+                      wr::RenderRoot aRenderRoot) override {}
 
   virtual void UpdateAsync(AsyncCanvasRenderer* aRenderer) override;
 
   void SetLayer(ShadowableLayer* aLayer) { mLayer = aLayer; }
 
  protected:
   CompositableHandle mAsyncHandle;
   ShadowableLayer* mLayer;
--- a/gfx/layers/client/ClientImageLayer.cpp
+++ b/gfx/layers/client/ClientImageLayer.cpp
@@ -115,34 +115,35 @@ class ClientImageLayer : public ImageLay
 
 void ClientImageLayer::RenderLayer() {
   RenderMaskLayers(this);
 
   if (!mContainer) {
     return;
   }
 
-  if (!mImageClient ||
-      !mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+  if (!mImageClient || !mImageClient->UpdateImage(mContainer, GetContentFlags(),
+                                                  Nothing())) {
     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(),
+                                   Nothing())) {
       return;
     }
   }
   ClientManager()->Hold(this);
 }
 
 already_AddRefed<ImageLayer> ClientLayerManager::CreateImageLayer() {
   NS_ASSERTION(InConstruction(), "Only allowed in construction phase");
--- a/gfx/layers/client/CompositableClient.cpp
+++ b/gfx/layers/client/CompositableClient.cpp
@@ -120,18 +120,19 @@ void CompositableClient::ClearCachedReso
 }
 
 void CompositableClient::HandleMemoryPressure() {
   if (mTextureClientRecycler) {
     mTextureClientRecycler->ShrinkToMinimumSize();
   }
 }
 
-void CompositableClient::RemoveTexture(TextureClient* aTexture) {
-  mForwarder->RemoveTextureFromCompositable(this, aTexture);
+void CompositableClient::RemoveTexture(
+    TextureClient* aTexture, const Maybe<wr::RenderRoot>& aRenderRoot) {
+  mForwarder->RemoveTextureFromCompositable(this, aTexture, aRenderRoot);
 }
 
 TextureClientRecycleAllocator* CompositableClient::GetTextureClientRecycler() {
   if (mTextureClientRecycler) {
     return mTextureClientRecycler;
   }
 
   if (!mForwarder) {
@@ -193,14 +194,14 @@ void CompositableClient::DumpTextureClie
     aStream << gfxUtils::GetAsLZ4Base64Str(dSurf).get();
   } else {
     aStream << gfxUtils::GetAsDataURI(dSurf).get();
   }
 }
 
 AutoRemoveTexture::~AutoRemoveTexture() {
   if (mCompositable && mTexture && mCompositable->IsConnected()) {
-    mCompositable->RemoveTexture(mTexture);
+    mCompositable->RemoveTexture(mTexture, Some(mRenderRoot));
   }
 }
 
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/client/CompositableClient.h
+++ b/gfx/layers/client/CompositableClient.h
@@ -11,17 +11,18 @@
 #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 "nsISupportsImpl.h"               // for MOZ_COUNT_CTOR, etc
+#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;
 class CompositableForwarder;
@@ -151,17 +152,18 @@ class CompositableClient {
 
   /**
    * 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,
+                             const Maybe<wr::RenderRoot>& aRenderRoot);
 
   void InitIPDL(const CompositableHandle& aHandle);
 
   TextureFlags GetTextureFlags() const { return mTextureFlags; }
 
   TextureClientRecycleAllocator* GetTextureClientRecycler();
 
   bool HasTextureClientRecycler() { return !!mTextureClientRecycler; }
@@ -183,23 +185,27 @@ class CompositableClient {
   friend class CompositableChild;
 };
 
 /**
  * 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) {}
+      : 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
@@ -710,17 +710,18 @@ void ContentClientRemoteBuffer::Updated(
     mForwarder->UseComponentAlphaTextures(this, 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, Nothing());
   }
 
   // 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(),
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -59,32 +59,37 @@ already_AddRefed<ImageClient> ImageClien
       MOZ_CRASH("GFX: unhandled program type image");
   }
 
   NS_ASSERTION(result, "Failed to create ImageClient");
 
   return result.forget();
 }
 
-void ImageClient::RemoveTexture(TextureClient* aTexture) {
-  GetForwarder()->RemoveTextureFromCompositable(this, aTexture);
+void ImageClient::RemoveTexture(TextureClient* aTexture,
+                                const Maybe<wr::RenderRoot>& aRenderRoot) {
+  GetForwarder()->RemoveTextureFromCompositable(this, aTexture, aRenderRoot);
 }
 
 ImageClientSingle::ImageClientSingle(CompositableForwarder* aFwd,
                                      TextureFlags aFlags,
                                      CompositableType aType)
     : ImageClient(aFwd, aFlags, aType) {}
 
 TextureInfo ImageClientSingle::GetTextureInfo() const {
   return TextureInfo(CompositableType::IMAGE);
 }
 
 void ImageClientSingle::FlushAllImages() {
   for (auto& b : mBuffers) {
-    RemoveTexture(b.mTextureClient);
+    // It should be safe to just assume a default render root here, even if
+    // the texture actually presents in a content render root, as the only
+    // risk would be if the content render root has not / is not going to
+    // generate a frame before the texture gets cleared.
+    RemoveTexture(b.mTextureClient, Some(wr::RenderRoot::Default));
   }
   mBuffers.Clear();
 }
 
 /* static */
 already_AddRefed<TextureClient> ImageClient::CreateTextureClientForImage(
     Image* aImage, KnowsCompositor* aForwarder) {
   RefPtr<TextureClient> texture;
@@ -152,17 +157,18 @@ already_AddRefed<TextureClient> ImageCli
     }
 
     texture->Unlock();
   }
   return texture.forget();
 }
 
 bool ImageClientSingle::UpdateImage(ImageContainer* aContainer,
-                                    uint32_t aContentFlags) {
+                                    uint32_t aContentFlags,
+                                    const Maybe<wr::RenderRoot>& aRenderRoot) {
   AutoTArray<ImageContainer::OwningImage, 4> images;
   uint32_t generationCounter;
   aContainer->GetCurrentImages(&images, &generationCounter);
 
   if (mLastUpdateGenerationCounter == generationCounter) {
     return true;
   }
   mLastUpdateGenerationCounter = generationCounter;
@@ -176,17 +182,17 @@ bool ImageClientSingle::UpdateImage(Imag
   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;
 
@@ -240,20 +246,20 @@ bool ImageClientSingle::UpdateImage(Imag
 
     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() {
   if (mBuffers.Length() == 0) {
@@ -276,17 +282,18 @@ ImageClient::ImageClient(CompositableFor
       mType(aType),
       mLastUpdateGenerationCounter(0) {}
 
 ImageClientBridge::ImageClientBridge(CompositableForwarder* aFwd,
                                      TextureFlags aFlags)
     : ImageClient(aFwd, aFlags, CompositableType::IMAGE_BRIDGE) {}
 
 bool ImageClientBridge::UpdateImage(ImageContainer* aContainer,
-                                    uint32_t aContentFlags) {
+                                    uint32_t aContentFlags,
+                                    const Maybe<wr::RenderRoot>& aRenderRoot) {
   if (!GetForwarder() || !mLayer) {
     return false;
   }
   if (mAsyncContainerHandle == aContainer->GetAsyncContainerHandle()) {
     return true;
   }
 
   mAsyncContainerHandle = aContainer->GetAsyncContainerHandle();
--- a/gfx/layers/client/ImageClient.h
+++ b/gfx/layers/client/ImageClient.h
@@ -48,29 +48,30 @@ class ImageClient : public CompositableC
 
   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,
+                           const Maybe<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,
+                             const Maybe<wr::RenderRoot>& aRenderRoot) override;
 
   virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
 
   static already_AddRefed<TextureClient> CreateTextureClientForImage(
       Image* aImage, KnowsCompositor* aForwarder);
 
   uint32_t GetLastUpdateGenerationCounter() {
     return mLastUpdateGenerationCounter;
@@ -90,18 +91,18 @@ class ImageClient : public CompositableC
 /**
  * An image client which uses a single texture client.
  */
 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,
+                           const Maybe<wr::RenderRoot>& aRenderRoot) override;
 
   virtual void OnDetach() override;
 
   virtual bool AddTextureClient(TextureClient* aTexture) override;
 
   virtual TextureInfo GetTextureInfo() const override;
 
   virtual void FlushAllImages() override;
@@ -124,18 +125,18 @@ class ImageClientSingle : public ImageCl
  * Image class to be used for async image uploads using the image bridge
  * protocol.
  * 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,
+                           const Maybe<wr::RenderRoot>& aRenderRoot) override;
   virtual bool Connect(ImageContainer* aImageContainer) override {
     return false;
   }
 
   virtual TextureInfo GetTextureInfo() const override {
     return TextureInfo(mType);
   }
 
--- a/gfx/layers/ipc/APZCTreeManagerChild.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerChild.cpp
@@ -43,58 +43,58 @@ void APZCTreeManagerChild::Destroy() {
     mInputBridge = nullptr;
   }
 }
 
 void APZCTreeManagerChild::SetKeyboardMap(const KeyboardMap& aKeyboardMap) {
   SendSetKeyboardMap(aKeyboardMap);
 }
 
-void APZCTreeManagerChild::ZoomToRect(const ScrollableLayerGuid& aGuid,
+void APZCTreeManagerChild::ZoomToRect(const SLGuidAndRenderRoot& aGuid,
                                       const CSSRect& aRect,
                                       const uint32_t aFlags) {
   SendZoomToRect(aGuid, aRect, aFlags);
 }
 
 void APZCTreeManagerChild::ContentReceivedInputBlock(uint64_t aInputBlockId,
                                                      bool aPreventDefault) {
   SendContentReceivedInputBlock(aInputBlockId, aPreventDefault);
 }
 
 void APZCTreeManagerChild::SetTargetAPZC(
-    uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) {
+    uint64_t aInputBlockId, const nsTArray<SLGuidAndRenderRoot>& aTargets) {
   SendSetTargetAPZC(aInputBlockId, aTargets);
 }
 
 void APZCTreeManagerChild::UpdateZoomConstraints(
-    const ScrollableLayerGuid& aGuid,
+    const SLGuidAndRenderRoot& aGuid,
     const Maybe<ZoomConstraints>& aConstraints) {
   if (mIPCOpen) {
     SendUpdateZoomConstraints(aGuid, aConstraints);
   }
 }
 
 void APZCTreeManagerChild::SetDPI(float aDpiValue) { SendSetDPI(aDpiValue); }
 
 void APZCTreeManagerChild::SetAllowedTouchBehavior(
     uint64_t aInputBlockId, const nsTArray<TouchBehaviorFlags>& aValues) {
   SendSetAllowedTouchBehavior(aInputBlockId, aValues);
 }
 
 void APZCTreeManagerChild::StartScrollbarDrag(
-    const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) {
+    const SLGuidAndRenderRoot& aGuid, const AsyncDragMetrics& aDragMetrics) {
   SendStartScrollbarDrag(aGuid, aDragMetrics);
 }
 
-bool APZCTreeManagerChild::StartAutoscroll(const ScrollableLayerGuid& aGuid,
+bool APZCTreeManagerChild::StartAutoscroll(const SLGuidAndRenderRoot& aGuid,
                                            const ScreenPoint& aAnchorLocation) {
   return SendStartAutoscroll(aGuid, aAnchorLocation);
 }
 
-void APZCTreeManagerChild::StopAutoscroll(const ScrollableLayerGuid& aGuid) {
+void APZCTreeManagerChild::StopAutoscroll(const SLGuidAndRenderRoot& aGuid) {
   SendStopAutoscroll(aGuid);
 }
 
 void APZCTreeManagerChild::SetLongTapEnabled(bool aTapGestureEnabled) {
   SendSetLongTapEnabled(aTapGestureEnabled);
 }
 
 APZInputBridge* APZCTreeManagerChild::InputBridge() {
--- a/gfx/layers/ipc/APZCTreeManagerChild.h
+++ b/gfx/layers/ipc/APZCTreeManagerChild.h
@@ -25,42 +25,42 @@ class APZCTreeManagerChild : public IAPZ
   APZCTreeManagerChild();
 
   void SetCompositorSession(RemoteCompositorSession* aSession);
   void SetInputBridge(APZInputBridgeChild* aInputBridge);
   void Destroy();
 
   void SetKeyboardMap(const KeyboardMap& aKeyboardMap) override;
 
-  void ZoomToRect(const ScrollableLayerGuid& aGuid, const CSSRect& aRect,
+  void ZoomToRect(const SLGuidAndRenderRoot& aGuid, const CSSRect& aRect,
                   const uint32_t aFlags = DEFAULT_BEHAVIOR) override;
 
   void ContentReceivedInputBlock(uint64_t aInputBlockId,
                                  bool aPreventDefault) override;
 
   void SetTargetAPZC(uint64_t aInputBlockId,
-                     const nsTArray<ScrollableLayerGuid>& aTargets) override;
+                     const nsTArray<SLGuidAndRenderRoot>& aTargets) override;
 
   void UpdateZoomConstraints(
-      const ScrollableLayerGuid& aGuid,
+      const SLGuidAndRenderRoot& aGuid,
       const Maybe<ZoomConstraints>& aConstraints) override;
 
   void SetDPI(float aDpiValue) override;
 
   void SetAllowedTouchBehavior(
       uint64_t aInputBlockId,
       const nsTArray<TouchBehaviorFlags>& aValues) override;
 
-  void StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+  void StartScrollbarDrag(const SLGuidAndRenderRoot& aGuid,
                           const AsyncDragMetrics& aDragMetrics) override;
 
-  bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
+  bool StartAutoscroll(const SLGuidAndRenderRoot& aGuid,
                        const ScreenPoint& aAnchorLocation) override;
 
-  void StopAutoscroll(const ScrollableLayerGuid& aGuid) override;
+  void StopAutoscroll(const SLGuidAndRenderRoot& aGuid) override;
 
   void SetLongTapEnabled(bool aTapGestureEnabled) override;
 
   APZInputBridge* InputBridge() override;
 
   void AddIPDLReference();
   void ReleaseIPDLReference();
   void ActorDestroy(ActorDestroyReason aWhy) override;
--- a/gfx/layers/ipc/APZCTreeManagerParent.cpp
+++ b/gfx/layers/ipc/APZCTreeManagerParent.cpp
@@ -9,19 +9,19 @@
 #include "apz/src/APZCTreeManager.h"
 #include "mozilla/layers/APZThreadUtils.h"
 #include "mozilla/layers/APZUpdater.h"
 
 namespace mozilla {
 namespace layers {
 
 APZCTreeManagerParent::APZCTreeManagerParent(
-    LayersId aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager,
+    WRRootId aWrRootId, RefPtr<APZCTreeManager> aAPZCTreeManager,
     RefPtr<APZUpdater> aAPZUpdater)
-    : mLayersId(aLayersId),
+    : mWrRootId(aWrRootId),
       mTreeManager(std::move(aAPZCTreeManager)),
       mUpdater(std::move(aAPZUpdater)) {
   MOZ_ASSERT(mTreeManager != nullptr);
   MOZ_ASSERT(mUpdater != nullptr);
   MOZ_ASSERT(mUpdater->HasTreeManager(mTreeManager));
 }
 
 APZCTreeManagerParent::~APZCTreeManagerParent() {}
@@ -33,160 +33,171 @@ void APZCTreeManagerParent::ChildAdopted
   MOZ_ASSERT(aAPZUpdater->HasTreeManager(aAPZCTreeManager));
   mTreeManager = std::move(aAPZCTreeManager);
   mUpdater = std::move(aAPZUpdater);
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetKeyboardMap(
     const KeyboardMap& aKeyboardMap) {
   mUpdater->RunOnControllerThread(
-      mLayersId, NewRunnableMethod<KeyboardMap>(
-                     "layers::IAPZCTreeManager::SetKeyboardMap", mTreeManager,
-                     &IAPZCTreeManager::SetKeyboardMap, aKeyboardMap));
+      UpdaterQueueSelector(mWrRootId),
+      NewRunnableMethod<KeyboardMap>(
+          "layers::IAPZCTreeManager::SetKeyboardMap", mTreeManager,
+          &IAPZCTreeManager::SetKeyboardMap, aKeyboardMap));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvZoomToRect(
-    const ScrollableLayerGuid& aGuid, const CSSRect& aRect,
+    const SLGuidAndRenderRoot& aGuid, const CSSRect& aRect,
     const uint32_t& aFlags) {
-  if (aGuid.mLayersId != mLayersId) {
-    // Guard against bad data from hijacked child processes
-    NS_ERROR("Unexpected layers id in RecvZoomToRect; dropping message...");
+  if (!IsGuidValid(aGuid)) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   mUpdater->RunOnControllerThread(
-      mLayersId, NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
-                     "layers::IAPZCTreeManager::ZoomToRect", mTreeManager,
-                     &IAPZCTreeManager::ZoomToRect, aGuid, aRect, aFlags));
+      UpdaterQueueSelector(aGuid.GetWRRootId()),
+      NewRunnableMethod<SLGuidAndRenderRoot, CSSRect, uint32_t>(
+          "layers::IAPZCTreeManager::ZoomToRect", mTreeManager,
+          &IAPZCTreeManager::ZoomToRect, aGuid, aRect, aFlags));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvContentReceivedInputBlock(
     const uint64_t& aInputBlockId, const bool& aPreventDefault) {
   mUpdater->RunOnControllerThread(
-      mLayersId, NewRunnableMethod<uint64_t, bool>(
-                     "layers::IAPZCTreeManager::ContentReceivedInputBlock",
-                     mTreeManager, &IAPZCTreeManager::ContentReceivedInputBlock,
-                     aInputBlockId, aPreventDefault));
+      UpdaterQueueSelector(mWrRootId),
+      NewRunnableMethod<uint64_t, bool>(
+          "layers::IAPZCTreeManager::ContentReceivedInputBlock", mTreeManager,
+          &IAPZCTreeManager::ContentReceivedInputBlock, aInputBlockId,
+          aPreventDefault));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetTargetAPZC(
-    const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) {
+    const uint64_t& aInputBlockId, nsTArray<SLGuidAndRenderRoot>&& aTargets) {
+  UpdaterQueueSelector selector(mWrRootId.mLayersId);
   for (size_t i = 0; i < aTargets.Length(); i++) {
-    if (aTargets[i].mLayersId != mLayersId) {
-      // Guard against bad data from hijacked child processes
-      NS_ERROR(
-          "Unexpected layers id in RecvSetTargetAPZC; dropping message...");
+    if (!IsGuidValid(aTargets[i])) {
       return IPC_FAIL_NO_REASON(this);
     }
+    selector.mRenderRoots += aTargets[i].mRenderRoot;
   }
   mUpdater->RunOnControllerThread(
-      mLayersId,
+      selector,
       NewRunnableMethod<uint64_t,
-                        StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
+                        StoreCopyPassByRRef<nsTArray<SLGuidAndRenderRoot>>>(
           "layers::IAPZCTreeManager::SetTargetAPZC", mTreeManager,
           &IAPZCTreeManager::SetTargetAPZC, aInputBlockId, aTargets));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvUpdateZoomConstraints(
-    const ScrollableLayerGuid& aGuid,
+    const SLGuidAndRenderRoot& aGuid,
     const MaybeZoomConstraints& aConstraints) {
-  if (aGuid.mLayersId != mLayersId) {
-    // Guard against bad data from hijacked child processes
-    NS_ERROR(
-        "Unexpected layers id in RecvUpdateZoomConstraints; dropping "
-        "message...");
+  if (!IsGuidValid(aGuid)) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   mTreeManager->UpdateZoomConstraints(aGuid, aConstraints);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetDPI(
     const float& aDpiValue) {
   mUpdater->RunOnControllerThread(
-      mLayersId,
+      UpdaterQueueSelector(mWrRootId),
       NewRunnableMethod<float>("layers::IAPZCTreeManager::SetDPI", mTreeManager,
                                &IAPZCTreeManager::SetDPI, aDpiValue));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetAllowedTouchBehavior(
     const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues) {
   mUpdater->RunOnControllerThread(
-      mLayersId,
+      UpdaterQueueSelector(mWrRootId),
       NewRunnableMethod<uint64_t,
                         StoreCopyPassByRRef<nsTArray<TouchBehaviorFlags>>>(
           "layers::IAPZCTreeManager::SetAllowedTouchBehavior", mTreeManager,
           &IAPZCTreeManager::SetAllowedTouchBehavior, aInputBlockId,
           std::move(aValues)));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartScrollbarDrag(
-    const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics) {
-  if (aGuid.mLayersId != mLayersId) {
-    // Guard against bad data from hijacked child processes
-    NS_ERROR(
-        "Unexpected layers id in RecvStartScrollbarDrag; dropping message...");
+    const SLGuidAndRenderRoot& aGuid, const AsyncDragMetrics& aDragMetrics) {
+  if (!IsGuidValid(aGuid)) {
     return IPC_FAIL_NO_REASON(this);
   }
 
   mUpdater->RunOnControllerThread(
-      mLayersId,
-      NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
+      UpdaterQueueSelector(aGuid.GetWRRootId()),
+      NewRunnableMethod<SLGuidAndRenderRoot, AsyncDragMetrics>(
           "layers::IAPZCTreeManager::StartScrollbarDrag", mTreeManager,
           &IAPZCTreeManager::StartScrollbarDrag, aGuid, aDragMetrics));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartAutoscroll(
-    const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation) {
+    const SLGuidAndRenderRoot& aGuid, const ScreenPoint& aAnchorLocation) {
   // Unlike RecvStartScrollbarDrag(), this message comes from the parent
   // process (via nsBaseWidget::mAPZC) rather than from the child process
   // (via TabChild::mApzcTreeManager), so there is no need to check the
-  // layers id against mLayersId (and in any case, it wouldn't match, because
-  // mLayersId stores the parent process's layers id, while nsBaseWidget is
+  // layers id against mWrRootId (and in any case, it wouldn't match, because
+  // mWrRootId stores the parent process's layers id, while nsBaseWidget is
   // sending the child process's layers id).
 
   mUpdater->RunOnControllerThread(
-      mLayersId,
-      NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>(
+      UpdaterQueueSelector(mWrRootId),
+      NewRunnableMethod<SLGuidAndRenderRoot, ScreenPoint>(
           "layers::IAPZCTreeManager::StartAutoscroll", mTreeManager,
           &IAPZCTreeManager::StartAutoscroll, aGuid, aAnchorLocation));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStopAutoscroll(
-    const ScrollableLayerGuid& aGuid) {
+    const SLGuidAndRenderRoot& aGuid) {
   // See RecvStartAutoscroll() for why we don't check the layers id.
 
   mUpdater->RunOnControllerThread(
-      mLayersId, NewRunnableMethod<ScrollableLayerGuid>(
-                     "layers::IAPZCTreeManager::StopAutoscroll", mTreeManager,
-                     &IAPZCTreeManager::StopAutoscroll, aGuid));
+      UpdaterQueueSelector(mWrRootId),
+      NewRunnableMethod<SLGuidAndRenderRoot>(
+          "layers::IAPZCTreeManager::StopAutoscroll", mTreeManager,
+          &IAPZCTreeManager::StopAutoscroll, aGuid));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetLongTapEnabled(
     const bool& aLongTapEnabled) {
   mUpdater->RunOnControllerThread(
-      mLayersId,
+      UpdaterQueueSelector(mWrRootId),
       NewRunnableMethod<bool>(
           "layers::IAPZCTreeManager::SetLongTapEnabled", mTreeManager,
           &IAPZCTreeManager::SetLongTapEnabled, aLongTapEnabled));
 
   return IPC_OK();
 }
 
+bool APZCTreeManagerParent::IsGuidValid(const SLGuidAndRenderRoot& aGuid) {
+  if (aGuid.mScrollableLayerGuid.mLayersId != mWrRootId.mLayersId) {
+    NS_ERROR("Unexpected layers id");
+    return false;
+  }
+  if (mWrRootId.mRenderRoot == wr::RenderRoot::Content) {
+    // If this APZCTreeManagerParent is for a content process IPDL bridge, then
+    // all the render root references that come over the bridge must be for
+    // the content render root.
+    if (aGuid.mRenderRoot != wr::RenderRoot::Content) {
+      NS_ERROR("Unexpected render root");
+      return false;
+    }
+  }
+  return true;
+}
+
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/ipc/APZCTreeManagerParent.h
+++ b/gfx/layers/ipc/APZCTreeManagerParent.h
@@ -12,65 +12,67 @@
 namespace mozilla {
 namespace layers {
 
 class APZCTreeManager;
 class APZUpdater;
 
 class APZCTreeManagerParent : public PAPZCTreeManagerParent {
  public:
-  APZCTreeManagerParent(LayersId aLayersId,
+  APZCTreeManagerParent(WRRootId aWrRootId,
                         RefPtr<APZCTreeManager> aAPZCTreeManager,
                         RefPtr<APZUpdater> mAPZUpdater);
   virtual ~APZCTreeManagerParent();
 
-  LayersId GetLayersId() const { return mLayersId; }
+  LayersId GetLayersId() const { return mWrRootId.mLayersId; }
 
   /**
    * Called when the layer tree that this protocol is connected to
    * is adopted by another compositor, and we need to switch APZCTreeManagers.
    */
   void ChildAdopted(RefPtr<APZCTreeManager> aAPZCTreeManager,
                     RefPtr<APZUpdater> aAPZUpdater);
 
   mozilla::ipc::IPCResult RecvSetKeyboardMap(const KeyboardMap& aKeyboardMap);
 
-  mozilla::ipc::IPCResult RecvZoomToRect(const ScrollableLayerGuid& aGuid,
+  mozilla::ipc::IPCResult RecvZoomToRect(const SLGuidAndRenderRoot& aGuid,
                                          const CSSRect& aRect,
                                          const uint32_t& aFlags);
 
   mozilla::ipc::IPCResult RecvContentReceivedInputBlock(
       const uint64_t& aInputBlockId, const bool& aPreventDefault);
 
   mozilla::ipc::IPCResult RecvSetTargetAPZC(
-      const uint64_t& aInputBlockId, nsTArray<ScrollableLayerGuid>&& aTargets);
+      const uint64_t& aInputBlockId, nsTArray<SLGuidAndRenderRoot>&& aTargets);
 
   mozilla::ipc::IPCResult RecvUpdateZoomConstraints(
-      const ScrollableLayerGuid& aGuid,
+      const SLGuidAndRenderRoot& aGuid,
       const MaybeZoomConstraints& aConstraints);
 
   mozilla::ipc::IPCResult RecvSetDPI(const float& aDpiValue);
 
   mozilla::ipc::IPCResult RecvSetAllowedTouchBehavior(
       const uint64_t& aInputBlockId, nsTArray<TouchBehaviorFlags>&& aValues);
 
   mozilla::ipc::IPCResult RecvStartScrollbarDrag(
-      const ScrollableLayerGuid& aGuid, const AsyncDragMetrics& aDragMetrics);
+      const SLGuidAndRenderRoot& aGuid, const AsyncDragMetrics& aDragMetrics);
 
   mozilla::ipc::IPCResult RecvStartAutoscroll(
-      const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation);
+      const SLGuidAndRenderRoot& aGuid, const ScreenPoint& aAnchorLocation);
 
-  mozilla::ipc::IPCResult RecvStopAutoscroll(const ScrollableLayerGuid& aGuid);
+  mozilla::ipc::IPCResult RecvStopAutoscroll(const SLGuidAndRenderRoot& aGuid);
 
   mozilla::ipc::IPCResult RecvSetLongTapEnabled(const bool& aTapGestureEnabled);
 
   void ActorDestroy(ActorDestroyReason aWhy) override {}
 
  private:
-  LayersId mLayersId;
+  bool IsGuidValid(const SLGuidAndRenderRoot& aGuid);
+
+  WRRootId mWrRootId;
   RefPtr<APZCTreeManager> mTreeManager;
   RefPtr<APZUpdater> mUpdater;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif  // mozilla_layers_APZCTreeManagerParent_h
--- a/gfx/layers/ipc/CompositableForwarder.h
+++ b/gfx/layers/ipc/CompositableForwarder.h
@@ -76,36 +76,48 @@ class CompositableForwarder : public Kno
 
   /**
    * 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;
+  virtual void RemoveTextureFromCompositable(
+      CompositableClient* aCompositable, TextureClient* aTexture,
+      const Maybe<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,
+                           const Maybe<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
@@ -466,17 +466,18 @@ void CompositorBridgeParent::StopAndClea
         lts->mParent = nullptr;
       });
     }
     for (const RefPtr<WebRenderBridgeParent>& bridge : indirectBridgeParents) {
       bridge->Destroy();
     }
     indirectBridgeParents.clear();
 
-    RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+    RefPtr<wr::WebRenderAPI> api =
+        mWrBridge->GetWebRenderAPI(wr::RenderRoot::Default);
     // Ensure we are not holding the sIndirectLayerTreesLock here because we
     // are going to block on WR threads in order to shut it down properly.
     mWrBridge->Destroy();
     mWrBridge = nullptr;
 
     if (api) {
       // Make extra sure we are done cleaning WebRender up before continuing.
       // After that we wont have a way to talk to a lot of the webrender parts.
@@ -642,21 +643,22 @@ void CompositorBridgeParent::ActorDestro
   // on this thread. We must keep the compositor parent alive untill the code
   // handling message reception is finished on this thread.
   mSelfRef = this;
   MessageLoop::current()->PostTask(
       NewRunnableMethod("layers::CompositorBridgeParent::DeferredDestroy", this,
                         &CompositorBridgeParent::DeferredDestroy));
 }
 
-void CompositorBridgeParent::ScheduleRenderOnCompositorThread() {
+void CompositorBridgeParent::ScheduleRenderOnCompositorThread(
+    const Maybe<wr::RenderRoot>& aRenderRoot) {
   MOZ_ASSERT(CompositorLoop());
-  CompositorLoop()->PostTask(
-      NewRunnableMethod("layers::CompositorBridgeParent::ScheduleComposition",
-                        this, &CompositorBridgeParent::ScheduleComposition));
+  CompositorLoop()->PostTask(NewRunnableMethod<Maybe<wr::RenderRoot>>(
+      "layers::CompositorBridgeParent::ScheduleComposition", this,
+      &CompositorBridgeParent::ScheduleComposition, aRenderRoot));
 }
 
 void CompositorBridgeParent::InvalidateOnCompositorThread() {
   MOZ_ASSERT(CompositorLoop());
   CompositorLoop()->PostTask(
       NewRunnableMethod("layers::CompositorBridgeParent::Invalidate", this,
                         &CompositorBridgeParent::Invalidate));
 }
@@ -839,39 +841,45 @@ void CompositorBridgeParent::NotifyShado
     // If plugins haven't been updated, stop waiting.
     if (!pluginsUpdatedFlag) {
       mWaitForPluginsUntil = TimeStamp();
       mHaveBlockedForPlugins = false;
     }
 #endif
 
     if (mApzUpdater) {
-      mApzUpdater->UpdateFocusState(mRootLayerTreeID, aId, aFocusTarget);
+      mApzUpdater->UpdateFocusState(mRootLayerTreeID,
+                                    WRRootId::NonWebRender(aId), aFocusTarget);
       if (aHitTestUpdate) {
         mApzUpdater->UpdateHitTestingTree(
             mRootLayerTreeID, mLayerManager->GetRoot(), aIsFirstPaint, aId,
             aPaintSequenceNumber);
       }
     }
 
     mLayerManager->NotifyShadowTreeTransaction();
   }
   if (aScheduleComposite) {
     ScheduleComposition();
   }
 }
 
-void CompositorBridgeParent::ScheduleComposition() {
+void CompositorBridgeParent::ScheduleComposition(
+    const Maybe<wr::RenderRoot>& aRenderRoot) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (mPaused) {
     return;
   }
 
   if (mWrBridge) {
-    mWrBridge->ScheduleGenerateFrame();
+    if (aRenderRoot.isSome()) {
+      mWrBridge->ScheduleGenerateFrame(aRenderRoot);
+    } else {
+      mWrBridge->ScheduleGenerateFrameAllRenderRoots();
+    }
   } else {
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 // Go down the composite layer tree, setting properties to match their
 // content-side counterparts.
 /* static */
@@ -1092,36 +1100,37 @@ PAPZCTreeManagerParent* CompositorBridge
   MOZ_ASSERT(!aLayersId.IsValid());
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   CompositorBridgeParent::LayerTreeState& state =
       sIndirectLayerTrees[mRootLayerTreeID];
   MOZ_ASSERT(state.mParent.get() == this);
   MOZ_ASSERT(!state.mApzcTreeManagerParent);
   state.mApzcTreeManagerParent = new APZCTreeManagerParent(
-      mRootLayerTreeID, mApzcTreeManager, mApzUpdater);
+      WRRootId(mRootLayerTreeID, wr::RenderRoot::Default), mApzcTreeManager,
+      mApzUpdater);
 
   return state.mApzcTreeManagerParent;
 }
 
 bool CompositorBridgeParent::DeallocPAPZCTreeManagerParent(
     PAPZCTreeManagerParent* aActor) {
   delete aActor;
   return true;
 }
 
 void CompositorBridgeParent::AllocateAPZCTreeManagerParent(
     const MonitorAutoLock& aProofOfLayerTreeStateLock,
-    const LayersId& aLayersId, LayerTreeState& aState) {
+    const WRRootId& aWrRootId, LayerTreeState& aState) {
   MOZ_ASSERT(aState.mParent == this);
   MOZ_ASSERT(mApzcTreeManager);
   MOZ_ASSERT(mApzUpdater);
   MOZ_ASSERT(!aState.mApzcTreeManagerParent);
   aState.mApzcTreeManagerParent =
-      new APZCTreeManagerParent(aLayersId, mApzcTreeManager, mApzUpdater);
+      new APZCTreeManagerParent(aWrRootId, mApzcTreeManager, mApzUpdater);
 }
 
 PAPZParent* CompositorBridgeParent::AllocPAPZParent(const LayersId& aLayersId) {
   // The main process should pass in 0 because we assume mRootLayerTreeID
   MOZ_ASSERT(!aLayersId.IsValid());
 
   RemoteContentController* controller = new RemoteContentController();
 
@@ -1177,17 +1186,18 @@ CompositorBridgeParent::GetCompositorBri
        it++) {
     LayerTreeState* state = &it->second;
     if (!state->mWrBridge) {
       continue;
     }
     // state->mWrBridge might be a root WebRenderBridgeParent or one of a
     // content process, but in either case the state->mParent will be the same.
     // So we don't need to distinguish between the two.
-    if (RefPtr<wr::WebRenderAPI> api = state->mWrBridge->GetWebRenderAPI()) {
+    if (RefPtr<wr::WebRenderAPI> api =
+            state->mWrBridge->GetWebRenderAPI(wr::RenderRoot::Default)) {
       if (api->GetId() == aWindowId) {
         return state->mParent;
       }
     }
   }
   return nullptr;
 }
 
@@ -1228,17 +1238,18 @@ void CompositorBridgeParent::ShadowLayer
     mLayerManager->GetCompositor()->SetScreenRotation(targetConfig.rotation());
   }
 
   mCompositionManager->Updated(aInfo.isFirstPaint(), targetConfig);
   Layer* root = aLayerTree->GetRoot();
   mLayerManager->SetRoot(root);
 
   if (mApzUpdater && !aInfo.isRepeatTransaction()) {
-    mApzUpdater->UpdateFocusState(mRootLayerTreeID, mRootLayerTreeID,
+    mApzUpdater->UpdateFocusState(mRootLayerTreeID,
+                                  WRRootId::NonWebRender(mRootLayerTreeID),
                                   aInfo.focusTarget());
 
     if (aHitTestUpdate) {
       AutoResolveRefLayers resolve(mCompositionManager);
 
       mApzUpdater->UpdateHitTestingTree(mRootLayerTreeID, root,
                                         aInfo.isFirstPaint(), mRootLayerTreeID,
                                         aInfo.paintSequenceNumber());
@@ -1345,65 +1356,70 @@ CompositorAnimationStorage* CompositorBr
 
 mozilla::ipc::IPCResult CompositorBridgeParent::RecvGetFrameUniformity(
     FrameUniformityData* aOutData) {
   mCompositionManager->GetFrameUniformity(aOutData);
   return IPC_OK();
 }
 
 void CompositorBridgeParent::SetTestAsyncScrollOffset(
-    const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+    const WRRootId& aWrRootId, const ScrollableLayerGuid::ViewID& aScrollId,
     const CSSPoint& aPoint) {
   if (mApzUpdater) {
-    MOZ_ASSERT(aLayersId.IsValid());
-    mApzUpdater->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
+    MOZ_ASSERT(aWrRootId.IsValid());
+    mApzUpdater->SetTestAsyncScrollOffset(aWrRootId, aScrollId, aPoint);
   }
 }
 
 void CompositorBridgeParent::SetTestAsyncZoom(
-    const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+    const WRRootId& aWrRootId, const ScrollableLayerGuid::ViewID& aScrollId,
     const LayerToParentLayerScale& aZoom) {
   if (mApzUpdater) {
-    MOZ_ASSERT(aLayersId.IsValid());
-    mApzUpdater->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
+    MOZ_ASSERT(aWrRootId.IsValid());
+    mApzUpdater->SetTestAsyncZoom(aWrRootId, aScrollId, aZoom);
   }
 }
 
-void CompositorBridgeParent::FlushApzRepaints(const LayersId& aLayersId) {
+void CompositorBridgeParent::FlushApzRepaints(const WRRootId& aWrRootId) {
   MOZ_ASSERT(mApzUpdater);
-  MOZ_ASSERT(aLayersId.IsValid());
+  MOZ_ASSERT(aWrRootId.IsValid());
   mApzUpdater->RunOnControllerThread(
-      aLayersId, NS_NewRunnableFunction(
-                     "layers::CompositorBridgeParent::FlushApzRepaints",
-                     [=]() { APZCTreeManager::FlushApzRepaints(aLayersId); }));
+      UpdaterQueueSelector(aWrRootId),
+      NS_NewRunnableFunction(
+          "layers::CompositorBridgeParent::FlushApzRepaints",
+          [=]() { APZCTreeManager::FlushApzRepaints(aWrRootId.mLayersId); }));
 }
 
-void CompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
+void CompositorBridgeParent::GetAPZTestData(const WRRootId& aWrRootId,
                                             APZTestData* aOutData) {
   if (mApzUpdater) {
-    MOZ_ASSERT(aLayersId.IsValid());
-    mApzUpdater->GetAPZTestData(aLayersId, aOutData);
+    MOZ_ASSERT(aWrRootId.IsValid());
+    mApzUpdater->GetAPZTestData(aWrRootId, aOutData);
   }
 }
 
 void CompositorBridgeParent::SetConfirmedTargetAPZC(
     const LayersId& aLayersId, const uint64_t& aInputBlockId,
-    const nsTArray<ScrollableLayerGuid>& aTargets) {
+    const nsTArray<SLGuidAndRenderRoot>& aTargets) {
   if (!mApzcTreeManager || !mApzUpdater) {
     return;
   }
   // Need to specifically bind this since it's overloaded.
   void (APZCTreeManager::*setTargetApzcFunc)(
-      uint64_t, const nsTArray<ScrollableLayerGuid>&) =
+      uint64_t, const nsTArray<SLGuidAndRenderRoot>&) =
       &APZCTreeManager::SetTargetAPZC;
   RefPtr<Runnable> task = NewRunnableMethod<
-      uint64_t, StoreCopyPassByConstLRef<nsTArray<ScrollableLayerGuid>>>(
+      uint64_t, StoreCopyPassByConstLRef<nsTArray<SLGuidAndRenderRoot>>>(
       "layers::CompositorBridgeParent::SetConfirmedTargetAPZC",
       mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets);
-  mApzUpdater->RunOnControllerThread(aLayersId, task.forget());
+  UpdaterQueueSelector selector(aLayersId);
+  for (size_t i = 0; i < aTargets.Length(); i++) {
+    selector.mRenderRoots += aTargets[i].mRenderRoot;
+  }
+  mApzUpdater->RunOnControllerThread(selector, task.forget());
 }
 
 void CompositorBridgeParent::InitializeLayerManager(
     const nsTArray<LayersBackend>& aBackendHints) {
   NS_ASSERTION(!mLayerManager, "Already initialised mLayerManager");
   NS_ASSERTION(!mCompositor, "Already initialised mCompositor");
 
   if (!InitializeAdvancedLayers(aBackendHints, nullptr)) {
@@ -1686,21 +1702,23 @@ mozilla::ipc::IPCResult CompositorBridge
   }
 
   if (scheduleComposition) {
     ScheduleComposition();
   }
 
   if (childWrBridge) {
     MOZ_ASSERT(mWrBridge);
-    RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
-    api = api->Clone();
+    nsTArray<RefPtr<wr::WebRenderAPI>> apis;
+    DebugOnly<bool> cloneSuccess = mWrBridge->CloneWebRenderAPIs(apis);
+    MOZ_ASSERT(cloneSuccess);
     wr::Epoch newEpoch = childWrBridge->UpdateWebRender(
-        mWrBridge->CompositorScheduler(), api, mWrBridge->AsyncImageManager(),
-        GetAnimationStorage(), mWrBridge->GetTextureFactoryIdentifier());
+        mWrBridge->CompositorScheduler(), std::move(apis),
+        mWrBridge->AsyncImageManager(), GetAnimationStorage(),
+        mWrBridge->GetTextureFactoryIdentifier());
     // Pretend we composited, since parent CompositorBridgeParent was replaced.
     TimeStamp now = TimeStamp::Now();
     NotifyPipelineRendered(childWrBridge->PipelineId(), newEpoch, VsyncId(),
                            now, now, now);
   }
 
   if (oldApzUpdater) {
     // We don't support moving a child from an APZ-enabled compositor to a
@@ -1711,17 +1729,18 @@ mozilla::ipc::IPCResult CompositorBridge
     // composited.
     MOZ_ASSERT(mApzUpdater);
   }
   if (mApzUpdater) {
     if (parent) {
       MOZ_ASSERT(mApzcTreeManager);
       parent->ChildAdopted(mApzcTreeManager, mApzUpdater);
     }
-    mApzUpdater->NotifyLayerTreeAdopted(child, oldApzUpdater);
+    mApzUpdater->NotifyLayerTreeAdopted(
+        WRRootId(child, gfxUtils::GetContentRenderRoot()), oldApzUpdater);
   }
   return IPC_OK();
 }
 
 PWebRenderBridgeParent* CompositorBridgeParent::AllocPWebRenderBridgeParent(
     const wr::PipelineId& aPipelineId, const LayoutDeviceIntSize& aSize) {
 #ifndef MOZ_BUILD_WEBRENDER
   // Extra guard since this in the parent process and we don't want a malicious
@@ -1747,31 +1766,43 @@ PWebRenderBridgeParent* CompositorBridge
     // before the updater thread is created in WebRenderAPI::Create, so
     // that the callback from the updater thread can find the right APZUpdater.
     mApzUpdater->SetWebRenderWindowId(windowId);
   }
   if (mApzSampler) {
     // Same as for mApzUpdater, but for the sampler thread.
     mApzSampler->SetWebRenderWindowId(windowId);
   }
-  RefPtr<wr::WebRenderAPI> api =
-      wr::WebRenderAPI::Create(this, std::move(widget), windowId, aSize);
-  if (!api) {
+  InfallibleTArray<RefPtr<wr::WebRenderAPI>> apis;
+  apis.AppendElement(
+      wr::WebRenderAPI::Create(this, std::move(widget), windowId, aSize));
+  if (!apis[0]) {
     mWrBridge = WebRenderBridgeParent::CreateDestroyed(aPipelineId);
     mWrBridge.get()->AddRef();  // IPDL reference
     return mWrBridge;
   }
-  mAsyncImageManager = new AsyncImagePipelineManager(api->Clone());
+
+  if (gfxPrefs::WebRenderSplitRenderRoots()) {
+    apis.AppendElement(
+        apis[0]->CreateDocument(aSize, 1, wr::RenderRoot::Content));
+  }
+
+  InfallibleTArray<RefPtr<wr::WebRenderAPI>> clonedApis;
+  for (auto& api : apis) {
+    wr::TransactionBuilder txn;
+    txn.SetRootPipeline(aPipelineId);
+    api->SendTransaction(txn);
+    clonedApis.AppendElement(api->Clone());
+  }
+
+  mAsyncImageManager = new AsyncImagePipelineManager(std::move(clonedApis));
   RefPtr<AsyncImagePipelineManager> asyncMgr = mAsyncImageManager;
-  wr::TransactionBuilder txn;
-  txn.SetRootPipeline(aPipelineId);
-  api->SendTransaction(txn);
   RefPtr<CompositorAnimationStorage> animStorage = GetAnimationStorage();
   mWrBridge = new WebRenderBridgeParent(this, aPipelineId, mWidget, nullptr,
-                                        std::move(api), std::move(asyncMgr),
+                                        std::move(apis), 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);
@@ -1796,26 +1827,28 @@ bool CompositorBridgeParent::DeallocPWeb
     }
   }
   parent->Release();  // IPDL reference
   return true;
 }
 
 void CompositorBridgeParent::NotifyMemoryPressure() {
   if (mWrBridge) {
-    RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+    RefPtr<wr::WebRenderAPI> api =
+        mWrBridge->GetWebRenderAPI(wr::RenderRoot::Default);
     if (api) {
       api->NotifyMemoryPressure();
     }
   }
 }
 
 void CompositorBridgeParent::AccumulateMemoryReport(wr::MemoryReport* aReport) {
   if (mWrBridge) {
-    RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+    RefPtr<wr::WebRenderAPI> api =
+        mWrBridge->GetWebRenderAPI(wr::RenderRoot::Default);
     if (api) {
       api->AccumulateMemoryReport(aReport);
     }
   }
 }
 
 RefPtr<WebRenderBridgeParent> CompositorBridgeParent::GetWebRenderBridgeParent()
     const {
@@ -1837,17 +1870,18 @@ static void EraseLayerState(LayersId aId
       if (parent) {
         apz = parent->GetAPZUpdater();
       }
       sIndirectLayerTrees.erase(iter);
     }
   }
 
   if (apz) {
-    apz->NotifyLayerTreeRemoved(aId);
+    apz->NotifyLayerTreeRemoved(
+        WRRootId(aId, gfxUtils::GetContentRenderRoot()));
   }
 }
 
 /*static*/
 void CompositorBridgeParent::DeallocateLayerTreeId(LayersId aId) {
   MOZ_ASSERT(NS_IsMainThread());
   // Here main thread notifies compositor to remove an element from
   // sIndirectLayerTrees. This removed element might be queried soon.
@@ -2024,24 +2058,24 @@ void CompositorBridgeParent::DidComposit
     mTxnStartTime = TimeStamp();
     mFwdTime = TimeStamp();
 #endif
     mPendingTransaction = TransactionId{0};
   }
 }
 
 void CompositorBridgeParent::NotifyDidSceneBuild(
-    RefPtr<wr::WebRenderPipelineInfo> aInfo) {
+    wr::RenderRoot aRenderRoot, RefPtr<wr::WebRenderPipelineInfo> aInfo) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (mPaused) {
     return;
   }
 
   if (mWrBridge) {
-    mWrBridge->NotifyDidSceneBuild(aInfo);
+    mWrBridge->NotifyDidSceneBuild(aRenderRoot, aInfo);
   } else {
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 void CompositorBridgeParent::NotifyPipelineRendered(
     const wr::PipelineId& aPipelineId, const wr::Epoch& aEpoch,
     const VsyncId& aCompositeStartId, TimeStamp& aCompositeStart,
@@ -2242,17 +2276,18 @@ bool CompositorBridgeParent::IsSameProce
 
 void CompositorBridgeParent::NotifyWebRenderError(wr::WebRenderError aError) {
   MOZ_ASSERT(CompositorLoop() == MessageLoop::current());
   Unused << SendNotifyWebRenderError(aError);
 }
 
 void CompositorBridgeParent::NotifyWebRenderContextPurge() {
   MOZ_ASSERT(CompositorLoop() == MessageLoop::current());
-  RefPtr<wr::WebRenderAPI> api = mWrBridge->GetWebRenderAPI();
+  RefPtr<wr::WebRenderAPI> api =
+      mWrBridge->GetWebRenderAPI(wr::RenderRoot::Default);
   api->ClearAllCaches();
 }
 
 #if defined(XP_WIN) || defined(MOZ_WIDGET_GTK)
 //#define PLUGINS_LOG(...) printf_stderr("CP [%s]: ", __FUNCTION__);
 //                         printf_stderr(__VA_ARGS__);
 //                         printf_stderr("\n");
 #  define PLUGINS_LOG(...)
--- a/gfx/layers/ipc/CompositorBridgeParent.h
+++ b/gfx/layers/ipc/CompositorBridgeParent.h
@@ -120,27 +120,27 @@ class CompositorBridgeParentBase : publi
   virtual bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) {
     return true;
   }
   virtual void LeaveTestMode(const LayersId& aId) {}
   enum class TransformsToSkip : uint8_t { NoneOfThem = 0, APZ = 1 };
   virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree,
                                     TransformsToSkip aSkip) = 0;
   virtual void SetTestAsyncScrollOffset(
-      const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+      const WRRootId& aWrRootId, const ScrollableLayerGuid::ViewID& aScrollId,
       const CSSPoint& aPoint) = 0;
-  virtual void SetTestAsyncZoom(const LayersId& aLayersId,
+  virtual void SetTestAsyncZoom(const WRRootId& aWrRootId,
                                 const ScrollableLayerGuid::ViewID& aScrollId,
                                 const LayerToParentLayerScale& aZoom) = 0;
-  virtual void FlushApzRepaints(const LayersId& aLayersId) = 0;
-  virtual void GetAPZTestData(const LayersId& aLayersId,
+  virtual void FlushApzRepaints(const WRRootId& aWrRootId) = 0;
+  virtual void GetAPZTestData(const WRRootId& aWrRootId,
                               APZTestData* aOutData) {}
   virtual void SetConfirmedTargetAPZC(
       const LayersId& aLayersId, const uint64_t& aInputBlockId,
-      const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
+      const nsTArray<SLGuidAndRenderRoot>& aTargets) = 0;
   virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree,
                                const TimeDuration& aPaintTime) {}
   virtual void RegisterPayload(
       LayerTransactionParent* aLayerTree,
       const InfallibleTArray<CompositionPayload>& aPayload) {}
 
   ShmemAllocator* AsShmemAllocator() override { return this; }
 
@@ -353,28 +353,28 @@ class CompositorBridgeParent final : pub
                            const TransactionInfo& aInfo,
                            bool aHitTestUpdate) override;
   void ScheduleComposite(LayerTransactionParent* aLayerTree) override;
   bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override;
   void LeaveTestMode(const LayersId& aId) override;
   void ApplyAsyncProperties(LayerTransactionParent* aLayerTree,
                             TransformsToSkip aSkip) override;
   CompositorAnimationStorage* GetAnimationStorage();
-  void SetTestAsyncScrollOffset(const LayersId& aLayersId,
+  void SetTestAsyncScrollOffset(const WRRootId& aWrRootId,
                                 const ScrollableLayerGuid::ViewID& aScrollId,
                                 const CSSPoint& aPoint) override;
-  void SetTestAsyncZoom(const LayersId& aLayersId,
+  void SetTestAsyncZoom(const WRRootId& aWrRootId,
                         const ScrollableLayerGuid::ViewID& aScrollId,
                         const LayerToParentLayerScale& aZoom) override;
-  void FlushApzRepaints(const LayersId& aLayersId) override;
-  void GetAPZTestData(const LayersId& aLayersId,
+  void FlushApzRepaints(const WRRootId& aWrRootId) override;
+  void GetAPZTestData(const WRRootId& aWrRootId,
                       APZTestData* aOutData) override;
   void SetConfirmedTargetAPZC(
       const LayersId& aLayersId, const uint64_t& aInputBlockId,
-      const nsTArray<ScrollableLayerGuid>& aTargets) override;
+      const nsTArray<SLGuidAndRenderRoot>& aTargets) override;
   AsyncCompositionManager* GetCompositionManager(
       LayerTransactionParent* aLayerTree) override {
     return mCompositionManager;
   }
 
   PTextureParent* AllocPTextureParent(
       const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
@@ -387,17 +387,18 @@ class CompositorBridgeParent final : pub
   void NotifyWebRenderError(wr::WebRenderError aError);
   void NotifyWebRenderContextPurge();
   void NotifyPipelineRendered(const wr::PipelineId& aPipelineId,
                               const wr::Epoch& aEpoch,
                               const VsyncId& aCompositeStartId,
                               TimeStamp& aCompositeStart,
                               TimeStamp& aRenderStart, TimeStamp& aCompositeEnd,
                               wr::RendererStats* aStats = nullptr);
-  void NotifyDidSceneBuild(RefPtr<wr::WebRenderPipelineInfo> aInfo);
+  void NotifyDidSceneBuild(wr::RenderRoot aRenderRoot,
+                           RefPtr<wr::WebRenderPipelineInfo> aInfo);
   RefPtr<AsyncImagePipelineManager> GetAsyncImagePipelineManager() const;
 
   PCompositorWidgetParent* AllocPCompositorWidgetParent(
       const CompositorWidgetInitData& aInitData) override;
   bool DeallocPCompositorWidgetParent(PCompositorWidgetParent* aActor) override;
 
   void ObserveLayersUpdate(LayersId aLayersId, LayersObserverEpoch aEpoch,
                            bool aActive) override {}
@@ -413,27 +414,29 @@ class CompositorBridgeParent final : pub
 
   static void SetShadowProperties(Layer* aLayer);
 
   void NotifyChildCreated(LayersId aChild);
 
   void AsyncRender();
 
   // Can be called from any thread
-  void ScheduleRenderOnCompositorThread() override;
+  void ScheduleRenderOnCompositorThread(
+      const Maybe<wr::RenderRoot>& aRenderRoot = Nothing()) 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(
+      const Maybe<wr::RenderRoot>& aRenderRoot = Nothing());
   void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint,
                                    const FocusTarget& aFocusTarget,
                                    bool aScheduleComposite,
                                    uint32_t aPaintSequenceNumber,
                                    bool aIsRepeatTransaction,
                                    bool aHitTestUpdate);
 
   void UpdatePaintTime(LayerTransactionParent* aLayerTree,
@@ -579,17 +582,17 @@ class CompositorBridgeParent final : pub
   PAPZCTreeManagerParent* AllocPAPZCTreeManagerParent(
       const LayersId& aLayersId) override;
   bool DeallocPAPZCTreeManagerParent(PAPZCTreeManagerParent* aActor) override;
 
   // Helper method so that we don't have to expose mApzcTreeManager to
   // ContentCompositorBridgeParent.
   void AllocateAPZCTreeManagerParent(
       const MonitorAutoLock& aProofOfLayerTreeStateLock,
-      const LayersId& aLayersId, LayerTreeState& aLayerTreeStateToUpdate);
+      const WRRootId& aWrRootId, LayerTreeState& aLayerTreeStateToUpdate);
 
   PAPZParent* AllocPAPZParent(const LayersId& aLayersId) override;
   bool DeallocPAPZParent(PAPZParent* aActor) override;
 
 #if defined(MOZ_WIDGET_ANDROID)
   AndroidDynamicToolbarAnimator* GetAndroidDynamicToolbarAnimator();
 #endif
   RefPtr<APZSampler> GetAPZSampler();
--- a/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.cpp
@@ -30,16 +30,17 @@
 #include "mozilla/layers/WebRenderBridgeParent.h"
 #include "mozilla/layers/AsyncImagePipelineManager.h"
 #include "mozilla/mozalloc.h"  // for operator new, etc
 #include "nsDebug.h"           // for NS_ASSERTION, etc
 #include "nsTArray.h"          // for nsTArray
 #include "nsXULAppAPI.h"       // for XRE_GetIOMessageLoop
 #include "mozilla/Unused.h"
 #include "mozilla/StaticPtr.h"
+#include "gfxUtils.h"
 
 using namespace std;
 
 namespace mozilla {
 
 namespace layers {
 
 // defined in CompositorBridgeParent.cpp
@@ -137,20 +138,23 @@ ContentCompositorBridgeParent::AllocPAPZ
   // In this case return an empty APZCTM.
   if (!state.mParent) {
     // Note: we immediately call ClearTree since otherwise the APZCTM will
     // retain a reference to itself, through the checkerboard observer.
     LayersId dummyId{0};
     RefPtr<APZCTreeManager> temp = new APZCTreeManager(dummyId);
     RefPtr<APZUpdater> tempUpdater = new APZUpdater(temp, false);
     tempUpdater->ClearTree(dummyId);
-    return new APZCTreeManagerParent(aLayersId, temp, tempUpdater);
+    return new APZCTreeManagerParent(
+        WRRootId(aLayersId, gfxUtils::GetContentRenderRoot()), temp,
+        tempUpdater);
   }
 
-  state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state);
+  state.mParent->AllocateAPZCTreeManagerParent(
+      lock, WRRootId(aLayersId, gfxUtils::GetContentRenderRoot()), state);
   return state.mApzcTreeManagerParent;
 }
 bool ContentCompositorBridgeParent::DeallocPAPZCTreeManagerParent(
     PAPZCTreeManagerParent* aActor) {
   APZCTreeManagerParent* parent = static_cast<APZCTreeManagerParent*>(aActor);
 
   MonitorAutoLock lock(*sIndirectLayerTreesLock);
   auto iter = sIndirectLayerTrees.find(parent->GetLayersId());
@@ -220,40 +224,40 @@ ContentCompositorBridgeParent::AllocPWeb
     MOZ_ASSERT(sIndirectLayerTrees.find(layersId) != sIndirectLayerTrees.end());
     MOZ_ASSERT(sIndirectLayerTrees[layersId].mWrBridge == nullptr);
     cbp = sIndirectLayerTrees[layersId].mParent;
     if (cbp) {
       root = sIndirectLayerTrees[cbp->RootLayerTreeId()].mWrBridge;
     }
   }
 
-  RefPtr<wr::WebRenderAPI> api;
+  InfallibleTArray<RefPtr<wr::WebRenderAPI>> apis;
+  bool cloneSuccess = false;
   if (root) {
-    api = root->GetWebRenderAPI();
+    cloneSuccess = root->CloneWebRenderAPIs(apis);
   }
 
-  if (!root || !api) {
+  if (!cloneSuccess) {
     // 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();
   RefPtr<AsyncImagePipelineManager> holder = root->AsyncImageManager();
   RefPtr<CompositorAnimationStorage> animStorage = cbp->GetAnimationStorage();
   WebRenderBridgeParent* parent = new WebRenderBridgeParent(
-      this, aPipelineId, nullptr, root->CompositorScheduler(), std::move(api),
+      this, aPipelineId, nullptr, root->CompositorScheduler(), std::move(apis),
       std::move(holder), std::move(animStorage), cbp->GetVsyncInterval());
   parent->AddRef();  // IPDL reference
 
   {  // scope lock
     MonitorAutoLock lock(*sIndirectLayerTreesLock);
     sIndirectLayerTrees[layersId].mContentCompositorBridgeParent = this;
     sIndirectLayerTrees[layersId].mWrBridge = parent;
   }
@@ -476,70 +480,70 @@ void ContentCompositorBridgeParent::Appl
     return;
   }
 
   MOZ_ASSERT(state->mParent);
   state->mParent->ApplyAsyncProperties(aLayerTree, aSkip);
 }
 
 void ContentCompositorBridgeParent::SetTestAsyncScrollOffset(
-    const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+    const WRRootId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
     const CSSPoint& aPoint) {
   MOZ_ASSERT(aLayersId.IsValid());
   const CompositorBridgeParent::LayerTreeState* state =
-      CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+      CompositorBridgeParent::GetIndirectShadowTree(aLayersId.mLayersId);
   if (!state) {
     return;
   }
 
   MOZ_ASSERT(state->mParent);
   state->mParent->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
 }
 
 void ContentCompositorBridgeParent::SetTestAsyncZoom(
-    const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+    const WRRootId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
     const LayerToParentLayerScale& aZoom) {
   MOZ_ASSERT(aLayersId.IsValid());
   const CompositorBridgeParent::LayerTreeState* state =
-      CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+      CompositorBridgeParent::GetIndirectShadowTree(aLayersId.mLayersId);
   if (!state) {
     return;
   }
 
   MOZ_ASSERT(state->mParent);
   state->mParent->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
 }
 
 void ContentCompositorBridgeParent::FlushApzRepaints(
-    const LayersId& aLayersId) {
+    const WRRootId& aLayersId) {
   MOZ_ASSERT(aLayersId.IsValid());
   const CompositorBridgeParent::LayerTreeState* state =
-      CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+      CompositorBridgeParent::GetIndirectShadowTree(aLayersId.mLayersId);
   if (!state || !state->mParent) {
     return;
   }
 
   state->mParent->FlushApzRepaints(aLayersId);
 }
 
-void ContentCompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
+void ContentCompositorBridgeParent::GetAPZTestData(const WRRootId& aLayersId,
                                                    APZTestData* aOutData) {
   MOZ_ASSERT(aLayersId.IsValid());
   const CompositorBridgeParent::LayerTreeState* state =
-      CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
+      CompositorBridgeParent::GetIndirectShadowTree(aLayersId.mLayersId);
   if (!state || !state->mParent) {
     return;
   }
 
   state->mParent->GetAPZTestData(aLayersId, aOutData);
 }
 
 void ContentCompositorBridgeParent::SetConfirmedTargetAPZC(
     const LayersId& aLayersId, const uint64_t& aInputBlockId,
-    const nsTArray<ScrollableLayerGuid>& aTargets) {
+    const nsTArray<SLGuidAndRenderRoot>& aTargets) {
   MOZ_ASSERT(aLayersId.IsValid());
   const CompositorBridgeParent::LayerTreeState* state =
       CompositorBridgeParent::GetIndirectShadowTree(aLayersId);
   if (!state || !state->mParent) {
     return;
   }
 
   state->mParent->SetConfirmedTargetAPZC(aLayersId, aInputBlockId, aTargets);
--- a/gfx/layers/ipc/ContentCompositorBridgeParent.h
+++ b/gfx/layers/ipc/ContentCompositorBridgeParent.h
@@ -109,28 +109,28 @@ class ContentCompositorBridgeParent fina
                            const TransactionInfo& aInfo,
                            bool aHitTestUpdate) override;
   void ScheduleComposite(LayerTransactionParent* aLayerTree) override;
   void NotifyClearCachedResources(LayerTransactionParent* aLayerTree) override;
   bool SetTestSampleTime(const LayersId& aId, const TimeStamp& aTime) override;
   void LeaveTestMode(const LayersId& aId) override;
   void ApplyAsyncProperties(LayerTransactionParent* aLayerTree,
                             TransformsToSkip aSkip) override;
-  void SetTestAsyncScrollOffset(const LayersId& aLayersId,
+  void SetTestAsyncScrollOffset(const WRRootId& aLayersId,
                                 const ScrollableLayerGuid::ViewID& aScrollId,
                                 const CSSPoint& aPoint) override;
-  void SetTestAsyncZoom(const LayersId& aLayersId,
+  void SetTestAsyncZoom(const WRRootId& aLayersId,
                         const ScrollableLayerGuid::ViewID& aScrollId,
                         const LayerToParentLayerScale& aZoom) override;
-  void FlushApzRepaints(const LayersId& aLayersId) override;
-  void GetAPZTestData(const LayersId& aLayersId,
+  void FlushApzRepaints(const WRRootId& aLayersId) override;
+  void GetAPZTestData(const WRRootId& aLayersId,
                       APZTestData* aOutData) override;
   void SetConfirmedTargetAPZC(
       const LayersId& aLayersId, const uint64_t& aInputBlockId,
-      const nsTArray<ScrollableLayerGuid>& aTargets) override;
+      const nsTArray<SLGuidAndRenderRoot>& aTargets) override;
 
   AsyncCompositionManager* GetCompositionManager(
       LayerTransactionParent* aParent) override;
   mozilla::ipc::IPCResult RecvRemotePluginsReady() override {
     return IPC_FAIL_NO_REASON(this);
   }
 
   // Use DidCompositeLocked if you already hold a lock on
--- a/gfx/layers/ipc/ImageBridgeChild.cpp
+++ b/gfx/layers/ipc/ImageBridgeChild.cpp
@@ -94,17 +94,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,
+    const Maybe<wr::RenderRoot>& aRenderRoot) {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->GetIPCHandle());
   MOZ_ASSERT(aCompositable->IsConnected());
 
   AutoTArray<TimedTexture, 4> textures;
 
   for (auto& t : aTextures) {
     MOZ_ASSERT(t.mTextureClient);
@@ -316,17 +317,17 @@ void ImageBridgeChild::UpdateImageClient
 
   // 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, Nothing());
   EndTransaction();
 }
 
 void ImageBridgeChild::UpdateAsyncCanvasRendererSync(
     SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper) {
   AutoCompleteTask complete(aTask);
 
   UpdateAsyncCanvasRendererNow(aWrapper);
@@ -355,17 +356,18 @@ void ImageBridgeChild::UpdateAsyncCanvas
     AsyncCanvasRenderer* aWrapper) {
   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) {
   AutoCompleteTask complete(aTask);
 
@@ -937,17 +939,18 @@ bool ImageBridgeChild::DestroyInTransact
   return IBCAddOpDestroy(mTxn, OpDestroy(aTexture));
 }
 
 bool ImageBridgeChild::DestroyInTransaction(const CompositableHandle& aHandle) {
   return IBCAddOpDestroy(mTxn, OpDestroy(aHandle));
 }
 
 void ImageBridgeChild::RemoveTextureFromCompositable(
-    CompositableClient* aCompositable, TextureClient* aTexture) {
+    CompositableClient* aCompositable, TextureClient* aTexture,
+    const Maybe<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
@@ -260,19 +260,19 @@ class ImageBridgeChild final : public PI
   virtual void Connect(CompositableClient* aCompositable,
                        ImageContainer* aImageContainer) override;
 
   virtual bool UsesImageBridge() const override { return true; }
 
   /**
    * See CompositableForwarder::UseTextures
    */
-  virtual void UseTextures(
-      CompositableClient* aCompositable,
-      const nsTArray<TimedTextureClient>& aTextures) override;
+  virtual void UseTextures(CompositableClient* aCompositable,
+                           const nsTArray<TimedTextureClient>& aTextures,
+                           const Maybe<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);
 
@@ -289,18 +289,19 @@ class ImageBridgeChild final : public PI
    */
   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;
+  virtual void RemoveTextureFromCompositable(
+      CompositableClient* aCompositable, TextureClient* aTexture,
+      const Maybe<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/LayerTransactionParent.cpp
+++ b/gfx/layers/ipc/LayerTransactionParent.cpp
@@ -760,54 +760,61 @@ mozilla::ipc::IPCResult LayerTransaction
 
 mozilla::ipc::IPCResult LayerTransactionParent::RecvSetAsyncScrollOffset(
     const ScrollableLayerGuid::ViewID& aScrollID, const float& aX,
     const float& aY) {
   if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
     return IPC_FAIL_NO_REASON(this);
   }
 
-  mCompositorBridge->SetTestAsyncScrollOffset(GetId(), aScrollID,
-                                              CSSPoint(aX, aY));
+  mCompositorBridge->SetTestAsyncScrollOffset(WRRootId::NonWebRender(GetId()),
+                                              aScrollID, CSSPoint(aX, aY));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult LayerTransactionParent::RecvSetAsyncZoom(
     const ScrollableLayerGuid::ViewID& aScrollID, const float& aValue) {
   if (mDestroyed || !mLayerManager || mLayerManager->IsDestroyed()) {
     return IPC_FAIL_NO_REASON(this);
   }
 
-  mCompositorBridge->SetTestAsyncZoom(GetId(), aScrollID,
+  mCompositorBridge->SetTestAsyncZoom(WRRootId::NonWebRender(GetId()),
+                                      aScrollID,
                                       LayerToParentLayerScale(aValue));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult LayerTransactionParent::RecvFlushApzRepaints() {
-  mCompositorBridge->FlushApzRepaints(GetId());
+  mCompositorBridge->FlushApzRepaints(WRRootId::NonWebRender(GetId()));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult LayerTransactionParent::RecvGetAPZTestData(
     APZTestData* aOutData) {
-  mCompositorBridge->GetAPZTestData(GetId(), aOutData);
+  mCompositorBridge->GetAPZTestData(WRRootId::NonWebRender(GetId()), aOutData);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult LayerTransactionParent::RecvRequestProperty(
     const nsString& aProperty, float* aValue) {
   *aValue = -1;
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult LayerTransactionParent::RecvSetConfirmedTargetAPZC(
-    const uint64_t& aBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) {
+    const uint64_t& aBlockId, nsTArray<SLGuidAndRenderRoot>&& aTargets) {
   for (size_t i = 0; i < aTargets.Length(); i++) {
-    if (aTargets[i].mLayersId != GetId()) {
-      // Guard against bad data from hijacked child processes
+    // Guard against bad data from hijacked child processes
+    if (aTargets[i].mRenderRoot != wr::RenderRoot::Default) {
+      NS_ERROR(
+          "Unexpected render root in RecvSetConfirmedTargetAPZC; dropping "
+          "message...");
+      return IPC_FAIL(this, "Bad render root"); 
+    }
+    if (aTargets[i].mScrollableLayerGuid.mLayersId != GetId()) {
       NS_ERROR(
           "Unexpected layers id in RecvSetConfirmedTargetAPZC; dropping "
           "message...");
       return IPC_FAIL(this, "Bad layers id");
     }
   }
   mCompositorBridge->SetConfirmedTargetAPZC(GetId(), aBlockId, aTargets);
   return IPC_OK();
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -125,17 +125,17 @@ class LayerTransactionParent final : pub
       const ScrollableLayerGuid::ViewID& aId, const float& aX, const float& aY);
   mozilla::ipc::IPCResult RecvSetAsyncZoom(
       const ScrollableLayerGuid::ViewID& aId, const float& aValue);
   mozilla::ipc::IPCResult RecvFlushApzRepaints();
   mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* aOutData);
   mozilla::ipc::IPCResult RecvRequestProperty(const nsString& aProperty,
                                               float* aValue);
   mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(
-      const uint64_t& aBlockId, nsTArray<ScrollableLayerGuid>&& aTargets);
+      const uint64_t& aBlockId, nsTArray<SLGuidAndRenderRoot>&& aTargets);
   mozilla::ipc::IPCResult RecvRecordPaintTimes(const PaintTiming& aTiming);
   mozilla::ipc::IPCResult RecvGetTextureFactoryIdentifier(
       TextureFactoryIdentifier* aIdentifier);
 
   bool SetLayerAttributes(const OpSetLayerAttributes& aOp);
 
   void ActorDestroy(ActorDestroyReason why) override;
 
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -9,27 +9,29 @@
 
 #include "FrameMetrics.h"
 #include "base/process_util.h"
 #include "chrome/common/ipc_message_utils.h"
 #include "gfxTelemetry.h"
 #include "ipc/IPCMessageUtils.h"
 #include "ipc/nsGUIEventIPC.h"
 #include "mozilla/GfxMessageUtils.h"
+#include "mozilla/layers/APZTypes.h"
 #include "mozilla/layers/AsyncDragMetrics.h"
 #include "mozilla/layers/CompositorOptions.h"
 #include "mozilla/layers/CompositorTypes.h"
 #include "mozilla/layers/FocusTarget.h"
 #include "mozilla/layers/GeckoContentController.h"
 #include "mozilla/layers/KeyboardMap.h"
 #include "mozilla/layers/LayerAttributes.h"
 #include "mozilla/layers/LayersTypes.h"
 #include "mozilla/layers/MatrixMessage.h"
 #include "mozilla/layers/RefCountedShmem.h"
 #include "mozilla/layers/RepaintRequest.h"
+#include "mozilla/layers/WebRenderMessageUtils.h"
 #include "VsyncSource.h"
 #include "mozilla/Move.h"
 
 #include <stdint.h>
 
 #ifdef _MSC_VER
 #  pragma warning(disable : 4800)
 #endif
@@ -483,16 +485,32 @@ struct ParamTraits<mozilla::layers::Scro
                    paramType* aResult) {
     return (ReadParam(aMsg, aIter, &aResult->mLayersId) &&
             ReadParam(aMsg, aIter, &aResult->mPresShellId) &&
             ReadParam(aMsg, aIter, &aResult->mScrollId));
   }
 };
 
 template <>
+struct ParamTraits<mozilla::layers::SLGuidAndRenderRoot> {
+  typedef mozilla::layers::SLGuidAndRenderRoot paramType;
+
+  static void Write(Message* aMsg, const paramType& aParam) {
+    WriteParam(aMsg, aParam.mScrollableLayerGuid);
+    WriteParam(aMsg, aParam.mRenderRoot);
+  }
+
+  static bool Read(const Message* aMsg, PickleIterator* aIter,
+                   paramType* aResult) {
+    return (ReadParam(aMsg, aIter, &aResult->mScrollableLayerGuid) &&
+            ReadParam(aMsg, aIter, &aResult->mRenderRoot));
+  }
+};
+
+template <>
 struct ParamTraits<mozilla::layers::ZoomConstraints> {
   typedef mozilla::layers::ZoomConstraints paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     WriteParam(aMsg, aParam.mAllowZoom);
     WriteParam(aMsg, aParam.mAllowDoubleTapZoom);
     WriteParam(aMsg, aParam.mMinZoom);
     WriteParam(aMsg, aParam.mMaxZoom);
@@ -532,23 +550,27 @@ struct ParamTraits<mozilla::layers::Even
 };
 
 template <>
 struct ParamTraits<mozilla::layers::FocusTarget::ScrollTargets> {
   typedef mozilla::layers::FocusTarget::ScrollTargets paramType;
 
   static void Write(Message* aMsg, const paramType& aParam) {
     WriteParam(aMsg, aParam.mHorizontal);
+    WriteParam(aMsg, aParam.mHorizontalRenderRoot);
     WriteParam(aMsg, aParam.mVertical);
+    WriteParam(aMsg, aParam.mVerticalRenderRoot);
   }
 
   static bool Read(const Message* aMsg, PickleIterator* aIter,
                    paramType* aResult) {
     return ReadParam(aMsg, aIter, &aResult->mHorizontal) &&
-           ReadParam(aMsg, aIter, &aResult->mVertical);
+           ReadParam(aMsg, aIter, &aResult->mHorizontalRenderRoot) &&
+           ReadParam(aMsg, aIter, &aResult->mVertical) &&
+           ReadParam(aMsg, aIter, &aResult->mVerticalRenderRoot);
   }
 };
 
 template <>
 struct ParamTraits<mozilla::layers::FocusTarget::NoFocusTarget>
     : public EmptyStructSerializer<
           mozilla::layers::FocusTarget::NoFocusTarget> {};
 
--- a/gfx/layers/ipc/PAPZCTreeManager.ipdl
+++ b/gfx/layers/ipc/PAPZCTreeManager.ipdl
@@ -11,20 +11,22 @@ include protocol PCompositorBridge;
 
 using CSSRect from "Units.h";
 using LayoutDeviceCoord from "Units.h";
 using mozilla::LayoutDevicePoint from "Units.h";
 using ScreenPoint from "Units.h";
 using mozilla::layers::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h";
 using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
 using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::SLGuidAndRenderRoot from "mozilla/layers/APZTypes.h";
 using mozilla::layers::TouchBehaviorFlags from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::AsyncDragMetrics from "mozilla/layers/AsyncDragMetrics.h";
 using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h";
 using class mozilla::layers::KeyboardMap from "mozilla/layers/KeyboardMap.h";
+using mozilla::wr::RenderRoot from "mozilla/webrender/WebRenderTypes.h";
 
 using mozilla::Modifiers from "mozilla/EventForwards.h";
 using mozilla::PinchGestureInput::PinchGestureType from "InputData.h";
 
 namespace mozilla {
 namespace layers {
 
 /**
@@ -40,35 +42,35 @@ protocol PAPZCTreeManager
 {
 manager PCompositorBridge;
 
 parent:
 
   // These messages correspond to the methods
   // on the IAPZCTreeManager interface
 
-  async ZoomToRect(ScrollableLayerGuid aGuid, CSSRect aRect, uint32_t Flags);
+  async ZoomToRect(SLGuidAndRenderRoot aGuid, CSSRect aRect, uint32_t Flags);
 
   async ContentReceivedInputBlock(uint64_t aInputBlockId, bool PreventDefault);
 
-  async SetTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] Targets);
+  async SetTargetAPZC(uint64_t aInputBlockId, SLGuidAndRenderRoot[] Targets);
 
-  async UpdateZoomConstraints(ScrollableLayerGuid aGuid, MaybeZoomConstraints aConstraints);
+  async UpdateZoomConstraints(SLGuidAndRenderRoot aGuid, MaybeZoomConstraints aConstraints);
 
   async SetKeyboardMap(KeyboardMap aKeyboardMap);
 
   async SetDPI(float aDpiValue);
 
   async SetAllowedTouchBehavior(uint64_t aInputBlockId, TouchBehaviorFlags[] aValues);
 
-  async StartScrollbarDrag(ScrollableLayerGuid aGuid, AsyncDragMetrics aDragMetrics);
+  async StartScrollbarDrag(SLGuidAndRenderRoot aGuid, AsyncDragMetrics aDragMetrics);
 
-  async StartAutoscroll(ScrollableLayerGuid aGuid, ScreenPoint aAnchorLocation);
+  async StartAutoscroll(SLGuidAndRenderRoot aGuid, ScreenPoint aAnchorLocation);
 
-  async StopAutoscroll(ScrollableLayerGuid aGuid);
+  async StopAutoscroll(SLGuidAndRenderRoot aGuid);
 
   async SetLongTapEnabled(bool aTapGestureEnabled);
 
   async __delete__();
 
 child:
 
   async HandleTap(TapType aType, LayoutDevicePoint point, Modifiers aModifiers,
--- a/gfx/layers/ipc/PLayerTransaction.ipdl
+++ b/gfx/layers/ipc/PLayerTransaction.ipdl
@@ -13,17 +13,17 @@ include protocol PTexture;
 include "mozilla/GfxMessageUtils.h";
 include "mozilla/layers/LayersMessageUtils.h";
 
 using struct mozilla::layers::TextureInfo from "mozilla/layers/CompositorTypes.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using struct mozilla::null_t from "ipc/IPCMessageUtils.h";
 using class mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
 using mozilla::layers::ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
-using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::SLGuidAndRenderRoot from "mozilla/layers/APZTypes.h";
 using struct mozilla::layers::TextureFactoryIdentifier from "mozilla/layers/CompositorTypes.h";
 using mozilla::layers::LayersBackend from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayerHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::CompositableHandle from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::TransactionId from "mozilla/layers/LayersTypes.h";
 
 /**
@@ -61,17 +61,17 @@ parent:
   async NewCompositable(CompositableHandle handle, TextureInfo info);
 
   // Release an object that is no longer in use.
   async ReleaseLayer(LayerHandle layer);
   async ReleaseCompositable(CompositableHandle compositable);
 
   // Tell the compositor to notify APZ that a layer has been confirmed for an
   // input event.
-  async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
+  async SetConfirmedTargetAPZC(uint64_t aInputBlockId, SLGuidAndRenderRoot[] aTargets);
 
   // Testing APIs
 
   // Enter test mode, set the sample time to sampleTime, and resample
   // animations. sampleTime must not be null.
   sync SetTestSampleTime(TimeStamp sampleTime);
   // Leave test mode and resume normal compositing
   sync LeaveTestMode();
--- a/gfx/layers/ipc/PWebRenderBridge.ipdl
+++ b/gfx/layers/ipc/PWebRenderBridge.ipdl
@@ -10,26 +10,27 @@ include LayersMessages;
 include "mozilla/GfxMessageUtils.h";
 include "mozilla/layers/WebRenderMessageUtils.h";
 
 include WebRenderMessages;
 include protocol PCompositorBridge;
 include protocol PTexture;
 
 using mozilla::layers::APZTestData from "mozilla/layers/APZTestData.h";
-using mozilla::layers::ScrollUpdatesMap from "FrameMetrics.h";
-using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::SLGuidAndRenderRoot from "mozilla/layers/APZTypes.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::layers::WebRenderScrollData from "mozilla/layers/WebRenderScrollData.h";
+using mozilla::wr::RenderRoot from "mozilla/webrender/WebRenderTypes.h";
+using moveonly mozilla::layers::RenderRootDisplayListData from "mozilla/layers/RenderRootTypes.h";
+using moveonly mozilla::layers::RenderRootUpdates from "mozilla/layers/RenderRootTypes.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";
 using mozilla::VsyncId from "mozilla/VsyncDispatcher.h";
 
 namespace mozilla {
 namespace layers {
 
@@ -40,44 +41,49 @@ sync protocol PWebRenderBridge
 parent:
   sync EnsureConnected()
     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, VsyncId vsyncId, TimeStamp vsyncStartTime, 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, VsyncId vsyncId, TimeStamp vsyncStartTime, TimeStamp refreshStartTime, TimeStamp txnStartTime, nsCString txnURL, TimeStamp fwdTime);
+  async SetDisplayList(RenderRootDisplayListData[] displayLists,
+                       OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+                       IdNamespace aIdNamespace, bool containsSVGGroup,
+                       VsyncId vsyncId, TimeStamp vsyncStartTime,
+                       TimeStamp refreshStartTime, TimeStamp txnStartTime, nsCString txnURL, TimeStamp fwdTime);
+  async EmptyTransaction(FocusTarget focusTarget, uint32_t aPaintSequenceNumber,
+                         RenderRootUpdates[] renderRootUpdates,
+                         OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
+                         IdNamespace aIdNamespace,
+                         VsyncId vsyncId, TimeStamp vsyncStartTime,
+                         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();
 
   // Replacement for PCompositorBridge::SyncWithCompositor, but for WR. We need
   // it on PWebRenderBridge because it associated with a particular top-level
   // window, and PCompositorBridge doesn't allow doing that in a secure manner.
   sync SyncWithCompositor();
 
   // These correspond exactly to the equivalent APIs in PLayerTransaction -
   // see those for documentation.
-  async SetConfirmedTargetAPZC(uint64_t aInputBlockId, ScrollableLayerGuid[] aTargets);
+  async SetConfirmedTargetAPZC(uint64_t aInputBlockId, SLGuidAndRenderRoot[] aTargets);
   // More copied from PLayerTransaction, but these are only used for testing.
   sync SetTestSampleTime(TimeStamp sampleTime);
   sync LeaveTestMode();
   sync GetAnimationValue(uint64_t aCompositorAnimationsId) returns (OMTAValue value);
   sync SetAsyncScrollOffset(ViewID scrollId, float x, float y);
   sync SetAsyncZoom(ViewID scrollId, float zoom);
   async FlushApzRepaints();
   sync GetAPZTestData() returns (APZTestData data);
--- a/gfx/layers/ipc/ShadowLayers.cpp
+++ b/gfx/layers/ipc/ShadowLayers.cpp
@@ -399,17 +399,18 @@ void ShadowLayerForwarder::UpdateTexture
 
   mTxn->AddNoSwapPaint(CompositableOperation(
       aCompositable->GetIPCHandle(),
       OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion)));
 }
 
 void ShadowLayerForwarder::UseTextures(
     CompositableClient* aCompositable,
-    const nsTArray<TimedTextureClient>& aTextures) {
+    const nsTArray<TimedTextureClient>& aTextures,
+    const Maybe<wr::RenderRoot>& aRenderRoot) {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
   AutoTArray<TimedTexture, 4> textures;
 
@@ -478,17 +479,18 @@ bool ShadowLayerForwarder::DestroyInTran
 }
 
 bool ShadowLayerForwarder::DestroyInTransaction(
     const CompositableHandle& aHandle) {
   return AddOpDestroy(mTxn, OpDestroy(aHandle));
 }
 
 void ShadowLayerForwarder::RemoveTextureFromCompositable(
-    CompositableClient* aCompositable, TextureClient* aTexture) {
+    CompositableClient* aCompositable, TextureClient* aTexture,
+    const Maybe<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
@@ -212,33 +212,34 @@ class ShadowLayerForwarder final : publi
   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;
+  virtual void RemoveTextureFromCompositable(
+      CompositableClient* aCompositable, TextureClient* aTexture,
+      const Maybe<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;
+  virtual void UseTextures(CompositableClient* aCompositable,
+                           const nsTArray<TimedTextureClient>& aTextures,
+                           const Maybe<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.
    */
--- a/gfx/layers/moz.build
+++ b/gfx/layers/moz.build
@@ -90,16 +90,17 @@ EXPORTS.mozilla.dom += [
     'apz/util/CheckerboardReportService.h',
 ]
 
 EXPORTS.mozilla.layers += [
     'AnimationHelper.h',
     'AnimationInfo.h',
     'apz/public/APZInputBridge.h',
     'apz/public/APZSampler.h',
+    'apz/public/APZTypes.h',
     'apz/public/APZUpdater.h',
     'apz/public/CompositorController.h',
     'apz/public/GeckoContentController.h',
     'apz/public/IAPZCTreeManager.h',
     'apz/public/MatrixMessage.h',
     'apz/public/MetricsSharingController.h',
     # exporting things from apz/src is temporary until we extract a
     # proper interface for the code there
@@ -240,17 +241,19 @@ EXPORTS.mozilla.layers += [
     'SyncObject.h',
     'TextureSourceProvider.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
     'UpdateImageHelper.h',
     'wr/AsyncImagePipelineManager.h',
     'wr/ClipManager.h',
     'wr/IpcResourceUpdateQueue.h',
+    'wr/RenderRootBoundary.h',
     'wr/RenderRootStateManager.h',
+    'wr/RenderRootTypes.h',
     'wr/StackingContextHelper.h',
     'wr/WebRenderBridgeChild.h',
     'wr/WebRenderBridgeParent.h',
     'wr/WebRenderCanvasRenderer.h',
     'wr/WebRenderCommandBuilder.h',
     'wr/WebRenderDrawEventRecorder.h',
     'wr/WebRenderImageHost.h',
     'wr/WebRenderLayerManager.h',
@@ -487,16 +490,17 @@ UNIFIED_SOURCES += [
     'SourceSurfaceVolatileData.cpp',
     'SyncObject.cpp',
     'TextureSourceProvider.cpp',
     'TextureWrapperImage.cpp',
     'wr/AsyncImagePipelineManager.cpp',
     'wr/ClipManager.cpp',
     'wr/IpcResourceUpdateQueue.cpp',
     'wr/RenderRootStateManager.cpp',
+    'wr/RenderRootTypes.cpp',
     'wr/StackingContextHelper.cpp',
     'wr/WebRenderBridgeChild.cpp',
     'wr/WebRenderBridgeParent.cpp',
     'wr/WebRenderCanvasRenderer.cpp',
     'wr/WebRenderCommandBuilder.cpp',
     'wr/WebRenderDrawEventRecorder.cpp',
     'wr/WebRenderImageHost.cpp',
     'wr/WebRenderLayerManager.cpp',
--- a/gfx/layers/wr/AsyncImagePipelineManager.cpp
+++ b/gfx/layers/wr/AsyncImagePipelineManager.cpp
@@ -22,64 +22,73 @@ namespace layers {
 
 AsyncImagePipelineManager::ForwardingExternalImage::~ForwardingExternalImage() {
   DebugOnly<bool> released = SharedSurfacesParent::Release(mImageId);
   MOZ_ASSERT(released);
 }
 
 AsyncImagePipelineManager::AsyncImagePipeline::AsyncImagePipeline()
     : mInitialised(false),
+      mRenderRoot(wr::RenderRoot::Default),
       mIsChanged(false),
       mUseExternalImage(false),
       mFilter(wr::ImageRendering::Auto),
       mMixBlendMode(wr::MixBlendMode::Normal) {}
 
 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)
-    : mApi(aApi),
-      mIdNamespace(mApi->GetNamespace()),
-      mUseTripleBuffering(mApi->GetUseTripleBuffering()),
+    nsTArray<RefPtr<wr::WebRenderAPI>>&& aApis)
+    : mApis(aApis),
+      mIdNamespace(mApis[0]->GetNamespace()),
+      mUseTripleBuffering(mApis[0]->GetUseTripleBuffering()),
       mResourceId(0),
       mAsyncImageEpoch{0},
-      mWillGenerateFrame(false),
+      mWillGenerateFrame{},
       mDestroyed(false),
       mUpdatesLock("UpdatesLock"),
       mUpdatesCount(0) {
   MOZ_COUNT_CTOR(AsyncImagePipelineManager);
 }
 
 AsyncImagePipelineManager::~AsyncImagePipelineManager() {
   MOZ_COUNT_DTOR(AsyncImagePipelineManager);
 }
 
 void AsyncImagePipelineManager::Destroy() {
   MOZ_ASSERT(!mDestroyed);
-  mApi = nullptr;
+  mApis.Clear();
   mPipelineTexturesHolders.Clear();
   mDestroyed = true;
 }
 
-void AsyncImagePipelineManager::SetWillGenerateFrame() {
-  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
-
-  mWillGenerateFrame = true;
+void AsyncImagePipelineManager::SetWillGenerateFrameAllRenderRoots() {
+  for (auto renderRoot : wr::kRenderRoots) {
+    SetWillGenerateFrame(renderRoot);
+  }
 }
 
-bool AsyncImagePipelineManager::GetAndResetWillGenerateFrame() {
+void AsyncImagePipelineManager::SetWillGenerateFrame(
+    wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
-  bool ret = mWillGenerateFrame;
-  mWillGenerateFrame = false;
+  mWillGenerateFrame[aRenderRoot] = true;
+}
+
+bool AsyncImagePipelineManager::GetAndResetWillGenerateFrame(
+    wr::RenderRoot aRenderRoot) {
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+  bool ret = mWillGenerateFrame[aRenderRoot];
+  mWillGenerateFrame[aRenderRoot] = false;
   return ret;
 }
 
 wr::ExternalImageId AsyncImagePipelineManager::GetNextExternalImageId() {
   static uint32_t sNextId = 0;
   ++sNextId;
   MOZ_RELEASE_ASSERT(sNextId != UINT32_MAX);
   // gecko allocates external image id as (IdNamespace:32bit +
@@ -140,25 +149,27 @@ WebRenderBridgeParent* AsyncImagePipelin
     MOZ_ASSERT(holder->mDestroyedEpoch.isNothing());
     return holder->mWrBridge;
   }
 
   return nullptr;
 }
 
 void AsyncImagePipelineManager::AddAsyncImagePipeline(
-    const wr::PipelineId& aPipelineId, WebRenderImageHost* aImageHost) {
+    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) {
   if (mDestroyed) {
@@ -281,17 +292,17 @@ Maybe<TextureHost::ResourceUpdateOp> Asy
 
   if (useWrTextureWrapper && aPipeline->mWrTextureWrapper) {
     MOZ_ASSERT(canUpdate);
     // Reuse WebRenderTextureHostWrapper. With it, rendered frame could be
     // updated without batch re-creation.
     aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(aMaybeFastTxn,
                                                              wrTexture);
     // Ensure frame generation.
-    SetWillGenerateFrame();
+    SetWillGenerateFrame(aPipeline->mRenderRoot);
   } else {
     if (useWrTextureWrapper) {
       aPipeline->mWrTextureWrapper = new WebRenderTextureHostWrapper(this);
       aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(aMaybeFastTxn,
                                                                wrTexture);
     }
     Range<wr::ImageKey> keys(&aKeys[0], aKeys.Length());
     auto externalImageKey =
@@ -342,35 +353,36 @@ AsyncImagePipelineManager::UpdateWithout
   }
 
   dSurf->Unmap();
 
   return Some(aOp);
 }
 
 void AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(
-    wr::TransactionBuilder& aSceneBuilderTxn,
-    wr::TransactionBuilder& aFastTxn) {
+    wr::RenderRootArray<Maybe<wr::TransactionBuilder>>& aSceneBuilderTxns,
+    wr::RenderRootArray<Maybe<wr::TransactionBuilder>>& aFastTxns) {
   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,
+                               *aSceneBuilderTxns[pipeline->mRenderRoot],
+                               *aFastTxns[pipeline->mRenderRoot]);
   }
 }
 
 void AsyncImagePipelineManager::ApplyAsyncImageForPipeline(
     const wr::Epoch& aEpoch, const wr::PipelineId& aPipelineId,
     AsyncImagePipeline* aPipeline, wr::TransactionBuilder& aSceneBuilderTxn,
     wr::TransactionBuilder& aMaybeFastTxn) {
   nsTArray<wr::ImageKey> keys;
@@ -451,24 +463,25 @@ void AsyncImagePipelineManager::ApplyAsy
   aSceneBuilderTxn.SetDisplayList(
       gfx::Color(0.f, 0.f, 0.f, 0.f), aEpoch,
       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 = mApis[(size_t)pipeline->mRenderRoot];
   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
@@ -665,21 +678,22 @@ void AsyncImagePipelineManager::ProcessP
         break;
       }
       holder->mExternalImages.pop();
     }
   }
 }
 
 void AsyncImagePipelineManager::ProcessPipelineRemoved(
-    const wr::PipelineId& aPipelineId, const uint64_t aUpdatesCount) {
+    const wr::RemovedPipeline& aRemovedPipeline, const uint64_t aUpdatesCount) {
   if (mDestroyed) {
     return;
   }
-  if (auto entry = mPipelineTexturesHolders.Lookup(wr::AsUint64(aPipelineId))) {
+  if (auto entry = mPipelineTexturesHolders.Lookup(
+          wr::AsUint64(aRemovedPipeline.pipeline_id))) {
     PipelineTexturesHolder* holder = entry.Data();
     if (holder->mDestroyedEpoch.isSome()) {
       while (!holder->mTextureHosts.empty()) {
         // Need to extend holding TextureHost if it is direct bounded texture.
         HoldUntilNotUsedByGPU(holder->mTextureHosts.front().mTexture,
                               aUpdatesCount);
         holder->mTextureHosts.pop();
       }
--- a/gfx/layers/wr/AsyncImagePipelineManager.h
+++ b/gfx/layers/wr/AsyncImagePipelineManager.h
@@ -33,17 +33,18 @@ class CompositorVsyncScheduler;
 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(
+      nsTArray<RefPtr<wr::WebRenderAPI>>&& aApis);
 
  protected:
   ~AsyncImagePipelineManager();
 
  public:
   void Destroy();
 
   void AddPipeline(const wr::PipelineId& aPipelineId,
@@ -82,56 +83,60 @@ class AsyncImagePipelineManager final {
   void CompositeUntil(TimeStamp aTimeStamp) {
     if (mCompositeUntilTime.IsNull() || mCompositeUntilTime < aTimeStamp) {
       mCompositeUntilTime = aTimeStamp;
     }
   }
   TimeStamp GetCompositeUntilTime() const { return mCompositeUntilTime; }
 
   void AddAsyncImagePipeline(const wr::PipelineId& aPipelineId,
-                             WebRenderImageHost* aImageHost);
+                             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 ApplyAsyncImagesOfImageBridge(
+      wr::RenderRootArray<Maybe<wr::TransactionBuilder>>& aSceneBuilderTxns,
+      wr::RenderRootArray<Maybe<wr::TransactionBuilder>>& aFastTxns);
   void ApplyAsyncImageForPipeline(const wr::PipelineId& aPipelineId,
                                   wr::TransactionBuilder& aTxn,
-                                  wr::TransactionBuilder& aTxnForImageBridge);
+                                  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 SetWillGenerateFrameAllRenderRoots();
+  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,
+  void ProcessPipelineRemoved(const wr::RemovedPipeline& aRemovedPipeline,
                               const uint64_t aUpdatesCount);
 
   wr::Epoch GetNextImageEpoch();
   uint32_t GetNextResourceId() { return ++mResourceId; }
   wr::IdNamespace GetNamespace() { return mIdNamespace; }
   wr::ImageKey GenerateImageKey() {
     wr::ImageKey key;
     key.mNamespace = GetNamespace();
@@ -186,16 +191,17 @@ class AsyncImagePipelineManager final {
       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;
@@ -218,26 +224,26 @@ class AsyncImagePipelineManager final {
       TextureHost* aTexture, wr::ImageKey aKey, 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;
+  nsTArray<RefPtr<wr::WebRenderAPI>> mApis;
   const wr::IdNamespace mIdNamespace;
   const bool mUseTripleBuffering;
   uint32_t mResourceId;
 
   nsClassHashtable<nsUint64HashKey, PipelineTexturesHolder>
       mPipelineTexturesHolders;
   nsClassHashtable<nsUint64HashKey, AsyncImagePipeline> mAsyncImagePipelines;
   wr::Epoch mAsyncImageEpoch;
-  bool mWillGenerateFrame;
+  wr::RenderRootArray<bool> mWillGenerateFrame;
   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
@@ -244,32 +244,53 @@ bool ShmSegmentsReader::Read(const layer
     srcCursor += copyRange;
     remainingBytesToCopy -= copyRange;
   }
 
   return aInto.Length() - initialLength == aRange.length();
 }
 
 IpcResourceUpdateQueue::IpcResourceUpdateQueue(
-    layers::WebRenderBridgeChild* aAllocator, size_t aChunkSize)
-    : mWriter(aAllocator, aChunkSize) {}
+    layers::WebRenderBridgeChild* aAllocator,
+    wr::RenderRoot aRenderRoot,
+    size_t aChunkSize)
+    : mWriter(aAllocator, aChunkSize), mRenderRoot(aRenderRoot) {}
 
 IpcResourceUpdateQueue::IpcResourceUpdateQueue(
     IpcResourceUpdateQueue&& aOther) noexcept
     : mWriter(std::move(aOther.mWriter)),
-      mUpdates(std::move(aOther.mUpdates)) {}
+      mUpdates(std::move(aOther.mUpdates)),
+      mRenderRoot(aOther.mRenderRoot) {
+  for (auto renderRoot : wr::kNonDefaultRenderRoots) {
+    mSubQueues[renderRoot] = std::move(aOther.mSubQueues[renderRoot]);
+  }
+}
 
 IpcResourceUpdateQueue& IpcResourceUpdateQueue::operator=(
     IpcResourceUpdateQueue&& aOther) noexcept {
   MOZ_ASSERT(IsEmpty(), "Will forget existing updates!");
   mWriter = std::move(aOther.mWriter);
   mUpdates = std::move(aOther.mUpdates);
+  mRenderRoot = aOther.mRenderRoot;
+  for (auto renderRoot : wr::kNonDefaultRenderRoots) {
+    mSubQueues[renderRoot] = std::move(aOther.mSubQueues[renderRoot]);
+  }
   return *this;
 }
 
+void IpcResourceUpdateQueue::ReplaceResources(
+    IpcResourceUpdateQueue&& aOther) {
+  MOZ_ASSERT(IsEmpty(), "Will forget existing updates!");
+  MOZ_ASSERT(!aOther.HasAnySubQueue(), "Subqueues will be lost!");
+  MOZ_ASSERT(mRenderRoot == aOther.mRenderRoot);
+  mWriter = std::move(aOther.mWriter);
+  mUpdates = std::move(aOther.mUpdates);
+  mRenderRoot = aOther.mRenderRoot;
+}
+
 bool IpcResourceUpdateQueue::AddImage(ImageKey key,
                                       const ImageDescriptor& aDescriptor,
                                       Range<uint8_t> aBytes) {
   auto bytes = mWriter.Write(aBytes);
   if (!bytes.length()) {
     return false;
   }
   mUpdates.AppendElement(layers::OpAddImage(aDescriptor, bytes, 0, key));
@@ -403,16 +424,22 @@ bool IpcResourceUpdateQueue::IsEmpty() c
     return true;
   }
   return false;
 }
 
 void IpcResourceUpdateQueue::Clear() {
   mWriter.Clear();
   mUpdates.Clear();
+
+  for (auto& subQueue : mSubQueues) {
+    if (subQueue) {
+      subQueue->Clear();
+    }
+  }
 }
 
 // static
 void IpcResourceUpdateQueue::ReleaseShmems(
     ipc::IProtocol* aShmAllocator, nsTArray<layers::RefCountedShmem>& aShms) {
   for (auto& shm : aShms) {
     if (RefCountedShm::IsValid(shm) && RefCountedShm::Release(shm) == 0) {
       RefCountedShm::Dealloc(aShmAllocator, shm);
--- a/gfx/layers/wr/IpcResourceUpdateQueue.h
+++ b/gfx/layers/wr/IpcResourceUpdateQueue.h
@@ -11,16 +11,20 @@
 #include "mozilla/layers/RefCountedShmem.h"
 #include "mozilla/layers/TextureClient.h"
 #include "mozilla/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace ipc {
 class IShmemAllocator;
 }
+namespace layers {
+class TextureClient;
+}
+
 namespace wr {
 
 /// ShmSegmentsWriter pushes bytes in a sequence of fixed size shmems for small
 /// allocations and creates dedicated shmems for large allocations.
 class ShmSegmentsWriter {
  public:
   ShmSegmentsWriter(layers::WebRenderBridgeChild* aAllocator,
                     size_t aChunkSize);
@@ -42,16 +46,17 @@ class ShmSegmentsWriter {
 
   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;
@@ -77,26 +82,62 @@ class ShmSegmentsReader {
 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,
+      wr::RenderRoot aRenderRoot = wr::RenderRoot::Default,
+      size_t aChunkSize = 57328);
+
+  // 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(wr::RenderRoot aRenderRoot) {
+    MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default);
+    if (aRenderRoot == wr::RenderRoot::Default) {
+      MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default);
+      return *this;
+    }
+    if (!mSubQueues[aRenderRoot]) {
+      mSubQueues[aRenderRoot] = MakeUnique<IpcResourceUpdateQueue>(
+          mWriter.WrBridge(), aRenderRoot, mWriter.ChunkSize());
+    }
+    return *mSubQueues[aRenderRoot];
+  }
+
+  bool HasAnySubQueue() {
+    for (auto renderRoot : wr::kNonDefaultRenderRoots) {
+      if (mSubQueues[renderRoot]) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool HasSubQueue(wr::RenderRoot aRenderRoot) {
+    return aRenderRoot == wr::RenderRoot::Default || !!mSubQueues[aRenderRoot];
+  }
+
+  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;
 
+  // Moves over everything but the subqueues
+  void ReplaceResources(IpcResourceUpdateQueue&& aOther);
+
   bool AddImage(wr::ImageKey aKey, const ImageDescriptor& aDescriptor,
                 Range<uint8_t> aBytes);
 
   bool AddBlobImage(wr::BlobImageKey aKey, const ImageDescriptor& aDescriptor,
                     Range<uint8_t> aBytes);
 
   void AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey);
 
@@ -147,14 +188,16 @@ class IpcResourceUpdateQueue {
   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;
+  wr::NonDefaultRenderRootArray<UniquePtr<IpcResourceUpdateQueue>> mSubQueues;
+  wr::RenderRoot mRenderRoot;
 };
 
 }  // namespace wr
 }  // namespace mozilla
 
 #endif
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/RenderRootBoundary.h
@@ -0,0 +1,87 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_RENDERROOTBOUNDARY_H
+#define GFX_RENDERROOTBOUNDARY_H
+
+#include "mozilla/webrender/WebRenderTypes.h"
+
+namespace mozilla {
+namespace layers {
+
+// clang-format off
+// A display list can have multiple nsDisplayRenderRoot instances in it, one
+// at each point that the display list transitions from being in one render root
+// to another. In particular, this means that a display list can transition
+// from e.g. the "Default" render root to the "Content" render root multiple
+// times. When we process this display list in WebRenderCommandBuilder, we build
+// a WebRenderScrollData for each render root, and they are "connected" by the
+// referent render root property on the WebRenderLayerScrollData instances.
+// (This is similar to how the layers id referent property works, i.e. a WRLSD
+// item with a referent is an attachment point for a subtree from a different
+// render root.) But given that there are multiple transitions to the same
+// "child" render root, we need an additional data point to uniquely identify
+// each of the transitions, so that we can attach the correct scroll data
+// subtree at each transition. Failure to uniquely identify the transitions
+// means that the entire "child" render root's scroll data tree will get
+// attached at each transition. The RenderRootBoundary class serves this
+// purpose.
+//
+// Example time! Consider the following display list structure:
+//
+//           R        // root of the display list
+//          / \       //
+//         A   B      // items in Default render root that generate scroll data
+//        /   / \     //
+//       C   D   E    // nsDisplayRenderRoot items transitioning to content
+//      / \     /     //
+//     F   G   H      // items in Content render root that generate scroll data
+//
+// In this example, the Default render root WebRenderScrollData will contain
+// 6 WRLSD items, one for each of R, A, B, C, D, E. Of these, C, D, and E will
+// have mReferentRenderRoot properties set. The WebRenderScrollData for the
+// Content render root will end up looking like this:
+//
+//          Dummy root         //
+//          /        \         //
+//        C-Root    E-Root     //
+//       /    |       |        //
+//      F     G       H        //
+//
+// The RenderRootBoundary item on C will point to C-Root, and likewise for E.
+// The RenderRootBoundary item on D will be valid, but there will be no
+// corresponding subtree in the Content-side WebRenderScrollData. C-Root and
+// E-Root are created via WebRenderScrollDataCollection::AppendWrapper, and
+// have their mBoundaryRoot property set to the same RenderRootBoundary value
+// as the mReferentRenderRoot properties from C and E respectively.
+// clang-format on
+class RenderRootBoundary {
+ public:
+  explicit RenderRootBoundary(wr::RenderRoot aChildType)
+      : mChildType(aChildType) {
+    static uint64_t sCounter = 0;
+    mId = ++sCounter;
+  }
+
+  wr::RenderRoot GetChildType() const { return mChildType; }
+
+  bool operator==(const RenderRootBoundary& aOther) const {
+    return mChildType == aOther.mChildType && mId == aOther.mId;
+  }
+
+  friend struct IPC::ParamTraits<RenderRootBoundary>;
+  // constructor for IPC
+  RenderRootBoundary() = default;
+
+ private:
+  wr::RenderRoot mChildType;
+  // The id is what distinguishes different transition points within a display
+  // list (i.e. what would be different in C, D, and E in the example above).
+  uint64_t mId;
+};
+
+}  // namespace layers
+}  // namespace mozilla
+
+#endif  // GFX_RENDERROOTBOUNDARY_H
--- a/gfx/layers/wr/RenderRootStateManager.cpp
+++ b/gfx/layers/wr/RenderRootStateManager.cpp
@@ -6,22 +6,16 @@
 
 #include "mozilla/layers/RenderRootStateManager.h"
 
 #include "mozilla/layers/WebRenderBridgeChild.h"
 
 namespace mozilla {
 namespace layers {
 
-RenderRootStateManager::RenderRootStateManager(
-    WebRenderLayerManager* aLayerManager)
-    : mLayerManager(aLayerManager), mDestroyed(false) {}
-
-RenderRootStateManager::~RenderRootStateManager() {}
-
 // RenderRootStateManager shares its ref count with the WebRenderLayerManager
 // that created it. You can think of the two classes as being one unit, except
 // there are multiple RenderRootStateManagers per WebRenderLayerManager. Since
 // we need to reference the WebRenderLayerManager and it needs to reference us,
 // this avoids us needing to involve the cycle collector.
 void RenderRootStateManager::AddRef() { mLayerManager->AddRef(); }
 
 void RenderRootStateManager::Release() { mLayerManager->Release(); }
@@ -36,19 +30,20 @@ WebRenderCommandBuilder& RenderRootState
 
 RenderRootStateManager::WebRenderUserDataRefTable*
 RenderRootStateManager::GetWebRenderUserDataTable() {
   return mLayerManager->GetWebRenderUserDataTable();
 }
 
 wr::IpcResourceUpdateQueue& RenderRootStateManager::AsyncResourceUpdates() {
   MOZ_ASSERT(NS_IsMainThread());
+  MOZ_ASSERT(XRE_IsParentProcess() || mRenderRoot == wr::RenderRoot::Default);
 
   if (!mAsyncResourceUpdates) {
-    mAsyncResourceUpdates.emplace(WrBridge());
+    mAsyncResourceUpdates.emplace(WrBridge(), mRenderRoot);
 
     RefPtr<Runnable> task = NewRunnableMethod(
         "RenderRootStateManager::FlushAsyncResourceUpdates", this,
         &RenderRootStateManager::FlushAsyncResourceUpdates);
     NS_DispatchToMainThread(task.forget());
   }
 
   return mAsyncResourceUpdates.ref();
@@ -72,17 +67,17 @@ void RenderRootStateManager::Destroy() {
 void RenderRootStateManager::FlushAsyncResourceUpdates() {
   MOZ_ASSERT(NS_IsMainThread());
 
   if (!mAsyncResourceUpdates) {
     return;
   }
 
   if (!IsDestroyed() && WrBridge()) {
-    WrBridge()->UpdateResources(mAsyncResourceUpdates.ref());
+    WrBridge()->UpdateResources(mAsyncResourceUpdates.ref(), mRenderRoot);
   }
 
   mAsyncResourceUpdates.reset();
 }
 
 void RenderRootStateManager::AddImageKeyForDiscard(wr::ImageKey key) {
   mImageKeysToDelete.AppendElement(key);
 }
@@ -175,44 +170,47 @@ void RenderRootStateManager::WrReleasedI
     if (i != mAsyncAnimations.end()) {
       i->second->ReleasePreviousFrame(this, pair.id);
     }
   }
 }
 
 void RenderRootStateManager::AddWebRenderParentCommand(
     const WebRenderParentCommand& aCmd) {
-  WrBridge()->AddWebRenderParentCommand(aCmd);
+  WrBridge()->AddWebRenderParentCommand(aCmd, mRenderRoot);
 }
 void RenderRootStateManager::UpdateResources(
     wr::IpcResourceUpdateQueue& aResources) {
-  WrBridge()->UpdateResources(aResources);
+  WrBridge()->UpdateResources(aResources, mRenderRoot);
 }
 void RenderRootStateManager::AddPipelineIdForAsyncCompositable(
     const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle) {
-  WrBridge()->AddPipelineIdForAsyncCompositable(aPipelineId, aHandle);
+  WrBridge()->AddPipelineIdForAsyncCompositable(aPipelineId, aHandle,
+                                                mRenderRoot);
 }
 void RenderRootStateManager::AddPipelineIdForCompositable(
     const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle) {
-  WrBridge()->AddPipelineIdForCompositable(aPipelineId, aHandle);
+  WrBridge()->AddPipelineIdForCompositable(aPipelineId, aHandle, mRenderRoot);
 }
 void RenderRootStateManager::RemovePipelineIdForCompositable(
     const wr::PipelineId& aPipelineId) {
-  WrBridge()->RemovePipelineIdForCompositable(aPipelineId);
+  WrBridge()->RemovePipelineIdForCompositable(aPipelineId, mRenderRoot);
 }
 /// Release TextureClient that is bounded to ImageKey.
 /// It is used for recycling TextureClient.
 void RenderRootStateManager::ReleaseTextureOfImage(const wr::ImageKey& aKey) {
-  WrBridge()->ReleaseTextureOfImage(aKey);
+  WrBridge()->ReleaseTextureOfImage(aKey, mRenderRoot);
 }
 
 Maybe<wr::FontInstanceKey> RenderRootStateManager::GetFontKeyForScaledFont(
     gfx::ScaledFont* aScaledFont, wr::IpcResourceUpdateQueue* aResources) {
-  return WrBridge()->GetFontKeyForScaledFont(aScaledFont, aResources);
+  return WrBridge()->GetFontKeyForScaledFont(aScaledFont, mRenderRoot,
+                                             aResources);
 }
 
 Maybe<wr::FontKey> RenderRootStateManager::GetFontKeyForUnscaledFont(
     gfx::UnscaledFont* aUnscaledFont, wr::IpcResourceUpdateQueue* aResources) {
-  return WrBridge()->GetFontKeyForUnscaledFont(aUnscaledFont, aResources);
+  return WrBridge()->GetFontKeyForUnscaledFont(aUnscaledFont, mRenderRoot,
+                                               aResources);
 }
 
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/wr/RenderRootStateManager.h
+++ b/gfx/layers/wr/RenderRootStateManager.h
@@ -5,34 +5,39 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef GFX_RENDERROOTSTATEMANAGER_H
 #define GFX_RENDERROOTSTATEMANAGER_H
 
 #include "mozilla/webrender/WebRenderAPI.h"
 
 #include "mozilla/layers/IpcResourceUpdateQueue.h"
+#include "mozilla/layers/SharedSurfacesChild.h"
 #include "mozilla/layers/WebRenderCommandBuilder.h"
 
 namespace mozilla {
 
 namespace layers {
 
 class RenderRootStateManager {
   typedef nsTHashtable<nsRefPtrHashKey<WebRenderUserData>>
       WebRenderUserDataRefTable;
 
  public:
   void AddRef();
   void Release();
 
-  explicit RenderRootStateManager(WebRenderLayerManager* aLayerManager);
+  RenderRootStateManager()
+      : mLayerManager(nullptr),
+        mRenderRoot(wr::RenderRoot::Default),
+        mDestroyed(false) {}
 
   void Destroy();
   bool IsDestroyed() { return mDestroyed; }
+  wr::RenderRoot GetRenderRoot() { return mRenderRoot; }
   wr::IpcResourceUpdateQueue& AsyncResourceUpdates();
   WebRenderBridgeChild* WrBridge() const;
   WebRenderCommandBuilder& CommandBuilder();
   WebRenderUserDataRefTable* GetWebRenderUserDataTable();
   WebRenderLayerManager* LayerManager() { return mLayerManager; }
 
   void AddImageKeyForDiscard(wr::ImageKey key);
   void AddBlobImageKeyForDiscard(wr::BlobImageKey key);
@@ -69,31 +74,31 @@ class RenderRootStateManager {
       wr::IpcResourceUpdateQueue* aResources = nullptr);
   Maybe<wr::FontKey> GetFontKeyForUnscaledFont(
       gfx::UnscaledFont* aUnscaledFont,
       wr::IpcResourceUpdateQueue* aResources = nullptr);
 
   void FlushAsyncResourceUpdates();
 
  private:
-  ~RenderRootStateManager();
   WebRenderLayerManager* mLayerManager;
   Maybe<wr::IpcResourceUpdateQueue> mAsyncResourceUpdates;
   nsTArray<wr::ImageKey> mImageKeysToDelete;
   nsTArray<wr::BlobImageKey> mBlobImageKeysToDelete;
   std::unordered_map<uint64_t, RefPtr<SharedSurfacesAnimation>>
       mAsyncAnimations;
 
   // 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;
 
+  wr::RenderRoot mRenderRoot;
   bool mDestroyed;
 
   friend class WebRenderLayerManager;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/RenderRootTypes.cpp
@@ -0,0 +1,75 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RenderRootTypes.h"
+#include "mozilla/layers/WebRenderMessageUtils.h"
+#include "mozilla/layers/WebRenderBridgeChild.h"
+
+namespace mozilla {
+namespace ipc {
+
+void IPDLParamTraits<mozilla::layers::RenderRootDisplayListData>::Write(IPC::Message* aMsg,
+                                                                        IProtocol* aActor,
+                                                                        paramType&& aParam) {
+    WriteIPDLParam(aMsg, aActor, aParam.mRenderRoot);
+    WriteIPDLParam(aMsg, aActor, aParam.mRect);
+    WriteIPDLParam(aMsg, aActor, aParam.mCommands);
+    WriteIPDLParam(aMsg, aActor, aParam.mContentSize);
+    WriteIPDLParam(aMsg, aActor, std::move(aParam.mDL));
+    WriteIPDLParam(aMsg, aActor, aParam.mDLDesc);
+    WriteIPDLParam(aMsg, aActor, aParam.mResourceUpdates);
+    WriteIPDLParam(aMsg, aActor, aParam.mSmallShmems);
+    WriteIPDLParam(aMsg, aActor, std::move(aParam.mLargeShmems));
+    WriteIPDLParam(aMsg, aActor, aParam.mScrollData);
+}
+
+bool IPDLParamTraits<mozilla::layers::RenderRootDisplayListData>::Read(const IPC::Message* aMsg,
+                                                                       PickleIterator* aIter,
+                                                                       IProtocol* aActor,
+                                                                       paramType* aResult) {
+    if (ReadIPDLParam(aMsg, aIter, aActor, &aResult->mRenderRoot) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mRect) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mCommands) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mContentSize) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mDL) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mDLDesc) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mResourceUpdates) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSmallShmems) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mLargeShmems) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mScrollData)) {
+        return true;
+    }
+    return false;
+}
+
+void IPDLParamTraits<mozilla::layers::RenderRootUpdates>::Write(IPC::Message* aMsg,
+                                                                        IProtocol* aActor,
+                                                                        paramType&& aParam) {
+    WriteIPDLParam(aMsg, aActor, aParam.mRenderRoot);
+    WriteIPDLParam(aMsg, aActor, aParam.mCommands);
+    WriteIPDLParam(aMsg, aActor, aParam.mResourceUpdates);
+    WriteIPDLParam(aMsg, aActor, aParam.mSmallShmems);
+    WriteIPDLParam(aMsg, aActor, std::move(aParam.mLargeShmems));
+    WriteIPDLParam(aMsg, aActor, aParam.mScrollUpdates);
+}
+
+bool IPDLParamTraits<mozilla::layers::RenderRootUpdates>::Read(const IPC::Message* aMsg,
+                                                                       PickleIterator* aIter,
+                                                                       IProtocol* aActor,
+                                                                       paramType* aResult) {
+    if (ReadIPDLParam(aMsg, aIter, aActor, &aResult->mRenderRoot) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mCommands) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mResourceUpdates) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mSmallShmems) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mLargeShmems) &&
+        ReadIPDLParam(aMsg, aIter, aActor, &aResult->mScrollUpdates)) {
+        return true;
+    }
+    return false;
+}
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/RenderRootTypes.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef GFX_RENDERROOTTYPES_H
+#define GFX_RENDERROOTTYPES_H
+
+#include "mozilla/webrender/WebRenderAPI.h"
+#include "mozilla/webrender/WebRenderTypes.h"
+#include "mozilla/layers/WebRenderMessages.h"
+#include "mozilla/layers/WebRenderScrollData.h"
+
+namespace mozilla {
+
+namespace layers {
+
+struct RenderRootDisplayListData {
+  wr::RenderRoot mRenderRoot;
+  gfx::IntRect mRect;
+  nsTArray<WebRenderParentCommand> mCommands;
+  wr::LayoutSize mContentSize;
+  mozilla::ipc::ByteBuf mDL;
+  wr::BuiltDisplayListDescriptor mDLDesc;
+  nsTArray<OpUpdateResource> mResourceUpdates;
+  nsTArray<RefCountedShmem> mSmallShmems;
+  nsTArray<mozilla::ipc::Shmem> mLargeShmems;
+  WebRenderScrollData mScrollData;
+};
+
+struct RenderRootUpdates {
+  wr::RenderRoot mRenderRoot;
+  nsTArray<WebRenderParentCommand> mCommands;
+  nsTArray<OpUpdateResource> mResourceUpdates;
+  nsTArray<RefCountedShmem> mSmallShmems;
+  nsTArray<mozilla::ipc::Shmem> mLargeShmems;
+  ScrollUpdatesMap mScrollUpdates;
+};
+
+}  // namespace layers
+
+namespace ipc {
+
+template <>
+struct IPDLParamTraits<mozilla::layers::RenderRootDisplayListData> {
+  typedef mozilla::layers::RenderRootDisplayListData paramType;
+
+  static void Write(IPC::Message* aMsg, IProtocol* aActor, paramType&& aParam);
+
+  static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+                   IProtocol* aActor, paramType* aResult);
+};
+
+template <>
+struct IPDLParamTraits<mozilla::layers::RenderRootUpdates> {
+  typedef mozilla::layers::RenderRootUpdates paramType;
+
+  static void Write(IPC::Message* aMsg, IProtocol* aActor, paramType&& aParam);
+
+  static bool Read(const IPC::Message* aMsg, PickleIterator* aIter,
+                   IProtocol* aActor, paramType* aResult);
+};
+
+}  // namespace ipc
+}  // namespace mozilla
+
+
+#endif /* GFX_RENDERROOTTYPES_H */
--- a/gfx/layers/wr/StackingContextHelper.cpp
+++ b/gfx/layers/wr/StackingContextHelper.cpp
@@ -27,16 +27,17 @@ StackingContextHelper::StackingContextHe
     wr::DisplayListBuilder& aBuilder, const wr::StackingContextParams& aParams,
     const LayoutDeviceRect& aBounds)
     : mBuilder(&aBuilder),
       mScale(1.0f, 1.0f),
       mDeferredTransformItem(aParams.mDeferredTransformItem),
       mIsPreserve3D(aParams.transform_style == wr::TransformStyle::Preserve3D),
       mRasterizeLocally(aParams.mRasterizeLocally ||
                         aParentSC.mRasterizeLocally) {
+  mOrigin = aParentSC.mOrigin + aBounds.TopLeft();
   // Compute scale for fallback rendering. We don't try to guess a scale for 3d
   // transformed items
   gfx::Matrix transform2d;
   if (aParams.mBoundTransform &&
       aParams.mBoundTransform->CanDraw2D(&transform2d) &&
       aParams.reference_frame_kind != wr::WrReferenceFrameKind::Perspective &&
       !aParentSC.mIsPreserve3D) {
     mInheritedTransform = transform2d * aParentSC.mInheritedTransform;
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -43,30 +43,37 @@ class MOZ_RAII StackingContextHelper {
   StackingContextHelper();
 
   // Pops the stacking context, if one was pushed during the constructor.
   ~StackingContextHelper();
 
   // Export the inherited scale
   gfx::Size GetInheritedScale() const { return mScale; }
 
+  const gfx::Matrix& GetInheritedTransform() const {
+    return mInheritedTransform;
+  }
+
   const gfx::Matrix& GetSnappingSurfaceTransform() const {
     return mSnappingSurfaceTransform;
   }
 
   const Maybe<nsDisplayTransform*>& GetDeferredTransformItem() const;
   Maybe<gfx::Matrix4x4> GetDeferredTransformMatrix() const;
 
   bool AffectsClipPositioning() const { return mAffectsClipPositioning; }
   Maybe<wr::WrSpatialId> ReferenceFrameId() const { return mReferenceFrameId; }
 
+  const LayoutDevicePoint& GetOrigin() const { return mOrigin; }
+
  private:
   wr::DisplayListBuilder* mBuilder;
   gfx::Size mScale;
   gfx::Matrix mInheritedTransform;
+  LayoutDevicePoint mOrigin;
 
   // The "snapping surface" defines the space that we want to snap in.
   // You can think of it as the nearest physical surface.
   // Animated transforms create a new snapping surface, so that changes to their
   // transform don't affect the snapping of their contents. Non-animated
   // transforms do *not* create a new snapping surface, so that for example the
   // existence of a non-animated identity transform does not affect snapping.
   gfx::Matrix mSnappingSurfaceTransform;
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -26,18 +26,18 @@ WebRenderBridgeChild::WebRenderBridgeChi
     : mIsInTransaction(false),
       mIsInClearCachedResources(false),
       mIdNamespace{0},
       mResourceId(0),
       mPipelineId(aPipelineId),
       mManager(nullptr),
       mIPCOpen(false),
       mDestroyed(false),
-      mFontKeysDeleted(0),
-      mFontInstanceKeysDeleted(0) {}
+      mFontKeysDeleted(),
+      mFontInstanceKeysDeleted() {}
 
 WebRenderBridgeChild::~WebRenderBridgeChild() {
   MOZ_ASSERT(NS_IsMainThread());
   MOZ_ASSERT(mDestroyed);
 }
 
 void WebRenderBridgeChild::Destroy(bool aIsSync) {
   if (!IPCOpen()) {
@@ -66,147 +66,158 @@ void 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) {
-  mParentCommands.AppendElement(aCmd);
+    const WebRenderParentCommand& aCmd, wr::RenderRoot aRenderRoot) {
+  MOZ_ASSERT(aRenderRoot == wr::RenderRoot::Default ||
+             (XRE_IsParentProcess() && gfxPrefs::WebRenderSplitRenderRoots()));
+  mParentCommands[aRenderRoot].AppendElement(aCmd);
 }
 
 void WebRenderBridgeChild::BeginTransaction() {
   MOZ_ASSERT(!mDestroyed);
 
   UpdateFwdTransactionId();
   mIsInTransaction = true;
 }
 
 void WebRenderBridgeChild::UpdateResources(
-    wr::IpcResourceUpdateQueue& aResources) {
+    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,
-                            std::move(largeShmems));
+                            std::move(largeShmems), aRenderRoot);
 }
 
 void WebRenderBridgeChild::EndTransaction(
-    const wr::LayoutSize& aContentSize, wr::BuiltDisplayList& aDL,
-    wr::IpcResourceUpdateQueue& aResources, const gfx::IntSize& aSize,
-    TransactionId aTransactionId, const WebRenderScrollData& aScrollData,
-    bool aContainsSVGGroup, const mozilla::VsyncId& aVsyncId,
-    const mozilla::TimeStamp& aVsyncStartTime,
+    nsTArray<RenderRootDisplayListData>& aRenderRoots,
+    TransactionId aTransactionId, bool aContainsSVGGroup,
+    const mozilla::VsyncId& aVsyncId, const mozilla::TimeStamp& aVsyncStartTime,
     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;
-
   TimeStamp fwdTime = TimeStamp::Now();
 
-  nsTArray<OpUpdateResource> resourceUpdates;
-  nsTArray<RefCountedShmem> smallShmems;
-  nsTArray<ipc::Shmem> largeShmems;
-  aResources.Flush(resourceUpdates, smallShmems, largeShmems);
+  for (auto& renderRoot : aRenderRoots) {
+    MOZ_ASSERT(
+        renderRoot.mRenderRoot == wr::RenderRoot::Default ||
+        (XRE_IsParentProcess() && gfxPrefs::WebRenderSplitRenderRoots()));
+    renderRoot.mCommands = std::move(mParentCommands[renderRoot.mRenderRoot]);
+  }
 
-  this->SendSetDisplayList(
-      aSize, mParentCommands, mDestroyedActors, GetFwdTransactionId(),
-      aTransactionId, aContentSize, std::move(dlData), aDL.dl_desc, aScrollData,
-      resourceUpdates, smallShmems, std::move(largeShmems), mIdNamespace,
-      aContainsSVGGroup, aVsyncId, aVsyncStartTime, aRefreshStartTime,
-      aTxnStartTime, aTxnURL, fwdTime);
+  this->SendSetDisplayList(std::move(aRenderRoots), mDestroyedActors,
+                           GetFwdTransactionId(), aTransactionId, mIdNamespace,
+                           aContainsSVGGroup, aVsyncId, aVsyncStartTime,
+                           aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime);
 
-  mParentCommands.Clear();
+  // With multiple render roots, we may not have sent all of our
+  // mParentCommands, so go ahead and go through our mParentCommands and ensure
+  // they get sent.
+  ProcessWebRenderParentCommands();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 void WebRenderBridgeChild::EndEmptyTransaction(
-    const FocusTarget& aFocusTarget, const ScrollUpdatesMap& aUpdates,
-    Maybe<wr::IpcResourceUpdateQueue>& aResources,
+    const FocusTarget& aFocusTarget,
+    nsTArray<RenderRootUpdates>& aRenderRootUpdates,
     uint32_t aPaintSequenceNumber, TransactionId aTransactionId,
     const mozilla::VsyncId& aVsyncId, const mozilla::TimeStamp& aVsyncStartTime,
     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();
+  for (auto& update : aRenderRootUpdates) {
+    MOZ_ASSERT(
+        update.mRenderRoot == wr::RenderRoot::Default ||
+        (XRE_IsParentProcess() && gfxPrefs::WebRenderSplitRenderRoots()));
+    update.mCommands = std::move(mParentCommands[update.mRenderRoot]);
   }
 
   this->SendEmptyTransaction(
-      aFocusTarget, aUpdates, aPaintSequenceNumber, mParentCommands,
-      mDestroyedActors, GetFwdTransactionId(), aTransactionId, resourceUpdates,
-      smallShmems, std::move(largeShmems), mIdNamespace, aVsyncId,
-      aVsyncStartTime, aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime);
-  mParentCommands.Clear();
+      aFocusTarget, aPaintSequenceNumber, std::move(aRenderRootUpdates),
+      mDestroyedActors, GetFwdTransactionId(), aTransactionId, mIdNamespace,
+      aVsyncId, aVsyncStartTime, aRefreshStartTime, aTxnStartTime, aTxnURL,
+      fwdTime);
+
+  // With multiple render roots, we may not have sent all of our
+  // mParentCommands, so go ahead and go through our mParentCommands and ensure
+  // they get sent.
+  ProcessWebRenderParentCommands();
   mDestroyedActors.Clear();
   mIsInTransaction = false;
 }
 
 void WebRenderBridgeChild::ProcessWebRenderParentCommands() {
   MOZ_ASSERT(!mDestroyed);
 
-  if (mParentCommands.IsEmpty()) {
-    return;
+  for (auto renderRoot : wr::kRenderRoots) {
+    if (!mParentCommands[renderRoot].IsEmpty()) {
+      MOZ_ASSERT(renderRoot == wr::RenderRoot::Default ||
+                 gfxPrefs::WebRenderSplitRenderRoots());
+      this->SendParentCommands(mParentCommands[renderRoot], renderRoot);
+      mParentCommands[renderRoot].Clear();
+    }
   }
-  this->SendParentCommands(mParentCommands);
-  mParentCommands.Clear();
 }
 
 void WebRenderBridgeChild::AddPipelineIdForAsyncCompositable(
-    const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle) {
+    const wr::PipelineId& aPipelineId, 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) {
-  AddWebRenderParentCommand(OpAddPipelineIdForCompositable(
-      aPipelineId, aHandle, /* isAsync */ false));
+    const wr::PipelineId& aPipelineId, const CompositableHandle& aHandle,
+    wr::RenderRoot aRenderRoot) {
+  AddWebRenderParentCommand(
+      OpAddPipelineIdForCompositable(aPipelineId, aHandle, /* isAsync */ false),
+      aRenderRoot);
 }
 
 void WebRenderBridgeChild::RemovePipelineIdForCompositable(
-    const wr::PipelineId& aPipelineId) {
-  AddWebRenderParentCommand(OpRemovePipelineIdForCompositable(aPipelineId));
+    const wr::PipelineId& aPipelineId, wr::RenderRoot aRenderRoot) {
+  AddWebRenderParentCommand(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) {
-  AddWebRenderParentCommand(OpReleaseTextureOfImage(aKey));
+void WebRenderBridgeChild::ReleaseTextureOfImage(const wr::ImageKey& aKey,
+                                                 wr::RenderRoot aRenderRoot) {
+  AddWebRenderParentCommand(OpReleaseTextureOfImage(aKey), aRenderRoot);
 }
 
 struct FontFileDataSink {
   wr::FontKey* mFontKey;
   WebRenderBridgeChild* mWrBridge;
   wr::IpcResourceUpdateQueue* mResources;
 };
 
@@ -235,42 +246,45 @@ static void WriteFontDescriptor(const ui
 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);
 
-  Maybe<wr::WrFontInstanceKey> key = GetFontKeyForScaledFont(aFont);
+  Maybe<wr::WrFontInstanceKey> key =
+      GetFontKeyForScaledFont(aFont, aBuilder.GetRenderRoot());
   MOZ_ASSERT(key.isSome());
 
   if (key.isSome()) {
     aBuilder.PushText(aBounds, aClip, aBackfaceVisible, aColor, key.value(),
                       aGlyphs, aGlyphOptions);
   }
 }
 
 Maybe<wr::FontInstanceKey> WebRenderBridgeChild::GetFontKeyForScaledFont(
-    gfx::ScaledFont* aScaledFont, wr::IpcResourceUpdateQueue* aResources) {
+    gfx::ScaledFont* aScaledFont, wr::RenderRoot aRenderRoot,
+    wr::IpcResourceUpdateQueue* aResources) {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aScaledFont);
   MOZ_ASSERT(aScaledFont->CanSerialize());
 
+  auto& fontInstanceKeys = mFontInstanceKeys[aRenderRoot];
   wr::FontInstanceKey instanceKey = {wr::IdNamespace{0}, 0};
-  if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) {
+  if (fontInstanceKeys.Get(aScaledFont, &instanceKey)) {
     return Some(instanceKey);
   }
 
   Maybe<wr::IpcResourceUpdateQueue> resources =
       aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this));
   aResources = resources.ptrOr(aResources);
 
-  Maybe<wr::FontKey> fontKey =
-      GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont(), aResources);
+  Maybe<wr::FontKey> fontKey = GetFontKeyForUnscaledFont(
+      aScaledFont->GetUnscaledFont(), aRenderRoot, aResources);
   if (fontKey.isNothing()) {
     return Nothing();
   }
 
   instanceKey = GetNextFontInstanceKey();
 
   Maybe<wr::FontInstanceOptions> options;
   Maybe<wr::FontInstancePlatformOptions> platformOptions;
@@ -278,70 +292,78 @@ Maybe<wr::FontInstanceKey> WebRenderBrid
   aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions,
                                         &variations);
 
   aResources->AddFontInstance(
       instanceKey, fontKey.value(), 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 Some(instanceKey);
 }
 
 Maybe<wr::FontKey> WebRenderBridgeChild::GetFontKeyForUnscaledFont(
-    gfx::UnscaledFont* aUnscaled, wr::IpcResourceUpdateQueue* aResources) {
+    gfx::UnscaledFont* aUnscaled, wr::RenderRoot aRenderRoot,
+    wr::IpcResourceUpdateQueue* aResources) {
   MOZ_ASSERT(!mDestroyed);
 
+  auto& fontKeys = mFontKeys[aRenderRoot];
   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 Nothing();
     }
 
     if (resources.isSome()) {
-      UpdateResources(resources.ref());
+      UpdateResources(resources.ref(), aRenderRoot);
     }
 
-    mFontKeys.Put(aUnscaled, fontKey);
+    fontKeys.Put(aUnscaled, fontKey);
   }
 
   return Some(fontKey);
 }
 
 void WebRenderBridgeChild::RemoveExpiredFontKeys(
     wr::IpcResourceUpdateQueue& aResourceUpdates) {
+  auto& fontInstanceKeys = mFontInstanceKeys[aResourceUpdates.GetRenderRoot()];
+  auto& fontKeys = mFontKeys[aResourceUpdates.GetRenderRoot()];
+  auto& fontInstanceKeysDeleted =
+      mFontInstanceKeysDeleted[aResourceUpdates.GetRenderRoot()];
+  auto& fontKeysDeleted = mFontKeysDeleted[aResourceUpdates.GetRenderRoot()];
+
   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();
       }
     }
   }
 }
 
@@ -415,35 +437,38 @@ bool WebRenderBridgeChild::DestroyInTran
 }
 
 bool WebRenderBridgeChild::DestroyInTransaction(
     const CompositableHandle& aHandle) {
   return AddOpDestroy(OpDestroy(aHandle));
 }
 
 void WebRenderBridgeChild::RemoveTextureFromCompositable(
-    CompositableClient* aCompositable, TextureClient* aTexture) {
+    CompositableClient* aCompositable, TextureClient* aTexture,
+    const Maybe<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())));
+  AddWebRenderParentCommand(
+      CompositableOperation(aCompositable->GetIPCHandle(),
+                            OpRemoveTexture(nullptr, aTexture->GetIPDLActor())),
+      *aRenderRoot);
 }
 
 void WebRenderBridgeChild::UseTextures(
     CompositableClient* aCompositable,
-    const nsTArray<TimedTextureClient>& aTextures) {
+    const nsTArray<TimedTextureClient>& aTextures,
+    const Maybe<wr::RenderRoot>& aRenderRoot) {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
   AutoTArray<TimedTexture, 4> textures;
 
@@ -456,17 +481,18 @@ void WebRenderBridgeChild::UseTextures(
 
     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) {}
 
 void WebRenderBridgeChild::UpdateFwdTransactionId() {
   GetCompositorBridgeChild()->UpdateFwdTransactionId();
@@ -486,18 +512,20 @@ mozilla::ipc::IPCResult WebRenderBridgeC
   }
   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();
+  for (auto renderRoot : wr::kRenderRoots) {
+    mFontInstanceKeys[renderRoot].Clear();
+    mFontKeys[renderRoot].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,33 +59,31 @@ class WebRenderBridgeChild final : publi
                                    public CompositableForwarder {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderBridgeChild, override)
 
   friend class PWebRenderBridgeChild;
 
  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, TransactionId aTransactionId,
-                      const WebRenderScrollData& aScrollData,
-                      bool aContainsSVGroup, const mozilla::VsyncId& aVsyncId,
+  void EndTransaction(nsTArray<RenderRootDisplayListData>& aRenderRoots,
+                      TransactionId aTransactionId, bool aContainsSVGroup,
+                      const mozilla::VsyncId& aVsyncId,
                       const mozilla::TimeStamp& aVsyncStartTime,
                       const mozilla::TimeStamp& aRefreshStartTime,
                       const mozilla::TimeStamp& aTxnStartTime,
                       const nsCString& aTxtURL);
   void EndEmptyTransaction(const FocusTarget& aFocusTarget,
-                           const ScrollUpdatesMap& aUpdates,
-                           Maybe<wr::IpcResourceUpdateQueue>& aResources,
+                           nsTArray<RenderRootUpdates>& aRenderRootUpdates,
                            uint32_t aPaintSequenceNumber,
                            TransactionId aTransactionId,
                            const mozilla::VsyncId& aVsyncId,
                            const mozilla::TimeStamp& aVsyncStartTime,
                            const mozilla::TimeStamp& aRefreshStartTime,
                            const mozilla::TimeStamp& aTxnStartTime,
                            const nsCString& aTxtURL);
   void ProcessWebRenderParentCommands();
@@ -98,24 +96,28 @@ class WebRenderBridgeChild final : publi
   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; }
@@ -142,20 +144,20 @@ class WebRenderBridgeChild final : publi
                   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);
 
   Maybe<wr::FontInstanceKey> GetFontKeyForScaledFont(
-      gfx::ScaledFont* aScaledFont,
+      gfx::ScaledFont* aScaledFont, wr::RenderRoot aRenderRoot,
       wr::IpcResourceUpdateQueue* aResources = nullptr);
   Maybe<wr::FontKey> GetFontKeyForUnscaledFont(
-      gfx::UnscaledFont* aUnscaledFont,
+      gfx::UnscaledFont* aUnscaledFont, wr::RenderRoot aRenderRoot,
       wr::IpcResourceUpdateQueue* aResources = nullptr);
   void RemoveExpiredFontKeys(wr::IpcResourceUpdateQueue& aResources);
 
   void BeginClearCachedResources();
   void EndClearCachedResources();
 
   void SetWebRenderLayerManager(WebRenderLayerManager* aManager);
 
@@ -192,20 +194,22 @@ class WebRenderBridgeChild final : publi
       CompositableClient* aCompositable,
       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;
+  void RemoveTextureFromCompositable(
+      CompositableClient* aCompositable, TextureClient* aTexture,
+      const Maybe<wr::RenderRoot>& aRenderRoot) override;
   void UseTextures(CompositableClient* aCompositable,
-                   const nsTArray<TimedTextureClient>& aTextures) override;
+                   const nsTArray<TimedTextureClient>& aTextures,
+                   const Maybe<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;
@@ -226,34 +230,36 @@ class WebRenderBridgeChild final : publi
   void ReleaseIPDLReference() {
     MOZ_ASSERT(mIPCOpen == true);
     mIPCOpen = false;
     Release();
   }
 
   bool AddOpDestroy(const OpDestroy& aOp);
 
-  nsTArray<WebRenderParentCommand> mParentCommands;
   nsTArray<OpDestroy> mDestroyedActors;
+  wr::RenderRootArray<nsTArray<WebRenderParentCommand>> mParentCommands;
   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;
-  nsDataHashtable<UnscaledFontHashKey, wr::FontKey> mFontKeys;
+  wr::RenderRootArray<uint32_t> mFontKeysDeleted;
+  wr::RenderRootArray<nsDataHashtable<UnscaledFontHashKey, wr::FontKey>>
+      mFontKeys;
 
-  uint32_t mFontInstanceKeysDeleted;
-  nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey> mFontInstanceKeys;
+  wr::RenderRootArray<uint32_t> mFontInstanceKeysDeleted;
+  wr::RenderRootArray<nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey>>
+      mFontInstanceKeys;
 
   UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
 
   RefCountedShmem mResourceShm;
 };
 
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -291,52 +291,61 @@ class MOZ_STACK_CLASS AutoWebRenderBridg
  private:
   WebRenderBridgeParent* mWebRenderBridgeParent;
   InfallibleTArray<OpDestroy>* mActorsToDestroy;
 };
 
 WebRenderBridgeParent::WebRenderBridgeParent(
     CompositorBridgeParentBase* aCompositorBridge,
     const wr::PipelineId& aPipelineId, widget::CompositorWidget* aWidget,
-    CompositorVsyncScheduler* aScheduler, RefPtr<wr::WebRenderAPI>&& aApi,
+    CompositorVsyncScheduler* aScheduler,
+    nsTArray<RefPtr<wr::WebRenderAPI>>&& aApis,
     RefPtr<AsyncImagePipelineManager>&& aImageMgr,
     RefPtr<CompositorAnimationStorage>&& aAnimStorage, TimeDuration aVsyncRate)
     : mCompositorBridge(aCompositorBridge),
       mPipelineId(aPipelineId),
       mWidget(aWidget),
-      mApi(aApi),
+      mApis(aApis),
       mAsyncImageManager(aImageMgr),
       mCompositorScheduler(aScheduler),
       mAnimStorage(aAnimStorage),
       mVsyncRate(aVsyncRate),
       mChildLayersObserverEpoch{0},
       mParentLayersObserverEpoch{0},
       mWrEpoch{0},
-      mIdNamespace(aApi->GetNamespace()),
+      mIdNamespace(aApis[0]->GetNamespace()),
+      mRenderRootRectMutex("WebRenderBridgeParent::mRenderRootRectMutex"),
+      mRenderRoot(wr::RenderRoot::Default),
       mPaused(false),
       mDestroyed(false),
       mReceivedDisplayList(false),
       mIsFirstPaint(true),
       mSkippedComposite(false) {
   MOZ_ASSERT(mAsyncImageManager);
   MOZ_ASSERT(mAnimStorage);
   mAsyncImageManager->AddPipeline(mPipelineId, this);
   if (IsRootWebRenderBridgeParent()) {
     MOZ_ASSERT(!mCompositorScheduler);
     mCompositorScheduler = new CompositorVsyncScheduler(this, mWidget);
   }
+
+  if (!IsRootWebRenderBridgeParent() && gfxPrefs::WebRenderSplitRenderRoots()) {
+    mRenderRoot = wr::RenderRoot::Content;
+  }
 }
 
 WebRenderBridgeParent::WebRenderBridgeParent(const wr::PipelineId& aPipelineId)
     : mCompositorBridge(nullptr),
       mPipelineId(aPipelineId),
       mChildLayersObserverEpoch{0},
       mParentLayersObserverEpoch{0},
       mWrEpoch{0},
       mIdNamespace{0},
+      mRenderRootRectMutex("WebRenderBridgeParent::mRenderRootRectMutex"),
+      mRenderRoot(wr::RenderRoot::Default),
       mPaused(false),
       mDestroyed(true),
       mReceivedDisplayList(false),
       mIsFirstPaint(false),
       mSkippedComposite(false) {}
 
 /* static */
 WebRenderBridgeParent* WebRenderBridgeParent::CreateDestroyed(
@@ -721,36 +730,38 @@ void WebRenderBridgeParent::ObserveShare
   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();
   }
 
+  MOZ_RELEASE_ASSERT((size_t)aRenderRoot < mApis.Length());
+
   wr::TransactionBuilder txn;
   txn.SetLowPriority(!IsRootWebRenderBridgeParent());
 
   bool success =
       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);
+  Api(aRenderRoot)->SendTransaction(txn);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvDeleteCompositorAnimations(
     InfallibleTArray<uint64_t>&& aIds) {
   if (mDestroyed) {
     return IPC_OK();
@@ -823,43 +834,50 @@ WebRenderBridgeParent::GetRootWebRenderB
 
 void WebRenderBridgeParent::UpdateAPZFocusState(const FocusTarget& aFocus) {
   CompositorBridgeParent* cbp = GetRootCompositorBridgeParent();
   if (!cbp) {
     return;
   }
   LayersId rootLayersId = cbp->RootLayerTreeId();
   if (RefPtr<APZUpdater> apz = cbp->GetAPZUpdater()) {
-    apz->UpdateFocusState(rootLayersId, GetLayersId(), aFocus);
+    apz->UpdateFocusState(rootLayersId, WRRootId(GetLayersId(), mRenderRoot),
+                          aFocus);
   }
 }
 
 void WebRenderBridgeParent::UpdateAPZScrollData(const wr::Epoch& aEpoch,
-                                                WebRenderScrollData&& aData) {
+                                                WebRenderScrollData&& aData,
+                                                wr::RenderRoot aRenderRoot) {
   CompositorBridgeParent* cbp = GetRootCompositorBridgeParent();
   if (!cbp) {
     return;
   }
   LayersId rootLayersId = cbp->RootLayerTreeId();
   if (RefPtr<APZUpdater> apz = cbp->GetAPZUpdater()) {
-    apz->UpdateScrollDataAndTreeState(rootLayersId, GetLayersId(), aEpoch,
-                                      std::move(aData));
+    apz->UpdateScrollDataAndTreeState(
+        WRRootId(rootLayersId, wr::RenderRoot::Default),
+        WRRootId(GetLayersId(), RenderRootForExternal(aRenderRoot)), aEpoch,
+        std::move(aData));
   }
 }
 
 void WebRenderBridgeParent::UpdateAPZScrollOffsets(
-    ScrollUpdatesMap&& aUpdates, uint32_t aPaintSequenceNumber) {
+    ScrollUpdatesMap&& aUpdates, uint32_t aPaintSequenceNumber,
+    wr::RenderRoot aRenderRoot) {
   CompositorBridgeParent* cbp = GetRootCompositorBridgeParent();
   if (!cbp) {
     return;
   }
   LayersId rootLayersId = cbp->RootLayerTreeId();
   if (RefPtr<APZUpdater> apz = cbp->GetAPZUpdater()) {
-    apz->UpdateScrollOffsets(rootLayersId, GetLayersId(), std::move(aUpdates),
-                             aPaintSequenceNumber);
+    apz->UpdateScrollOffsets(
+        WRRootId(rootLayersId, wr::RenderRoot::Default),
+        WRRootId(GetLayersId(), RenderRootForExternal(aRenderRoot)),
+        std::move(aUpdates), aPaintSequenceNumber);
   }
 }
 
 void WebRenderBridgeParent::SetAPZSampleTime() {
   CompositorBridgeParent* cbp = GetRootCompositorBridgeParent();
   if (!cbp) {
     return;
   }
@@ -871,120 +889,171 @@ void WebRenderBridgeParent::SetAPZSample
     // 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 = Api(aRenderRoot);
+  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::Default) {
+        MutexAutoLock lock(mRenderRootRectMutex);
+        mRenderRootRects[aRenderRoot] = 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.SetDocumentView(rect, widgetSize);
+    }
+    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));
+    }
+
+    if (!IsRootWebRenderBridgeParent()) {
+      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,
+    nsTArray<RenderRootDisplayListData>&& aDisplayLists,
     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 TransactionId& aTransactionId, const wr::IdNamespace& aIdNamespace,
     const bool& aContainsSVGGroup, const VsyncId& aVsyncId,
     const TimeStamp& aVsyncStartTime, const TimeStamp& aRefreshStartTime,
     const TimeStamp& aTxnStartTime, const nsCString& aTxnURL,
     const TimeStamp& aFwdTime) {
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
     }
     return IPC_OK();
   }
 
+  // Guard against malicious content processes
+  MOZ_RELEASE_ASSERT(aDisplayLists.Length() > 0);
+  for (auto& displayList : aDisplayLists) {
+    // mApis.Length() should be the lowest possible length of things that we
+    // will be indexing via a RenderRoot, so it should be sufficient to check
+    // just that.
+    MOZ_RELEASE_ASSERT((size_t)displayList.mRenderRoot < mApis.Length());
+  }
+
   if (!IsRootWebRenderBridgeParent()) {
     CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, aTxnURL);
   }
 
   AUTO_PROFILER_TRACING("Paint", "SetDisplayList", GRAPHICS);
   UpdateFwdTransactionId(aFwdTransactionId);
 
   // 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);
-  }
+  mReceivedDisplayList = true;
+  bool observeLayersUpdate = ShouldParentObserveEpoch();
 
-  if (!ProcessWebRenderParentCommands(aCommands, txn)) {
-    return IPC_FAIL(this, "Invalid parent command found");
+  // The IsFirstPaint() flag should be the same for all the scrolldata across
+  // all the renderroot display lists in a given transaction. We assert this
+  // below. So we can read the flag from any one of them.
+  if (aDisplayLists[0].mScrollData.IsFirstPaint()) {
+    mIsFirstPaint = true;
   }
-
-  if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
-    return IPC_FAIL(this, "Failed to deserialize resource updates");
-  }
-
-  mReceivedDisplayList = true;
-
-  if (aScrollData.IsFirstPaint()) {
-    mIsFirstPaint = true;
+  for (size_t i = 1; i < aDisplayLists.Length(); i++) {
+    // Ensure the flag is the same on all of them.
+    MOZ_RELEASE_ASSERT(aDisplayLists[i].mScrollData.IsFirstPaint() ==
+                       aDisplayLists[0].mScrollData.IsFirstPaint());
   }
 
   // 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();
+  for (auto& displayList : aDisplayLists) {
+    UpdateAPZScrollData(wrEpoch, std::move(displayList.mScrollData),
+                        displayList.mRenderRoot);
+  }
 
-  if (validTransaction) {
-    if (IsRootWebRenderBridgeParent()) {
-      LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
-      LayoutDeviceIntRect docRect(LayoutDeviceIntPoint(), widgetSize);
-      txn.SetDocumentView(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);
+  bool validTransaction = aIdNamespace == mIdNamespace;
 
-    if (observeLayersUpdate) {
-      txn.Notify(wr::Checkpoint::SceneBuilt,
-                 MakeUnique<ScheduleObserveLayersUpdate>(
-                     mCompositorBridge, GetLayersId(),
-                     mChildLayersObserverEpoch, true));
+  wr::RenderRootArray<wr::TransactionBuilder> txns;
+  wr::RenderRootArray<Maybe<wr::AutoTransactionSender>> senders;
+  for (auto& displayList : aDisplayLists) {
+    MOZ_ASSERT(displayList.mRenderRoot == wr::RenderRoot::Default ||
+               IsRootWebRenderBridgeParent());
+    if (!SetDisplayList(
+            displayList.mRenderRoot, displayList.mRect, displayList.mCommands,
+            displayList.mContentSize, std::move(displayList.mDL),
+            displayList.mDLDesc, displayList.mResourceUpdates,
+            displayList.mSmallShmems, displayList.mLargeShmems, aTxnStartTime,
+            txns[displayList.mRenderRoot], senders[displayList.mRenderRoot],
+            wrEpoch, validTransaction, observeLayersUpdate)) {
+      return IPC_FAIL(this, "Failed call to SetDisplayList");
     }
+  }
 
-    if (!IsRootWebRenderBridgeParent()) {
-      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, aVsyncId,
                            aVsyncStartTime, aRefreshStartTime, aTxnStartTime,
                            aTxnURL, aFwdTime, mIsFirstPaint);
   mIsFirstPaint = false;
@@ -994,216 +1063,257 @@ mozilla::ipc::IPCResult WebRenderBridgeP
     // though DisplayList was not pushed to webrender.
     if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
       TimeStamp now = TimeStamp::Now();
       cbp->NotifyPipelineRendered(mPipelineId, wrEpoch, VsyncId(), now, now,
                                   now);
     }
   }
 
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
+  for (auto& displayList : aDisplayLists) {
+    wr::IpcResourceUpdateQueue::ReleaseShmems(this, displayList.mSmallShmems);
+    wr::IpcResourceUpdateQueue::ReleaseShmems(this, displayList.mLargeShmems);
+  }
+
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvEmptyTransaction(
-    const FocusTarget& aFocusTarget, const ScrollUpdatesMap& aUpdates,
-    const uint32_t& aPaintSequenceNumber,
-    InfallibleTArray<WebRenderParentCommand>&& aCommands,
+    const FocusTarget& aFocusTarget, const uint32_t& aPaintSequenceNumber,
+    nsTArray<RenderRootUpdates>&& aRenderRootUpdates,
     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 TransactionId& aTransactionId, const wr::IdNamespace& aIdNamespace,
     const VsyncId& aVsyncId, const TimeStamp& aVsyncStartTime,
     const TimeStamp& aRefreshStartTime, const TimeStamp& aTxnStartTime,
     const nsCString& aTxnURL, const TimeStamp& aFwdTime) {
   if (mDestroyed) {
     for (const auto& op : aToDestroy) {
       DestroyActor(op);
     }
     return IPC_OK();
   }
 
+  // Guard against malicious content processes
+  for (auto& update : aRenderRootUpdates) {
+    // mApis.Length() should be the lowest possible length of things that we
+    // will be indexing via a RenderRoot, so it should be sufficient to check
+    // just that.
+    MOZ_RELEASE_ASSERT((size_t)update.mRenderRoot < mApis.Length());
+  }
+
   if (!IsRootWebRenderBridgeParent()) {
     CrashReporter::AnnotateCrashReport(CrashReporter::Annotation::URL, aTxnURL);
   }
 
   AUTO_PROFILER_TRACING("Paint", "EmptyTransaction", GRAPHICS);
   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;
+  wr::RenderRootArray<bool> scheduleComposite(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);
+
+  wr::RenderRootArray<Maybe<wr::TransactionBuilder>> txns;
+  for (auto& update : aRenderRootUpdates) {
+    MOZ_ASSERT(update.mRenderRoot == wr::RenderRoot::Default ||
+               IsRootWebRenderBridgeParent());
+
+    if (!update.mScrollUpdates.empty()) {
+      UpdateAPZScrollOffsets(std::move(update.mScrollUpdates),
+                             aPaintSequenceNumber, update.mRenderRoot);
+    }
+
+    txns[update.mRenderRoot].emplace();
+    txns[update.mRenderRoot]->SetLowPriority(!IsRootWebRenderBridgeParent());
   }
 
-  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)) {
-    return IPC_FAIL(this, "Failed to deserialize resource updates");
-  }
-
-  if (!aCommands.IsEmpty()) {
-    mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
-    if (!ProcessWebRenderParentCommands(aCommands, txn)) {
-      return IPC_FAIL(this, "Invalid parent command found");
+  for (auto& update : aRenderRootUpdates) {
+    if (!UpdateResources(update.mResourceUpdates, update.mSmallShmems,
+                         update.mLargeShmems, *txns[update.mRenderRoot])) {
+      return IPC_FAIL(this, "Failed to deserialize resource updates");
     }
   }
 
-  if (ShouldParentObserveEpoch()) {
-    txn.Notify(
-        wr::Checkpoint::SceneBuilt,
-        MakeUnique<ScheduleObserveLayersUpdate>(
-            mCompositorBridge, GetLayersId(), mChildLayersObserverEpoch, true));
+  bool compositionTimeSet = false;
+  for (auto& update : aRenderRootUpdates) {
+    if (!update.mCommands.IsEmpty()) {
+      if (!compositionTimeSet) {
+        mAsyncImageManager->SetCompositionTime(TimeStamp::Now());
+        compositionTimeSet = true;
+      }
+      if (!ProcessWebRenderParentCommands(update.mCommands,
+                                          *txns[update.mRenderRoot],
+                                          update.mRenderRoot)) {
+        return IPC_FAIL(this, "Invalid parent command found");
+      }
+    }
+
+    if (ShouldParentObserveEpoch()) {
+      txns[update.mRenderRoot]->Notify(wr::Checkpoint::SceneBuilt,
+                                       MakeUnique<ScheduleObserveLayersUpdate>(
+                                           mCompositorBridge, GetLayersId(),
+                                           mChildLayersObserverEpoch, true));
+    }
   }
 
-  // Even when txn.IsResourceUpdatesEmpty() is true, there could be resource
-  // updates. It is handled by WebRenderTextureHostWrapper. In this case
-  // txn.IsRenderedFrameInvalidated() becomes true.
-  if (txn.IsResourceUpdatesEmpty() && !txn.IsRenderedFrameInvalidated()) {
+  bool rollbackEpoch = true;
+  for (auto& update : aRenderRootUpdates) {
+    auto& txn = *txns[update.mRenderRoot];
+
+    // Even when txn.IsResourceUpdatesEmpty() is true, there could be resource
+    // updates. It is handled by WebRenderTextureHostWrapper. In this case
+    // txn.IsRenderedFrameInvalidated() becomes true.
+    if (!txn.IsResourceUpdatesEmpty() || txn.IsRenderedFrameInvalidated()) {
+      // There are resource updates, then we update Epoch of transaction.
+      txn.UpdateEpoch(mPipelineId, mWrEpoch);
+      scheduleComposite[update.mRenderRoot] = true;
+      rollbackEpoch = false;
+    }
+  }
+  if (rollbackEpoch) {
     // 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 {
-    // There are resource updates, then we update Epoch of transaction.
-    txn.UpdateEpoch(mPipelineId, mWrEpoch);
-    scheduleComposite = true;
-  }
-
-  if (!txn.IsEmpty()) {
-    mApi->SendTransaction(txn);
   }
 
-  bool sendDidComposite = true;
-  if (scheduleComposite || !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;
+  bool scheduleAnyComposite = false;
+
+  for (auto renderRoot : wr::kRenderRoots) {
+    if (txns[renderRoot] && !txns[renderRoot]->IsEmpty()) {
+      Api(renderRoot)->SendTransaction(*txns[renderRoot]);
+    }
+    if (scheduleComposite[renderRoot]) {
+      scheduleAnyComposite = true;
+    }
   }
 
+  // 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.
+  bool sendDidComposite =
+      !scheduleAnyComposite && mPendingTransactionIds.empty();
+
   // Only register a value for CONTENT_FRAME_TIME telemetry if we actually drew
   // something. It is for consistency with disabling WebRender.
   HoldPendingTransactionId(mWrEpoch, aTransactionId, false, aVsyncId,
                            aVsyncStartTime, aRefreshStartTime, aTxnStartTime,
                            aTxnURL, aFwdTime,
                            /* aIsFirstPaint */ false,
-                           /* aUseForTelemetry */ scheduleComposite);
+                           /* aUseForTelemetry */ scheduleAnyComposite);
 
-  if (scheduleComposite) {
-    // 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();
+  for (auto renderRoot : wr::kRenderRoots) {
+    if (scheduleComposite[renderRoot]) {
+      mAsyncImageManager->SetWillGenerateFrame(renderRoot);
+    }
+  }
+
+  if (scheduleAnyComposite) {
+    ScheduleGenerateFrame(Nothing());
   } 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, VsyncId(), now, now,
                                   now);
     }
   }
 
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aSmallShmems);
-  wr::IpcResourceUpdateQueue::ReleaseShmems(this, aLargeShmems);
+  for (auto& update : aRenderRootUpdates) {
+    wr::IpcResourceUpdateQueue::ReleaseShmems(this, update.mSmallShmems);
+    wr::IpcResourceUpdateQueue::ReleaseShmems(this, update.mLargeShmems);
+  }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSetFocusTarget(
     const FocusTarget& aFocusTarget) {
   UpdateAPZFocusState(aFocusTarget);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvParentCommands(
-    nsTArray<WebRenderParentCommand>&& aCommands) {
+    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);
+
+  Api(aRenderRoot)->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(Api(aRenderRoot), &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);
+                                     aTxn, 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;
       }
@@ -1213,17 +1323,18 @@ bool WebRenderBridgeParent::ProcessWebRe
         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));
           const auto activeAnim = mActiveAnimations.find(data.id());
           if (activeAnim == mActiveAnimations.end()) {
             mActiveAnimations.emplace(data.id(), mWrEpoch);
           } else {
             // Update wr::Epoch if the animation already exists.
             activeAnim->second = mWrEpoch;
           }
         }
@@ -1240,31 +1351,31 @@ bool WebRenderBridgeParent::ProcessWebRe
 
 void WebRenderBridgeParent::FlushSceneBuilds() {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   // Since we are sending transactions through the scene builder thread, we need
   // to block until all the inflight transactions have been processed. This
   // flush message blocks until all previously sent scenes have been built
   // and received by the render backend thread.
-  mApi->FlushSceneBuilder();
+  Api(wr::RenderRoot::Default)->FlushSceneBuilder();
   // The post-swap hook for async-scene-building calls the
   // ScheduleRenderOnCompositorThread function from the scene builder thread,
   // which then triggers a call to ScheduleGenerateFrame() on the compositor
   // thread. But since *this* function is running on the compositor thread,
   // that scheduling will not happen until this call stack unwinds (or we
   // 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();
+  ScheduleGenerateFrameAllRenderRoots();
 }
 
 void WebRenderBridgeParent::FlushFrameGeneration() {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   MOZ_ASSERT(IsRootWebRenderBridgeParent());  // This function is only useful on
                                               // the root WRBP
 
   // This forces a new GenerateFrame transaction to be sent to the render
@@ -1280,17 +1391,17 @@ void WebRenderBridgeParent::FlushFrameGe
 void WebRenderBridgeParent::FlushFramePresentation() {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   // This sends a message to the render backend thread to send a message
   // to the renderer thread, and waits for that message to be processed. So
   // this effectively blocks on the render backend and renderer threads,
   // following the same codepath that WebRender takes to render and composite
   // a frame.
-  mApi->WaitFlushed();
+  Api(wr::RenderRoot::Default)->WaitFlushed();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvGetSnapshot(
     PTextureParent* aTexture) {
   if (mDestroyed) {
     return IPC_OK();
   }
   MOZ_ASSERT(!mPaused);
@@ -1333,31 +1444,35 @@ mozilla::ipc::IPCResult WebRenderBridgeP
   MOZ_ASSERT(bufferTexture->GetFormat() == SurfaceFormat::B8G8R8A8);
   uint32_t buffer_size = size.width * size.height * 4;
 
   // Assert the stride of the buffer is what webrender expects
   MOZ_ASSERT((uint32_t)(size.width * 4) == stride);
 
   FlushSceneBuilds();
   FlushFrameGeneration();
-  mApi->Readback(start, size, Range<uint8_t>(buffer, buffer_size));
+  Api(wr::RenderRoot::Default)
+      ->Readback(start, size, Range<uint8_t>(buffer, buffer_size));
 
   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 = mAsyncCompositables[aRenderRoot];
+
+  MOZ_ASSERT(asyncCompositables.find(wr::AsUint64(aPipelineId)) ==
+             asyncCompositables.end());
 
   RefPtr<CompositableHost> host;
   if (aAsync) {
     RefPtr<ImageBridgeParent> imageBridge =
         ImageBridgeParent::GetInstance(OtherPid());
     if (!imageBridge) {
       return;
     }
@@ -1376,47 +1491,51 @@ void WebRenderBridgeParent::AddPipelineI
         << "Incompatible CompositableHost at WebRenderBridgeParent.";
   }
 
   if (!wrHost) {
     return;
   }
 
   wrHost->SetWrBridge(this);
-  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) {
+    const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn,
+    wr::RenderRoot aRenderRoot) {
   if (mDestroyed) {
     return;
   }
 
-  auto it = mAsyncCompositables.find(wr::AsUint64(aPipelineId));
-  if (it == mAsyncCompositables.end()) {
+  auto& asyncCompositables = mAsyncCompositables[aRenderRoot];
+
+  auto it = asyncCompositables.find(wr::AsUint64(aPipelineId));
+  if (it == asyncCompositables.end()) {
     return;
   }
   RefPtr<WebRenderImageHost>& wrHost = it->second;
 
   wrHost->ClearWrBridge(this);
   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) {
     return;
   }
@@ -1458,56 +1577,65 @@ mozilla::ipc::IPCResult WebRenderBridgeP
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvClearCachedResources() {
   if (mDestroyed) {
     return IPC_OK();
   }
 
-  // Clear resources
-  wr::TransactionBuilder txn;
-  txn.SetLowPriority(true);
-  txn.ClearDisplayList(GetNextWrEpoch(), mPipelineId);
-  txn.Notify(
-      wr::Checkpoint::SceneBuilt,
-      MakeUnique<ScheduleObserveLayersUpdate>(
-          mCompositorBridge, GetLayersId(), mChildLayersObserverEpoch, false));
+  for (auto renderRoot : wr::kRenderRoots) {
+    if (renderRoot == wr::RenderRoot::Default ||
+        (IsRootWebRenderBridgeParent() &&
+         gfxPrefs::WebRenderSplitRenderRoots())) {
+      // Clear resources
+      wr::TransactionBuilder txn;
+      txn.SetLowPriority(true);
+      txn.ClearDisplayList(GetNextWrEpoch(), mPipelineId);
+      txn.Notify(wr::Checkpoint::SceneBuilt,
+                 MakeUnique<ScheduleObserveLayersUpdate>(
+                     mCompositorBridge, GetLayersId(),
+                     mChildLayersObserverEpoch, false));
 
-  mApi->SendTransaction(txn);
+      Api(renderRoot)->SendTransaction(txn);
+    }
+  }
   // Schedule generate frame to clean up Pipeline
-  ScheduleGenerateFrame();
+
+  ScheduleGenerateFrameAllRenderRoots();
+
   // Remove animations.
   for (const auto& id : mActiveAnimations) {
     mAnimStorage->ClearById(id.first);
   }
   mActiveAnimations.clear();
   std::queue<CompositorAnimationIdsForEpoch>().swap(
       mCompositorAnimationsToDelete);  // clear queue
   return IPC_OK();
 }
 
 wr::Epoch WebRenderBridgeParent::UpdateWebRender(
-    CompositorVsyncScheduler* aScheduler, wr::WebRenderAPI* aApi,
+    CompositorVsyncScheduler* aScheduler,
+    nsTArray<RefPtr<wr::WebRenderAPI>>&& aApis,
     AsyncImagePipelineManager* aImageMgr,
     CompositorAnimationStorage* aAnimStorage,
     const TextureFactoryIdentifier& aTextureFactoryIdentifier) {
   MOZ_ASSERT(!IsRootWebRenderBridgeParent());
   MOZ_ASSERT(aScheduler);
-  MOZ_ASSERT(aApi);
+  MOZ_ASSERT(!aApis.IsEmpty());
   MOZ_ASSERT(aImageMgr);
   MOZ_ASSERT(aAnimStorage);
 
   if (mDestroyed) {
     return mWrEpoch;
   }
 
   // Update id name space to identify obsoleted keys.
   // Since usage of invalid keys could cause crash in webrender.
-  mIdNamespace = aApi->GetNamespace();
+  mIdNamespace = aApis[0]->GetNamespace();
   // XXX Remove it when webrender supports sharing/moving Keys between different
   // webrender instances.
   // XXX It requests client to update/reallocate webrender related resources,
   // but parent side does not wait end of the update.
   // The code could become simpler if we could serialise old keys deallocation
   // and new keys allocation. But we do not do it, it is because client side
   // deallocate old layers/webrender keys after new layers/webrender keys
   // allocation. Without client side's layout refactoring, we could not finish
@@ -1515,46 +1643,52 @@ wr::Epoch WebRenderBridgeParent::UpdateW
   // 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;
+  mApis = aApis;
   mAsyncImageManager = aImageMgr;
   mAnimStorage = aAnimStorage;
 
   // Register pipeline to updated AsyncImageManager.
   mAsyncImageManager->AddPipeline(mPipelineId, this);
 
   return GetNextWrEpoch();  // Update webrender epoch
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvScheduleComposite() {
-  ScheduleGenerateFrame();
+  ScheduleGenerateFrameAllRenderRoots();
   return IPC_OK();
 }
 
 void WebRenderBridgeParent::ScheduleForcedGenerateFrame() {
   if (mDestroyed) {
     return;
   }
 
-  wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
-  fastTxn.InvalidateRenderedFrame();
-  mApi->SendTransaction(fastTxn);
+  for (auto renderRoot : wr::kRenderRoots) {
+    if (renderRoot == wr::RenderRoot::Default ||
+        (IsRootWebRenderBridgeParent() &&
+         gfxPrefs::WebRenderSplitRenderRoots())) {
+      wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
+      fastTxn.InvalidateRenderedFrame();
+      Api(renderRoot)->SendTransaction(fastTxn);
+    }
+  }
 
-  ScheduleGenerateFrame();
+  ScheduleGenerateFrameAllRenderRoots();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvCapture() {
   if (!mDestroyed) {
-    mApi->Capture();
+    Api(wr::RenderRoot::Default)->Capture();
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSyncWithCompositor() {
   FlushSceneBuilds();
   if (RefPtr<WebRenderBridgeParent> root = GetRootWebRenderBridgeParent()) {
     root->FlushFrameGeneration();
@@ -1565,26 +1699,35 @@ mozilla::ipc::IPCResult WebRenderBridgeP
   // unneeded textures. Then we can return from this sync IPC call knowing
   // that we've done everything we can to flush stuff on the compositor.
   mAsyncImageManager->ProcessPipelineUpdates();
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSetConfirmedTargetAPZC(
-    const uint64_t& aBlockId, nsTArray<ScrollableLayerGuid>&& aTargets) {
+    const uint64_t& aBlockId, nsTArray<SLGuidAndRenderRoot>&& aTargets) {
   for (size_t i = 0; i < aTargets.Length(); i++) {
-    if (aTargets[i].mLayersId != GetLayersId()) {
-      // Guard against bad data from hijacked child processes
+    // Guard against bad data from hijacked child processes
+    if (aTargets[i].mRenderRoot > wr::kHighestRenderRoot ||
+        (!gfxPrefs::WebRenderSplitRenderRoots() &&
+         aTargets[i].mRenderRoot != wr::RenderRoot::Default)) {
+      NS_ERROR(
+          "Unexpected render root in RecvSetConfirmedTargetAPZC; dropping "
+          "message...");
+      return IPC_FAIL(this, "Bad render root");
+    }
+    if (aTargets[i].mScrollableLayerGuid.mLayersId != GetLayersId()) {
       NS_ERROR(
           "Unexpected layers id in RecvSetConfirmedTargetAPZC; dropping "
           "message...");
       return IPC_FAIL(this, "Bad layers id");
     }
   }
+
   if (mDestroyed) {
     return IPC_OK();
   }
   mCompositorBridge->SetConfirmedTargetAPZC(GetLayersId(), aBlockId, aTargets);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSetTestSampleTime(
@@ -1618,42 +1761,44 @@ mozilla::ipc::IPCResult WebRenderBridgeP
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSetAsyncScrollOffset(
     const ScrollableLayerGuid::ViewID& aScrollId, const float& aX,
     const float& aY) {
   if (mDestroyed) {
     return IPC_OK();
   }
-  mCompositorBridge->SetTestAsyncScrollOffset(GetLayersId(), aScrollId,
-                                              CSSPoint(aX, aY));
+  mCompositorBridge->SetTestAsyncScrollOffset(
+      WRRootId(GetLayersId(), mRenderRoot), aScrollId, CSSPoint(aX, aY));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSetAsyncZoom(
     const ScrollableLayerGuid::ViewID& aScrollId, const float& aZoom) {
   if (mDestroyed) {
     return IPC_OK();
   }
-  mCompositorBridge->SetTestAsyncZoom(GetLayersId(), aScrollId,
+  mCompositorBridge->SetTestAsyncZoom(WRRootId(GetLayersId(), mRenderRoot),
+                                      aScrollId,
                                       LayerToParentLayerScale(aZoom));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvFlushApzRepaints() {
   if (mDestroyed) {
     return IPC_OK();
   }
-  mCompositorBridge->FlushApzRepaints(GetLayersId());
+  mCompositorBridge->FlushApzRepaints(WRRootId(GetLayersId(), mRenderRoot));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvGetAPZTestData(
     APZTestData* aOutData) {
-  mCompositorBridge->GetAPZTestData(GetLayersId(), aOutData);
+  mCompositorBridge->GetAPZTestData(WRRootId(GetLayersId(), mRenderRoot),
+                                    aOutData);
   return IPC_OK();
 }
 
 void WebRenderBridgeParent::ActorDestroy(ActorDestroyReason aWhy) { Destroy(); }
 
 bool WebRenderBridgeParent::AdvanceAnimations() {
   if (CompositorBridgeParent* cbp = GetRootCompositorBridgeParent()) {
     Maybe<TimeStamp> testingTimeStamp = cbp->GetTestingTimeStamp();
@@ -1676,30 +1821,33 @@ bool WebRenderBridgeParent::AdvanceAnima
   // animations to avoid using the time which is far behind for newly
   // started animations.
   mPreviousFrameTimeStamp = isAnimating ? lastComposeTime : TimeStamp();
 
   return isAnimating;
 }
 
 bool WebRenderBridgeParent::SampleAnimations(
-    nsTArray<wr::WrOpacityProperty>& aOpacityArray,
-    nsTArray<wr::WrTransformProperty>& aTransformArray) {
+    wr::RenderRootArray<nsTArray<wr::WrOpacityProperty>>& aOpacityArrays,
+    wr::RenderRootArray<nsTArray<wr::WrTransformProperty>>& aTransformArrays) {
   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 = aTransformArrays[renderRoot];
+      auto& opacityArray = aOpacityArrays[renderRoot];
       if (value->mType == AnimatedValue::TRANSFORM) {
-        aTransformArray.AppendElement(wr::ToWrTransformProperty(
+        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;
 }
 
@@ -1722,18 +1870,18 @@ void WebRenderBridgeParent::CompositeToT
   MOZ_ASSERT(aRect == nullptr);
 
   AUTO_PROFILER_TRACING("Paint", "CompositeToTarget", GRAPHICS);
   if (mPaused || !mReceivedDisplayList) {
     mPreviousFrameTimeStamp = TimeStamp();
     return;
   }
 
-  if (mSkippedComposite ||
-      wr::RenderThread::Get()->TooManyPendingFrames(mApi->GetId())) {
+  if (mSkippedComposite || wr::RenderThread::Get()->TooManyPendingFrames(
+                               Api(wr::RenderRoot::Default)->GetId())) {
     // Render thread is busy, try next time.
     mSkippedComposite = true;
     mSkippedCompositeId = aId;
     mPreviousFrameTimeStamp = TimeStamp();
 
     // Record that we skipped presenting a frame for
     // all pending transactions that have finished scene building.
     for (auto& id : mPendingTransactionIds) {
@@ -1758,67 +1906,94 @@ TimeDuration WebRenderBridgeParent::GetV
 void WebRenderBridgeParent::MaybeGenerateFrame(VsyncId aId,
                                                bool aForceGenerateFrame) {
   // This function should only get called in the root WRBP
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 
   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::RenderRootArray<Maybe<wr::TransactionBuilder>> fastTxns;
   // Handle transaction that is related to DisplayList.
-  wr::TransactionBuilder sceneBuilderTxn;
-  wr::AutoTransactionSender sender(mApi, &sceneBuilderTxn);
+  wr::RenderRootArray<Maybe<wr::TransactionBuilder>> sceneBuilderTxns;
+  wr::RenderRootArray<Maybe<wr::AutoTransactionSender>> senders;
+  for (auto& api : mApis) {
+    auto renderRoot = api->GetRenderRoot();
+    // 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.
+    fastTxns[renderRoot].emplace(false /* useSceneBuilderThread */);
+    sceneBuilderTxns[renderRoot].emplace();
+    senders[renderRoot].emplace(api, sceneBuilderTxns[renderRoot].ptr());
+  }
 
   // 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(sceneBuilderTxns, fastTxns);
 
   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) {
+  uint8_t framesGenerated = 0;
+  wr::RenderRootArray<bool> generateFrame;
+  for (auto& api : mApis) {
+    auto renderRoot = api->GetRenderRoot();
+    generateFrame[renderRoot] =
+        mAsyncImageManager->GetAndResetWillGenerateFrame(renderRoot) ||
+        !fastTxns[renderRoot]->IsEmpty() || aForceGenerateFrame;
+    if (generateFrame[renderRoot]) {
+      framesGenerated++;
+    }
+  }
+
+  if (framesGenerated == 0) {
     // Could skip generating frame now.
     mPreviousFrameTimeStamp = TimeStamp();
     return;
   }
 
-  nsTArray<wr::WrOpacityProperty> opacityArray;
-  nsTArray<wr::WrTransformProperty> transformArray;
+  wr::RenderRootArray<nsTArray<wr::WrOpacityProperty>> opacityArrays;
+  wr::RenderRootArray<nsTArray<wr::WrTransformProperty>> transformArrays;
 
-  if (SampleAnimations(opacityArray, transformArray)) {
-    ScheduleGenerateFrame();
+  if (SampleAnimations(opacityArrays, transformArrays)) {
+    // TODO we should have a better way of assessing whether we need a content
+    // or a chrome frame generation.
+    ScheduleGenerateFrameAllRenderRoots();
   }
   // 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);
+  for (auto& api : mApis) {
+    auto renderRoot = api->GetRenderRoot();
+    fastTxns[renderRoot]->UpdateDynamicProperties(opacityArrays[renderRoot],
+                                                  transformArrays[renderRoot]);
+  }
 
   SetAPZSampleTime();
 
-  wr::RenderThread::Get()->IncPendingFrameCount(mApi->GetId(), aId, start);
+  wr::RenderThread::Get()->IncPendingFrameCount(
+      Api(wr::RenderRoot::Default)->GetId(), aId, start, framesGenerated);
 
 #if defined(ENABLE_FRAME_LATENCY_LOG)
   auto startTime = TimeStamp::Now();
-  mApi->SetFrameStartTime(startTime);
+  Api(wr::RenderRoot::Default)->SetFrameStartTime(startTime);
 #endif
 
-  fastTxn.GenerateFrame();
-
-  mApi->SendTransaction(fastTxn);
+  MOZ_ASSERT(framesGenerated > 0);
+  for (auto& api : mApis) {
+    auto renderRoot = api->GetRenderRoot();
+    if (generateFrame[renderRoot]) {
+      fastTxns[renderRoot]->GenerateFrame();
+      mApis[(size_t)renderRoot]->SendTransaction(*fastTxns[renderRoot]);
+    }
+  }
   mMostRecentComposite = TimeStamp::Now();
 }
 
 void WebRenderBridgeParent::HoldPendingTransactionId(
     const wr::Epoch& aWrEpoch, TransactionId aTransactionId,
     bool aContainsSVGGroup, const VsyncId& aVsyncId,
     const TimeStamp& aVsyncStartTime, const TimeStamp& aRefreshStartTime,
     const TimeStamp& aTxnStartTime, const nsCString& aTxnURL,
@@ -1826,16 +2001,27 @@ void WebRenderBridgeParent::HoldPendingT
     const bool aUseForTelemetry) {
   MOZ_ASSERT(aTransactionId > LastPendingTransactionId());
   mPendingTransactionIds.push_back(PendingTransactionId(
       aWrEpoch, aTransactionId, aContainsSVGGroup, aVsyncId, aVsyncStartTime,
       aRefreshStartTime, aTxnStartTime, aTxnURL, aFwdTime, aIsFirstPaint,
       aUseForTelemetry));
 }
 
+already_AddRefed<wr::WebRenderAPI>
+WebRenderBridgeParent::GetWebRenderAPIAtPoint(const gfx::IntPoint& aPoint) {
+  MutexAutoLock lock(mRenderRootRectMutex);
+  for (auto renderRoot : wr::kNonDefaultRenderRoots) {
+    if (mRenderRootRects[renderRoot].Contains(aPoint)) {
+      return do_AddRef(Api(renderRoot));
+    }
+  }
+  return do_AddRef(Api(wr::RenderRoot::Default));
+}
+
 TransactionId WebRenderBridgeParent::LastPendingTransactionId() {
   TransactionId id{0};
   if (!mPendingTransactionIds.empty()) {
     id = mPendingTransactionIds.back().mId;
   }
   return id;
 }
 
@@ -1845,23 +2031,23 @@ void WebRenderBridgeParent::NotifySceneB
     if (id.mEpoch.mHandle == aEpoch.mHandle) {
       id.mSceneBuiltTime = aEndTime;
       break;
     }
   }
 }
 
 void WebRenderBridgeParent::NotifyDidSceneBuild(
-    RefPtr<wr::WebRenderPipelineInfo> aInfo) {
+    wr::RenderRoot aRenderRoot, RefPtr<wr::WebRenderPipelineInfo> aInfo) {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
   if (!mCompositorScheduler) {
     return;
   }
 
-  mAsyncImageManager->SetWillGenerateFrame();
+  mAsyncImageManager->SetWillGenerateFrame(aRenderRoot);
 
   // If the scheduler has a composite more recent than our last composite (which
   // we missed), and we're within the threshold ms of the last vsync, then
   // kick of a late composite.
   TimeStamp lastVsync = mCompositorScheduler->GetLastVsyncTime();
   VsyncId lastVsyncId = mCompositorScheduler->GetLastVsyncId();
   if (lastVsyncId == VsyncId() || !mMostRecentComposite ||
       mMostRecentComposite >= lastVsync ||
@@ -1927,17 +2113,18 @@ TransactionId WebRenderBridgeParent::Flu
             transactionId.mId, aCompositeStartTime, aRenderStartTime, aEndTime,
             contentFrameTime,
             aStats ? (double(aStats->resource_upload_time) / 1000000.0) : 0.0,
             aStats ? (double(aStats->gpu_cache_upload_time) / 1000000.0) : 0.0,
             transactionId.mTxnStartTime, transactionId.mRefreshStartTime,
             transactionId.mFwdTime, transactionId.mSceneBuiltTime,
             transactionId.mSkippedComposites, transactionId.mTxnURL));
 
-        wr::RenderThread::Get()->NotifySlowFrame(mApi->GetId());
+        wr::RenderThread::Get()->NotifySlowFrame(
+            Api(wr::RenderRoot::Default)->GetId());
       }
     }
 
 #if defined(ENABLE_FRAME_LATENCY_LOG)
     if (transactionId.mRefreshStartTime) {
       int32_t latencyMs =
           lround((aEndTime - transactionId.mRefreshStartTime).ToMilliseconds());
       printf_stderr(
@@ -1964,19 +2151,29 @@ TransactionId WebRenderBridgeParent::Flu
   }
   return id;
 }
 
 LayersId WebRenderBridgeParent::GetLayersId() const {
   return wr::AsLayersId(mPipelineId);
 }
 
-void WebRenderBridgeParent::ScheduleGenerateFrame() {
+void WebRenderBridgeParent::ScheduleGenerateFrameAllRenderRoots() {
   if (mCompositorScheduler) {
-    mAsyncImageManager->SetWillGenerateFrame();
+    mAsyncImageManager->SetWillGenerateFrameAllRenderRoots();
+    mCompositorScheduler->ScheduleComposition();
+  }
+}
+
+void WebRenderBridgeParent::ScheduleGenerateFrame(
+    const Maybe<wr::RenderRoot>& aRenderRoot) {
+  if (mCompositorScheduler) {
+    if (aRenderRoot.isSome()) {
+      mAsyncImageManager->SetWillGenerateFrame(*aRenderRoot);
+    }
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 void WebRenderBridgeParent::FlushRendering(bool aWaitForPresent) {
   if (mDestroyed) {
     return;
   }
@@ -1991,92 +2188,97 @@ void WebRenderBridgeParent::FlushRenderi
 }
 
 void WebRenderBridgeParent::Pause() {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 #ifdef MOZ_WIDGET_ANDROID
   if (!IsRootWebRenderBridgeParent() || mDestroyed) {
     return;
   }
-  mApi->Pause();
+
+  Api(wr::RenderRoot::Default)->Pause();
 #endif
   mPaused = true;
 }
 
 bool WebRenderBridgeParent::Resume() {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 #ifdef MOZ_WIDGET_ANDROID
   if (!IsRootWebRenderBridgeParent() || mDestroyed) {
     return false;
   }
 
-  if (!mApi->Resume()) {
+  if (!Api(wr::RenderRoot::Default)->Resume()) {
     return false;
   }
 #endif
   mPaused = false;
   return true;
 }
 
 void WebRenderBridgeParent::ClearResources() {
-  if (!mApi) {
+  if (mApis.IsEmpty()) {
     return;
   }
 
   wr::Epoch wrEpoch = GetNextWrEpoch();
+  mReceivedDisplayList = false;
+  // Schedule generate frame to clean up Pipeline
+  ScheduleGenerateFrameAllRenderRoots();
 
-  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 : mAsyncCompositables) {
-    wr::PipelineId pipelineId = wr::AsPipelineId(entry.first);
-    RefPtr<WebRenderImageHost> host = entry.second;
-    host->ClearWrBridge(this);
-    mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId, txn);
-    txn.RemovePipeline(pipelineId);
-  }
-  mAsyncCompositables.clear();
+
   for (const auto& entry : mSharedSurfaceIds) {
     mAsyncImageManager->HoldExternalImage(mPipelineId, mWrEpoch, entry.second);
   }
   mSharedSurfaceIds.clear();
 
   mAsyncImageManager->RemovePipeline(mPipelineId, wrEpoch);
-  txn.RemovePipeline(mPipelineId);
+
+  for (auto& api : mApis) {
+    wr::TransactionBuilder txn;
+    txn.SetLowPriority(true);
+    txn.ClearDisplayList(wrEpoch, mPipelineId);
 
-  mApi->SendTransaction(txn);
+    auto renderRoot = api->GetRenderRoot();
+    for (const auto& entry : mAsyncCompositables[renderRoot]) {
+      wr::PipelineId pipelineId = wr::AsPipelineId(entry.first);
+      RefPtr<WebRenderImageHost> host = entry.second;
+      host->ClearWrBridge(this);
+      mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId, txn);
+      txn.RemovePipeline(pipelineId);
+    }
+    mAsyncCompositables[renderRoot].clear();
+    txn.RemovePipeline(mPipelineId);
+    api->SendTransaction(txn);
+  }
 
   for (const auto& id : mActiveAnimations) {
     mAnimStorage->ClearById(id.first);
   }
   mActiveAnimations.clear();
   std::queue<CompositorAnimationIdsForEpoch>().swap(
       mCompositorAnimationsToDelete);  // clear queue
 
   if (IsRootWebRenderBridgeParent()) {
     mCompositorScheduler->Destroy();
   }
 
   mAnimStorage = nullptr;
   mCompositorScheduler = nullptr;
   mAsyncImageManager = nullptr;
-  mApi = nullptr;
+  mApis.Clear();
   mCompositorBridge = nullptr;
 }
 
 bool WebRenderBridgeParent::ShouldParentObserveEpoch() {
   if (mParentLayersObserverEpoch == mChildLayersObserverEpoch) {
     return false;
   }
 
@@ -2128,22 +2330,24 @@ mozilla::ipc::IPCResult WebRenderBridgeP
   if (mDestroyed) {
     return IPC_OK();
   }
   ReleaseCompositable(aHandle);
   return IPC_OK();
 }
 
 TextureFactoryIdentifier WebRenderBridgeParent::GetTextureFactoryIdentifier() {
-  MOZ_ASSERT(mApi);
+  MOZ_ASSERT(!mApis.IsEmpty());
 
   return TextureFactoryIdentifier(
-      LayersBackend::LAYERS_WR, XRE_GetProcessType(), mApi->GetMaxTextureSize(),
-      false, mApi->GetUseANGLE(), mApi->GetUseDComp(), false, false, false,
-      mApi->GetSyncHandle());
+      LayersBackend::LAYERS_WR, XRE_GetProcessType(),
+      Api(wr::RenderRoot::Default)->GetMaxTextureSize(), false,
+      Api(wr::RenderRoot::Default)->GetUseANGLE(),
+      Api(wr::RenderRoot::Default)->GetUseDComp(), false, false, false,
+      Api(wr::RenderRoot::Default)->GetSyncHandle());
 }
 
 wr::Epoch WebRenderBridgeParent::GetNextWrEpoch() {
   MOZ_RELEASE_ASSERT(mWrEpoch.mHandle != UINT32_MAX);
   mWrEpoch.mHandle++;
   return mWrEpoch;
 }
 
--- a/gfx/layers/wr/WebRenderBridgeParent.h
+++ b/gfx/layers/wr/WebRenderBridgeParent.h
@@ -54,27 +54,44 @@ class WebRenderBridgeParent final
       public layers::FrameRecorder,
       public SupportsWeakPtr<WebRenderBridgeParent> {
  public:
   MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebRenderBridgeParent)
   WebRenderBridgeParent(CompositorBridgeParentBase* aCompositorBridge,
                         const wr::PipelineId& aPipelineId,
                         widget::CompositorWidget* aWidget,
                         CompositorVsyncScheduler* aScheduler,
-                        RefPtr<wr::WebRenderAPI>&& aApi,
+                        nsTArray<RefPtr<wr::WebRenderAPI>>&& aApis,
                         RefPtr<AsyncImagePipelineManager>&& aImageMgr,
                         RefPtr<CompositorAnimationStorage>&& aAnimStorage,
                         TimeDuration aVsyncRate);
 
   static WebRenderBridgeParent* CreateDestroyed(
       const wr::PipelineId& aPipelineId);
 
   wr::PipelineId PipelineId() { return mPipelineId; }
-  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() {
-    return do_AddRef(mApi);
+
+  bool CloneWebRenderAPIs(nsTArray<RefPtr<wr::WebRenderAPI>>& aOutAPIs) {
+    for (auto& api : mApis) {
+      RefPtr<wr::WebRenderAPI> clone = api->Clone();
+      if (!clone) {
+        return false;
+      }
+      aOutAPIs.AppendElement(clone);
+    }
+    return true;
+  }
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPIAtPoint(
+      const gfx::IntPoint& aPoint);
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI(
+      wr::RenderRoot aRenderRoot) {
+    if ((size_t)aRenderRoot >= mApis.Length()) {
+      return nullptr;
+    }
+    return do_AddRef(mApis[(int)aRenderRoot]);
   }
   AsyncImagePipelineManager* AsyncImageManager() { return mAsyncImageManager; }
   CompositorVsyncScheduler* CompositorScheduler() {
     return mCompositorScheduler.get();
   }
   CompositorBridgeParentBase* GetCompositorBridge() {
     return mCompositorBridge;
   }
@@ -90,61 +107,53 @@ class WebRenderBridgeParent final
 
   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;
+      nsTArray<ipc::Shmem>&& aLargeShmems,
+      const wr::RenderRoot& aRenderRoot) override;
   mozilla::ipc::IPCResult RecvSetDisplayList(
-      const gfx::IntSize& aSize,
-      InfallibleTArray<WebRenderParentCommand>&& aCommands,
+      nsTArray<RenderRootDisplayListData>&& aDisplayLists,
       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 VsyncId& aVsyncId,
+      const wr::IdNamespace& aIdNamespace, const bool& aContainsSVGGroup,
+      const VsyncId& aVsyncId, const TimeStamp& aVsyncStartTime,
+      const TimeStamp& aRefreshStartTime, const TimeStamp& aTxnStartTime,
+      const nsCString& aTxnURL, const TimeStamp& aFwdTime) override;
+  mozilla::ipc::IPCResult RecvEmptyTransaction(
+      const FocusTarget& aFocusTarget, const uint32_t& aPaintSequenceNumber,
+      nsTArray<RenderRootUpdates>&& aRenderRootUpdates,
+      InfallibleTArray<OpDestroy>&& aToDestroy,
+      const uint64_t& aFwdTransactionId, const TransactionId& aTransactionId,
+      const wr::IdNamespace& aIdNamespace, const VsyncId& aVsyncId,
       const TimeStamp& aVsyncStartTime, 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<OpDestroy>&& aToDestroy,
-      const uint64_t& aFwdTransactionId, const TransactionId& aTransactionId,
-      nsTArray<OpUpdateResource>&& aResourceUpdates,
-      nsTArray<RefCountedShmem>&& aSmallShmems,
-      nsTArray<ipc::Shmem>&& aLargeShmems, const wr::IdNamespace& aIdNamespace,
-      const VsyncId& aVsyncId, const TimeStamp& aVsyncStartTime,
-      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;
+      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;
 
   mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(
       const uint64_t& aBlockId,
-      nsTArray<ScrollableLayerGuid>&& aTargets) override;
+      nsTArray<SLGuidAndRenderRoot>&& aTargets) override;
 
   mozilla::ipc::IPCResult RecvSetTestSampleTime(
       const TimeStamp& aTime) override;
   mozilla::ipc::IPCResult RecvLeaveTestMode() override;
   mozilla::ipc::IPCResult RecvGetAnimationValue(
       const uint64_t& aCompositorAnimationsId, OMTAValue* aValue) override;
   mozilla::ipc::IPCResult RecvSetAsyncScrollOffset(
       const ScrollableLayerGuid::ViewID& aScrollId, const float& aX,
@@ -214,30 +223,33 @@ class WebRenderBridgeParent final
    * 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(const Maybe<wr::RenderRoot>& aRenderRoot);
+  void ScheduleGenerateFrameAllRenderRoots();
 
   /**
    * 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();
 
-  void NotifyDidSceneBuild(RefPtr<wr::WebRenderPipelineInfo> aInfo);
+  void NotifyDidSceneBuild(wr::RenderRoot aRenderRoot,
+                           RefPtr<wr::WebRenderPipelineInfo> aInfo);
 
   wr::Epoch UpdateWebRender(
-      CompositorVsyncScheduler* aScheduler, wr::WebRenderAPI* aApi,
+      CompositorVsyncScheduler* aScheduler,
+      nsTArray<RefPtr<wr::WebRenderAPI>>&& aApis,
       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
@@ -246,28 +258,67 @@ class WebRenderBridgeParent final
    * the viewport information). The message will sent back to the widget code
    * via UiCompositorControllerParent::NotifyFirstPaint() when the corresponding
    * transaction is flushed.
    */
   void ForceIsFirstPaint() { mIsFirstPaint = true; }
 
   bool IsRootWebRenderBridgeParent() const;
   LayersId GetLayersId() const;
+  WRRootId GetWRRootId() const;
 
  private:
   class ScheduleSharedSurfaceRelease;
 
   explicit WebRenderBridgeParent(const wr::PipelineId& aPipelineId);
   virtual ~WebRenderBridgeParent();
 
+  wr::WebRenderAPI* Api(wr::RenderRoot aRenderRoot) {
+    if (IsRootWebRenderBridgeParent()) {
+      return mApis[(size_t)aRenderRoot];
+    } else {
+      MOZ_ASSERT(aRenderRoot == wr::RenderRoot::Default);
+      return mApis[(size_t)mRenderRoot];
+    }
+  }
+
+  // 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 (IsRootWebRenderBridgeParent()) {
+      return aRenderRoot;
+    } else {
+      MOZ_ASSERT(aRenderRoot == wr::RenderRoot::Default);
+      return mRenderRoot;
+    }
+  }
+
+  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 UpdateAPZScrollData(const wr::Epoch& aEpoch, WebRenderScrollData&& aData,
+                           wr::RenderRoot aRenderRoot);
   void UpdateAPZScrollOffsets(ScrollUpdatesMap&& aUpdates,
-                              uint32_t aPaintSequenceNumber);
+                              uint32_t aPaintSequenceNumber,
+                              wr::RenderRoot aRenderRoot);
 
   bool UpdateResources(const nsTArray<OpUpdateResource>& aResourceUpdates,
                        const nsTArray<RefCountedShmem>& aSmallShmems,
                        const nsTArray<ipc::Shmem>& aLargeShmems,
                        wr::TransactionBuilder& aUpdates);
   bool AddExternalImage(wr::ExternalImageId aExtId, wr::ImageKey aKey,
                         wr::TransactionBuilder& aResources);
   bool UpdateExternalImage(
@@ -281,36 +332,39 @@ class WebRenderBridgeParent final
                                    wr::ImageKey aKey, 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);
+  bool SampleAnimations(
+      wr::RenderRootArray<nsTArray<wr::WrOpacityProperty>>& aOpacityArrays,
+      wr::RenderRootArray<nsTArray<wr::WrTransformProperty>>& aTransformArrays);
 
   CompositorBridgeParent* GetRootCompositorBridgeParent() const;
 
   RefPtr<WebRenderBridgeParent> GetRootWebRenderBridgeParent() const;
 
   // Tell APZ what the subsequent sampling's timestamp should be.
   void SetAPZSampleTime();
 
@@ -379,25 +433,26 @@ class WebRenderBridgeParent final
 
     wr::Epoch mEpoch;
     InfallibleTArray<uint64_t> mIds;
   };
 
   CompositorBridgeParentBase* MOZ_NON_OWNING_REF mCompositorBridge;
   wr::PipelineId mPipelineId;
   RefPtr<widget::CompositorWidget> mWidget;
-  RefPtr<wr::WebRenderAPI> mApi;
+  nsTArray<RefPtr<wr::WebRenderAPI>> mApis;
   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_map<uint64_t, wr::Epoch> mActiveAnimations;
-  std::unordered_map<uint64_t, RefPtr<WebRenderImageHost>> mAsyncCompositables;
+  wr::RenderRootArray<std::unordered_map<uint64_t, RefPtr<WebRenderImageHost>>>
+      mAsyncCompositables;
   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
@@ -408,16 +463,22 @@ class WebRenderBridgeParent final
   std::deque<PendingTransactionId> mPendingTransactionIds;
   std::queue<CompositorAnimationIdsForEpoch> mCompositorAnimationsToDelete;
   wr::Epoch mWrEpoch;
   wr::IdNamespace mIdNamespace;
 
   VsyncId mSkippedCompositeId;
   TimeStamp mMostRecentComposite;
 
+  // Kind of clunky, but I can't sort out a more elegant way of getting this to
+  // work.
+  Mutex mRenderRootRectMutex;
+  wr::NonDefaultRenderRootArray<IntRect> mRenderRootRects;
+
+  wr::RenderRoot mRenderRoot;
   bool mPaused;
   bool mDestroyed;
   bool mReceivedDisplayList;
   bool mIsFirstPaint;
   bool mSkippedComposite;
 };
 
 }  // namespace layers
--- a/gfx/layers/wr/WebRenderCanvasRenderer.cpp
+++ b/gfx/layers/wr/WebRenderCanvasRenderer.cpp
@@ -78,17 +78,17 @@ void WebRenderCanvasRendererAsync::Destr
   if (mPipelineId.isSome()) {
     mManager->RemovePipelineIdForCompositable(mPipelineId.ref());
     mPipelineId.reset();
   }
 }
 
 void WebRenderCanvasRendererAsync::
     UpdateCompositableClientForEmptyTransaction() {
-  UpdateCompositableClient();
+  UpdateCompositableClient(mManager->GetRenderRoot());
   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->AddWebRenderParentCommand(
         OpUpdatedAsyncImagePipeline(mPipelineId.ref()));
--- a/gfx/layers/wr/WebRenderCommandBuilder.cpp
+++ b/gfx/layers/wr/WebRenderCommandBuilder.cpp
@@ -295,17 +295,17 @@ struct DIGroup {
   LayerPoint mResidualOffset;
   LayerIntRect mLayerBounds;
   // The current bounds of the blob image, relative to
   // the top-left of the mLayerBounds.
   IntRect mImageBounds;
   // mImageBounds clipped to the container/parent of the
   // current item being processed.
   IntRect mClippedImageBounds;
-  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) {}
 
   void InvalidateRect(const IntRect& aRect) {
@@ -326,17 +326,17 @@ struct DIGroup {
       iter.Remove();
       delete data;
     }
   }
 
   void ClearImageKey(RenderRootStateManager* aManager, bool aForce = false) {
     if (mKey) {
       MOZ_RELEASE_ASSERT(aForce || mInvalidRect.IsEmpty());
-      aManager->AddBlobImageKeyForDiscard(mKey.value());
+      aManager->AddBlobImageKeyForDiscard(mKey.value().second());
       mKey = Nothing();
     }
     mFonts.clear();
   }
 
   static IntRect ToDeviceSpace(nsRect aBounds, Matrix& aMatrix,
                                int32_t aAppUnitsPerDevPixel,
                                LayerIntPoint aOffset) {
@@ -627,17 +627,17 @@ struct DIGroup {
     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;
@@ -647,17 +647,17 @@ struct DIGroup {
         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) {
                 Maybe<wr::FontInstanceKey> key =
                     aWrManager->WrBridge()->GetFontKeyForScaledFont(
-                        scaled, &aResources);
+                        scaled, aBuilder.GetRenderRoot(), &aResources);
                 if (key.isNothing()) {
                   validFonts = false;
                   break;
                 }
                 BlobFont font = {key.value(), scaled};
                 aStream.write((const char*)&font, sizeof(font));
               }
               fonts = std::move(aScaledFonts);
@@ -674,29 +674,33 @@ struct DIGroup {
     context->SetMatrix(Matrix::Scaling(mScale.width, mScale.height)
                            .PreTranslate(-bounds.x, -bounds.y));
 
     GP("mInvalidRect: %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y,
        mInvalidRect.width, mInvalidRect.height);
 
     bool empty = aStartItem == aEndItem;
     if (empty) {
-      ClearImageKey(aWrManager->GetRenderRootStateManager(), true);
+      ClearImageKey(
+          aWrManager->GetRenderRootStateManager(aBuilder.GetRenderRoot()),
+          true);
       return;
     }
 
     PaintItemRange(aGrouper, aStartItem, aEndItem, context, recorder);
 
     // XXX: set this correctly perhaps using
     // aItem->GetOpaqueRegion(aDisplayListBuilder, &snapped).
     //   Contains(paintBounds);?
     wr::OpacityType opacity = wr::OpacityType::HasAlphaChannel;
 
-    TakeExternalSurfaces(recorder, mExternalSurfaces,
-                         aWrManager->GetRenderRootStateManager(), aResources);
+    TakeExternalSurfaces(
+        recorder, mExternalSurfaces,
+        aWrManager->GetRenderRootStateManager(aBuilder.GetRenderRoot()),
+        aResources);
     bool hasItems = recorder->Finish();
     GP("%d Finish\n", hasItems);
     if (!validFonts) {
       gfxCriticalNote << "Failed serializing fonts for blob image";
       return;
     }
     Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
                          recorder->mOutputStream.mLength);
@@ -707,35 +711,35 @@ struct DIGroup {
       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,
+      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);
@@ -751,17 +755,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) {
     LayerIntSize size = mLayerBounds.Size();
     for (nsDisplayItem* item = aStartItem; item != aEndItem;
@@ -1001,16 +1005,72 @@ void Grouper::PaintContainerItem(DIGroup
 
     default:
       aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext,
                              aRecorder);
       break;
   }
 }
 
+size_t WebRenderScrollDataCollection::GetLayerCount(
+    wr::RenderRoot aRoot) const {
+  return mInternalScrollDatas[aRoot].size();
+}
+
+void WebRenderScrollDataCollection::AppendRoot(
+    Maybe<ScrollMetadata>& aRootMetadata,
+    wr::RenderRootArray<WebRenderScrollData>& aScrollDatas) {
+  mSeenRenderRoot[wr::RenderRoot::Default] = true;
+
+  for (auto renderRoot : wr::kRenderRoots) {
+    if (mSeenRenderRoot[renderRoot]) {
+      auto& layerScrollData = mInternalScrollDatas[renderRoot];
+      layerScrollData.emplace_back();
+      layerScrollData.back().InitializeRoot(layerScrollData.size() - 1);
+
+      if (aRootMetadata) {
+        layerScrollData.back().AppendScrollMetadata(aScrollDatas[renderRoot],
+                                                    aRootMetadata.ref());
+      }
+    }
+  }
+}
+
+void WebRenderScrollDataCollection::AppendWrapper(
+    const RenderRootBoundary& aBoundary, size_t aLayerCountBeforeRecursing) {
+  wr::RenderRoot root = aBoundary.GetChildType();
+  size_t layerCountAfterRecursing = GetLayerCount(root);
+  MOZ_ASSERT(layerCountAfterRecursing >= aLayerCountBeforeRecursing);
+  if (layerCountAfterRecursing == aLayerCountBeforeRecursing) {
+    // nothing to wrap
+    return;
+  }
+  mInternalScrollDatas[root].emplace_back();
+  mInternalScrollDatas[root].back().InitializeRoot(layerCountAfterRecursing -
+                                                   aLayerCountBeforeRecursing);
+  mInternalScrollDatas[root].back().SetBoundaryRoot(aBoundary);
+}
+
+void WebRenderScrollDataCollection::AppendScrollData(
+    const wr::DisplayListBuilder& aBuilder, WebRenderLayerManager* aManager,
+    nsDisplayItem* aItem, size_t aLayerCountBeforeRecursing,
+    const ActiveScrolledRoot* aStopAtAsr,
+    const Maybe<gfx::Matrix4x4>& aAncestorTransform) {
+  wr::RenderRoot renderRoot = aBuilder.GetRenderRoot();
+  mSeenRenderRoot[renderRoot] = true;
+
+  int descendants =
+      mInternalScrollDatas[renderRoot].size() - aLayerCountBeforeRecursing;
+
+  mInternalScrollDatas[renderRoot].emplace_back();
+  mInternalScrollDatas[renderRoot].back().Initialize(
+      aManager->GetScrollData(renderRoot), aItem, descendants, aStopAtAsr,
+      aAncestorTransform, renderRoot);
+}
+
 class WebRenderGroupData : public WebRenderUserData {
  public:
   explicit WebRenderGroupData(RenderRootStateManager* aWRManager,
                               nsDisplayItem* aItem);
   virtual ~WebRenderGroupData();
 
   virtual WebRenderGroupData* AsGroupData() override { return this; }
   virtual UserDataType GetType() override { return UserDataType::eGroup; }
@@ -1097,36 +1157,38 @@ void Grouper::ConstructGroups(nsDisplayL
   nsDisplayItem* startOfCurrentGroup = item;
   while (item) {
     if (IsItemProbablyActive(item, mDisplayListBuilder)) {
       currentGroup->EndGroup(aCommandBuilder->mManager, aDisplayListBuilder,
                              aBuilder, aResources, this, startOfCurrentGroup,
                              item);
 
       {
+        MOZ_ASSERT(item->GetType() != DisplayItemType::TYPE_RENDER_ROOT);
         auto spaceAndClipChain = mClipManager.SwitchItem(item);
         wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain);
 
         sIndent++;
         // Note: this call to CreateWebRenderCommands can recurse back into
         // this function.
         RenderRootStateManager* manager =
-            aCommandBuilder->mManager->GetRenderRootStateManager();
+            aCommandBuilder->mManager->GetRenderRootStateManager(
+                aBuilder.GetRenderRoot());
         bool createdWRCommands = item->CreateWebRenderCommands(
             aBuilder, aResources, aSc, manager, mDisplayListBuilder);
         sIndent--;
         MOZ_RELEASE_ASSERT(
             createdWRCommands,
             "active transforms should always succeed at creating "
             "WebRender commands");
       }
 
       RefPtr<WebRenderGroupData> groupData =
           aCommandBuilder->CreateOrRecycleWebRenderUserData<WebRenderGroupData>(
-              item);
+              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 b