doc splitting megapatch draft
authorDoug Thayer <dothayer@mozilla.com>
Sat, 24 Nov 2018 11:18:25 -0800
changeset 1832376 9b372d014146
parent 1831975 4677e6074704
child 1832377 d768c5acc7c7
push id332561
push userdothayer@mozilla.com
push dateThu, 07 Feb 2019 00:12:13 +0000
treeherdertry@d768c5acc7c7 [default view] [failures only]
milestone67.0a1
doc splitting megapatch
browser/base/content/browser.xul
browser/components/extensions/ExtensionPopups.jsm
dom/ipc/PBrowser.ipdl
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/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/FocusState.cpp
gfx/layers/apz/src/FocusState.h
gfx/layers/apz/src/HitTestingTreeNode.cpp
gfx/layers/apz/src/HitTestingTreeNode.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/test/gtest/TestEventRegions.cpp
gfx/layers/apz/test/gtest/TestTreeManager.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/CrossProcessCompositorBridgeParent.cpp
gfx/layers/ipc/CrossProcessCompositorBridgeParent.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/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/thebes/gfxFontMissingGlyphs.cpp
gfx/thebes/gfxPrefs.h
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.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
ipc/glue/ByteBuf.h
layout/generic/TextDrawTarget.h
layout/generic/nsFrame.cpp
layout/generic/nsHTMLCanvasFrame.cpp
layout/painting/RetainedDisplayListBuilder.cpp
layout/painting/nsDisplayItemTypesList.h
layout/painting/nsDisplayList.cpp
layout/painting/nsDisplayList.h
layout/tools/reftest/reftest.xul
layout/xul/nsBoxFrame.cpp
testing/marionette/reftest.xul
widget/PuppetWidget.cpp
widget/PuppetWidget.h
widget/nsBaseWidget.cpp
widget/nsBaseWidget.h
widget/nsIWidget.h
xpcom/ds/StaticAtoms.py
--- a/browser/base/content/browser.xul
+++ b/browser/base/content/browser.xul
@@ -717,17 +717,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                 accesskey="&selectAllCmd.accesskey;"
                 cmd="cmd_selectAll"/>
       <menuseparator/>
       <menuitem label="&syncSyncNowItem.label;"
                 accesskey="&syncSyncNowItem.accesskey;"
                 id="syncedTabsRefreshFilter"/>
     </menupopup>
 
-    <hbox id="statuspanel" inactive="true" layer="true">
+    <hbox id="statuspanel" inactive="true" renderroot="content">
       <hbox id="statuspanel-inner">
         <label id="statuspanel-label"
                role="status"
                aria-live="off"
                flex="1"
                crop="end"/>
       </hbox>
     </hbox>
@@ -1330,17 +1330,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                    persist="width">
         <searchbar id="searchbar" flex="1"/>
       </toolbaritem>
     </toolbarpalette>
   </toolbox>
 
   <hbox id="fullscr-toggler" hidden="true"/>
 
-  <deck id="content-deck" flex="1">
+  <deck id="content-deck" flex="1" 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"/>
@@ -1366,17 +1366,17 @@ xmlns="http://www.w3.org/1999/xhtml"
                      flex="1" class="plain" selectedIndex="0"/>
         </tabbox>
       </vbox>
       <vbox id="browser-border-end" hidden="true" layer="true"/>
     </hbox>
 #include ../../components/customizableui/content/customizeMode.inc.xul
   </deck>
 
-  <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true">
+  <html:div id="fullscreen-warning" class="pointerlockfswarning" hidden="true" 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>
@@ -1385,24 +1385,23 @@ xmlns="http://www.w3.org/1999/xhtml"
 #ifdef XP_MACOSX
             &exitDOMFullscreenMac.button;
 #else
             &exitDOMFullscreen.button;
 #endif
     </html:button>
   </html:div>
 
-  <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true">
+  <html:div id="pointerlock-warning" class="pointerlockfswarning" hidden="true" 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/PBrowser.ipdl
+++ b/dom/ipc/PBrowser.ipdl
@@ -35,16 +35,17 @@ using class IPC::Principal from "mozilla
 using moveonly mozilla::gfx::PaintFragment from "mozilla/gfx/CrossProcessPaint.h";
 using mozilla::gfx::Matrix from "mozilla/gfx/Matrix.h";
 using mozilla::gfx::SurfaceFormat from "mozilla/gfx/Types.h";
 using mozilla::LayoutDeviceIntPoint from "Units.h";
 using mozilla::LayoutDevicePoint from "Units.h";
 using mozilla::ScreenIntPoint from "Units.h";
 using ScreenIntSize from "Units.h";
 using struct mozilla::layers::ScrollableLayerGuid from "mozilla/layers/ScrollableLayerGuid.h";
+using struct mozilla::layers::APZCGuid from "mozilla/layers/APZTypes.h";
 using struct mozilla::layers::ZoomConstraints from "mozilla/layers/ZoomConstraints.h";
 using mozilla::layers::LayersId from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::LayersObserverEpoch from "mozilla/layers/LayersTypes.h";
 using mozilla::layers::MaybeZoomConstraints from "mozilla/layers/ZoomConstraints.h";
 using mozilla::layers::GeckoContentController::TapType from "mozilla/layers/GeckoContentController.h";
 using ScrollableLayerGuid::ViewID from "mozilla/layers/ScrollableLayerGuid.h";
 using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
 using mozilla::WindowsHandle from "ipc/IPCMessageUtils.h";
--- a/dom/ipc/TabChild.cpp
+++ b/dom/ipc/TabChild.cpp
@@ -26,16 +26,17 @@
 #include "mozilla/dom/LoadURIOptionsBinding.h"
 #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/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"
@@ -466,19 +467,18 @@ TabChild::Observe(nsISupports* aSubject,
 void TabChild::ContentReceivedInputBlock(const ScrollableLayerGuid& aGuid,
                                          uint64_t aInputBlockId,
                                          bool aPreventDefault) const {
   if (mApzcTreeManager) {
     mApzcTreeManager->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
   }
 }
 
-void TabChild::SetTargetAPZC(
-    uint64_t aInputBlockId,
-    const nsTArray<ScrollableLayerGuid>& aTargets) const {
+void TabChild::SetTargetAPZC(uint64_t aInputBlockId,
+                             const nsTArray<APZCGuid>& aTargets) const {
   if (mApzcTreeManager) {
     mApzcTreeManager->SetTargetAPZC(aInputBlockId, aTargets);
   }
 }
 
 void TabChild::SetAllowedTouchBehavior(
     uint64_t aInputBlockId,
     const nsTArray<TouchBehaviorFlags>& aTargets) const {
@@ -489,18 +489,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);
+  APZCGuid guid = APZCGuid(mLayersId, aPresShellId, aViewId,
+                           gfxUtils::GetContentRenderRoot());
 
   mApzcTreeManager->UpdateZoomConstraints(guid, aConstraints);
   return true;
 }
 
 nsresult TabChild::Init(mozIDOMWindowProxy* aParent) {
   if (!mTabGroup) {
     mTabGroup = TabGroup::GetFromActor(this);
@@ -1228,17 +1228,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);
+    APZCGuid 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,
@@ -1304,28 +1305,29 @@ 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);
+  APZCGuid 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);
+  APZCGuid 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
@@ -568,17 +568,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<APZCGuid>& aTargets) const;
   MOZ_CAN_RUN_SCRIPT
   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
   mozilla::ipc::IPCResult RecvNormalPriorityHandleTap(
--- a/dom/ipc/TabParent.cpp
+++ b/dom/ipc/TabParent.cpp
@@ -84,16 +84,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"
@@ -3250,17 +3251,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};
+      APZCGuid 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();
@@ -3279,17 +3281,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};
+      APZCGuid 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
@@ -20,23 +20,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);
 }
 
@@ -128,20 +130,22 @@ void CompositorAnimationStorage::SetAnim
 
 AnimationArray* CompositorAnimationStorage::GetAnimations(
     const uint64_t& aId) const {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   return mAnimations.Get(aId);
 }
 
 void CompositorAnimationStorage::SetAnimations(uint64_t aId,
-                                               const AnimationArray& aValue) {
+                                               const AnimationArray& aValue,
+                                               wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AnimationArray* value = new AnimationArray(aValue);
   mAnimations.Put(aId, value);
+  mAnimationRenderRoots.Put(aId, aRenderRoot);
 }
 
 AnimationHelper::SampleResult AnimationHelper::SampleAnimationForEachNode(
     TimeStamp aPreviousFrameTime, TimeStamp aCurrentFrameTime,
     AnimationArray& aAnimations, InfallibleTArray<AnimData>& aAnimationData,
     RefPtr<RawServoAnimationValue>& aAnimationValue,
     const AnimatedValue* aPreviousValue) {
   MOZ_ASSERT(!aAnimations.IsEmpty(), "Should be called with animations");
--- 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 layers {
 class Animation;
 
@@ -92,16 +93,18 @@ struct AnimatedValue {
 // Each layer which has animations gets a CompositorAnimationsId key, and reuses
 // that key during its lifetime. Likewise, in layers-free webrender, a display
 // item that is animated (e.g. nsDisplayTransform) gets a CompositorAnimationsId
 // key and reuses that key (it persists the key via the frame user-data
 // mechanism).
 class CompositorAnimationStorage final {
   typedef nsClassHashtable<nsUint64HashKey, AnimatedValue> AnimatedValueTable;
   typedef nsClassHashtable<nsUint64HashKey, AnimationArray> AnimationsTable;
+  typedef nsDataHashtable<nsUint64HashKey, wr::RenderRoot>
+      AnimationsRenderRootsTable;
 
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(CompositorAnimationStorage)
  public:
   /**
    * Set the animation transform based on the unique id and also
    * set up |aFrameTransform| and |aData| for OMTA testing
    */
   void SetAnimatedValue(uint64_t aId, gfx::Matrix4x4&& aTransformInDevSpace,
@@ -137,44 +140,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
    */
   AnimationArray* 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/LayersLogging.cpp
+++ b/gfx/layers/LayersLogging.cpp
@@ -253,16 +253,24 @@ 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 APZCGuid& 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
@@ -176,16 +176,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 APZCGuid& 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
@@ -192,17 +192,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);
   }
 
@@ -212,20 +213,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,
+                                     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,150 @@
+/* -*- 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 {
+
+struct APZNodeId {
+  LayersId mLayersId;
+  wr::RenderRoot mRenderRoot;
+
+  APZNodeId() = default;
+
+  explicit APZNodeId(LayersId aLayersId)
+    : mLayersId(aLayersId),
+      mRenderRoot(wr::RenderRoot::Default) {}
+
+  APZNodeId(LayersId aLayersId, wr::RenderRoot aRenderRoot)
+    : mLayersId(aLayersId),
+      mRenderRoot(aRenderRoot) {}
+
+  APZNodeId(uint64_t aLayersId, wr::RenderRoot aRenderRoot)
+    : mLayersId(LayersId{aLayersId}),
+      mRenderRoot(aRenderRoot) {}
+
+  APZNodeId(wr::PipelineId aLayersId, wr::DocumentId aRenderRootId)
+    : mLayersId(AsLayersId(aLayersId)),
+      mRenderRoot(RenderRootFromId(aRenderRootId)) {}
+
+  bool operator==(const APZNodeId& aOther) const {
+    return mRenderRoot == aOther.mRenderRoot &&
+           mLayersId == aOther.mLayersId;
+  }
+
+  bool operator!=(const APZNodeId& 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<APZNodeId, ValueType, APZNodeId::HashFn> myMap;
+  struct HashFn {
+    std::size_t operator()(const APZNodeId& aKey) const {
+      return HashGeneric((uint64_t)aKey.mLayersId, (uint8_t)aKey.mRenderRoot);
+    }
+  };
+};
+
+struct APZCGuid {
+  ScrollableLayerGuid mScrollableLayerGuid;
+  wr::RenderRoot mRenderRoot;
+
+  APZCGuid()
+      : mRenderRoot(wr::RenderRoot::Default) {}
+
+  APZCGuid(LayersId aLayersId, uint32_t aPresShellId,
+           ScrollableLayerGuid::ViewID aScrollId, wr::RenderRoot aRenderRoot)
+      : mScrollableLayerGuid(aLayersId, aPresShellId, aScrollId),
+        mRenderRoot(aRenderRoot) {}
+
+  APZCGuid(const ScrollableLayerGuid& other,
+           wr::RenderRoot aRenderRoot)
+      : mScrollableLayerGuid(other),
+        mRenderRoot(aRenderRoot) {}
+
+  APZCGuid(const APZCGuid& other)
+      : mScrollableLayerGuid(other.mScrollableLayerGuid),
+        mRenderRoot(other.mRenderRoot) {}
+
+  ~APZCGuid() {}
+
+  APZNodeId GetAPZNodeId() const {
+    return APZNodeId(mScrollableLayerGuid.mLayersId, mRenderRoot);
+  }
+
+  bool operator==(const APZCGuid& other) const {
+    return mScrollableLayerGuid == other.mScrollableLayerGuid &&
+           mRenderRoot == other.mRenderRoot;
+  }
+
+  bool operator!=(const APZCGuid& other) const {
+    return !(*this == other);
+  }
+
+  bool operator<(const APZCGuid& other) const {
+    if (mScrollableLayerGuid < other.mScrollableLayerGuid) {
+      return true;
+    }
+    if (mScrollableLayerGuid == other.mScrollableLayerGuid) {
+      return mRenderRoot < other.mRenderRoot;
+    }
+    return false;
+  }
+
+  // Helper structs to use as hash/equality functions in std::unordered_map.
+  // e.g. std::unordered_map<APZCGuid,
+  //                    ValueType,
+  //                    APZCGuid::HashFn> myMap;
+  // std::unordered_map<APZCGuid,
+  //                    ValueType,
+  //                    APZCGuid::HashIgnoringPresShellFn,
+  //                    APZCGuid::EqualIgnoringPresShellFn> myMap;
+
+  struct HashFn {
+    std::size_t operator()(const APZCGuid& aGuid) const {
+      return AddToHash(ScrollableLayerGuid::HashFn{}(aGuid.mScrollableLayerGuid),
+                       uint8_t(aGuid.mRenderRoot));
+    }
+  };
+
+  struct HashIgnoringPresShellFn {
+    std::size_t operator()(const APZCGuid& aGuid) const {
+      return AddToHash(ScrollableLayerGuid::HashIgnoringPresShellFn{}(aGuid.mScrollableLayerGuid),
+                       uint8_t(aGuid.mRenderRoot));
+    }
+  };
+
+  struct EqualIgnoringPresShellFn {
+    bool operator()(const APZCGuid& lhs,
+                    const APZCGuid& rhs) const {
+      return ScrollableLayerGuid::EqualIgnoringPresShellFn{}(lhs.mScrollableLayerGuid,
+                                                             rhs.mScrollableLayerGuid) &&
+             lhs.mRenderRoot == rhs.mRenderRoot;
+    }
+  };
+};
+
+template <int LogLevel>
+gfx::Log<LogLevel>& operator<<(gfx::Log<LogLevel>& log,
+                               const APZCGuid& 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,59 +55,59 @@ 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,
+                        APZNodeId aOriginatingLayersId,
                         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,
+                                    APZNodeId aOriginatingLayersId,
                                     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,
+                           APZNodeId aOriginatingLayersId,
                            ScrollUpdatesMap&& aUpdates,
                            uint32_t aPaintSequenceNumber);
 
-  void NotifyLayerTreeAdopted(LayersId aLayersId,
+  void NotifyLayerTreeAdopted(APZNodeId aLayersId,
                               const RefPtr<APZUpdater>& aOldUpdater);
-  void NotifyLayerTreeRemoved(LayersId aLayersId);
+  void NotifyLayerTreeRemoved(APZNodeId aLayersId);
 
-  bool GetAPZTestData(LayersId aLayersId, APZTestData* aOutData);
+  bool GetAPZTestData(APZNodeId aLayersId, APZTestData* aOutData);
 
-  void SetTestAsyncScrollOffset(LayersId aLayersId,
+  void SetTestAsyncScrollOffset(APZNodeId aLayersId,
                                 const ScrollableLayerGuid::ViewID& aScrollId,
                                 const CSSPoint& aOffset);
-  void SetTestAsyncZoom(LayersId aLayersId,
+  void SetTestAsyncZoom(APZNodeId aLayersId,
                         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(APZNodeId aLayersId) 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;
 
@@ -118,17 +115,17 @@ class APZUpdater {
    * 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.
    */
-  void RunOnUpdaterThread(LayersId aLayersId, already_AddRefed<Runnable> aTask);
+  void RunOnUpdaterThread(APZNodeId aLayersId, 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
@@ -136,17 +133,17 @@ class APZUpdater {
    * 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.
    */
-  void RunOnControllerThread(LayersId aLayersId,
+  void RunOnControllerThread(APZNodeId aLayersId,
                              already_AddRefed<Runnable> aTask);
 
  protected:
   virtual ~APZUpdater();
 
   bool UsingWebRenderUpdaterThread() const;
   static already_AddRefed<APZUpdater> GetUpdater(
       const wr::WrWindowId& aWindowId);
@@ -154,17 +151,17 @@ class APZUpdater {
   void ProcessQueue();
 
  private:
   RefPtr<APZCTreeManager> mApz;
   bool mIsUsingWebRender;
 
   // Map from layers id to WebRenderScrollData. This can only be touched on
   // the updater thread.
-  std::unordered_map<LayersId, WebRenderScrollData, LayersId::HashFn>
+  std::unordered_map<APZNodeId, WebRenderScrollData, APZNodeId::HashFn>
       mScrollData;
 
   // Stores epoch state for a particular layers id. This structure is only
   // accessed on the updater thread.
   struct EpochState {
     // The epoch for the most recent scroll data sent from the content side.
     wr::Epoch mRequired;
     // The epoch for the most recent scene built and swapped in on the WR side.
@@ -185,17 +182,17 @@ class APZUpdater {
     // 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".
     bool IsBlocked() const;
   };
 
   // Map from layers 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<APZNodeId, EpochState, APZNodeId::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;
@@ -210,17 +207,17 @@ class APZUpdater {
   // 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.
   struct QueuedTask {
-    LayersId mLayersId;
+    APZNodeId mLayersId;
     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
--- a/gfx/layers/apz/public/CompositorController.h
+++ b/gfx/layers/apz/public/CompositorController.h
@@ -3,25 +3,26 @@
 /* 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/webrender/WebRenderTypes.h"
 
 namespace mozilla {
 namespace layers {
 
 class CompositorController {
  public:
   NS_INLINE_DECL_PURE_VIRTUAL_REFCOUNTING
 
-  virtual void ScheduleRenderOnCompositorThread() = 0;
+  virtual void ScheduleRenderOnCompositorThread(wr::RenderRoot aRenderRootid = wr::kRenderRootUnknown) = 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
@@ -49,17 +49,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 APZCGuid& 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
@@ -75,48 +75,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<APZCGuid>& 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 APZCGuid& 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 APZCGuid& aGuid,
                                   const AsyncDragMetrics& aDragMetrics) = 0;
 
-  virtual bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
+  virtual bool StartAutoscroll(const APZCGuid& aGuid,
                                const ScreenPoint& aAnchorLocation) = 0;
 
-  virtual void StopAutoscroll(const ScrollableLayerGuid& aGuid) = 0;
+  virtual void StopAutoscroll(const APZCGuid& 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
@@ -100,23 +100,23 @@ struct APZCTreeManager::TreeBuildingStat
 
   // 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;
 
   // This map is populated as we place APZCs into the new tree. Its purpose is
   // to facilitate re-using the same APZC for different layers that scroll
-  // together (and thus have the same ScrollableLayerGuid). The presShellId
+  // together (and thus have the same APZCGuid). The presShellId
   // doesn't matter for this purpose, and we move the map to the APZCTreeManager
   // after we're done building, so it's useful to have the presshell-ignoring
   // map for that.
-  std::unordered_map<ScrollableLayerGuid, RefPtr<AsyncPanZoomController>,
-                     ScrollableLayerGuid::HashIgnoringPresShellFn,
-                     ScrollableLayerGuid::EqualIgnoringPresShellFn>
+  std::unordered_map<APZCGuid, RefPtr<AsyncPanZoomController>,
+                     APZCGuid::HashIgnoringPresShellFn,
+                     APZCGuid::EqualIgnoringPresShellFn>
       mApzcMap;
 
   // This is populated with all the HitTestingTreeNodes that are scroll thumbs
   // and have a scrollthumb animation id (which indicates that they need to be
   // sampled for WebRender on the sampler thread).
   std::vector<HitTestingTreeNode*> mScrollThumbs;
   // This is populated with all the scroll target nodes. We use in conjunction
   // with mScrollThumbs to build APZCTreeManager::mScrollThumbInfo.
@@ -287,21 +287,22 @@ void APZCTreeManager::SetSampler(APZSamp
 
 void APZCTreeManager::SetUpdater(APZUpdater* aUpdater) {
   // We're either setting the updater or clearing it
   MOZ_ASSERT((mUpdater == nullptr) != (aUpdater == nullptr));
   mUpdater = aUpdater;
 }
 
 void APZCTreeManager::NotifyLayerTreeAdopted(
-    LayersId aLayersId, const RefPtr<APZCTreeManager>& aOldApzcTreeManager) {
+    LayersId aLayersId, const RefPtr<APZCTreeManager>& aOldApzcTreeManager,
+    wr::RenderRoot aRenderRoot) {
   AssertOnUpdaterThread();
 
   if (aOldApzcTreeManager) {
-    aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId);
+    aOldApzcTreeManager->mFocusState.RemoveFocusTarget(aLayersId, aRenderRoot);
     // While we could move the focus target information from the old APZC tree
     // manager into this one, it's safer to not do that, as we'll probably have
     // that information repopulated soon anyway (on the next layers update).
   }
 
   UniquePtr<APZTestData> adoptedData;
   if (aOldApzcTreeManager) {
     MutexAutoLock lock(aOldApzcTreeManager->mTestDataLock);
@@ -312,31 +313,33 @@ void APZCTreeManager::NotifyLayerTreeAdo
     }
   }
   if (adoptedData) {
     MutexAutoLock lock(mTestDataLock);
     mTestData[aLayersId] = std::move(adoptedData);
   }
 }
 
-void APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId) {
+void APZCTreeManager::NotifyLayerTreeRemoved(LayersId aLayersId,
+                                             wr::RenderRoot aRenderRoot) {
   AssertOnUpdaterThread();
 
-  mFocusState.RemoveFocusTarget(aLayersId);
+  mFocusState.RemoveFocusTarget(aLayersId, aRenderRoot);
 
   {  // 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();
@@ -389,30 +392,31 @@ APZCTreeManager::UpdateHitTestingTreeImp
   mRootNode = nullptr;
 
   if (aRoot) {
     std::stack<gfx::TreeAutoIndent> 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,
         [&](ScrollNode aLayerMetrics) {
           mApzcTreeLog << aLayerMetrics.Name() << '\t';
 
           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 non-zero 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
@@ -450,16 +454,21 @@ APZCTreeManager::UpdateHitTestingTreeImp
           parent = node;
           next = nullptr;
 
           // Update the layersId 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(mApzcTreeLog));
           state.mParentHasPerspective.push(
               aLayerMetrics.TransformIsPerspective());
         },
         [&](ScrollNode aLayerMetrics) {
           next = parent;
           parent = parent->GetParent();
           layersId = next->GetLayersId();
@@ -545,24 +554,26 @@ APZCTreeManager::UpdateHitTestingTreeImp
   if (mRootNode) {
     mRootNode->Dump("  ");
   }
 #endif
 }
 
 void APZCTreeManager::UpdateFocusState(LayersId aRootLayerTreeId,
                                        LayersId aOriginatingLayersId,
-                                       const FocusTarget& aFocusTarget) {
+                                       const FocusTarget& aFocusTarget,
+                                       wr::RenderRoot aRenderRoot) {
   AssertOnUpdaterThread();
 
   if (!gfxPrefs::APZKeyboardEnabled()) {
     return;
   }
 
-  mFocusState.Update(aRootLayerTreeId, aOriginatingLayersId, aFocusTarget);
+  mFocusState.Update(aRootLayerTreeId, aOriginatingLayersId, aFocusTarget,
+                     aRenderRoot);
 }
 
 void APZCTreeManager::UpdateHitTestingTree(LayersId aRootLayerTreeId,
                                            Layer* aRoot, bool aIsFirstPaint,
                                            LayersId aOriginatingLayersId,
                                            uint32_t aPaintSequenceNumber) {
   AssertOnUpdaterThread();
 
@@ -577,24 +588,27 @@ void APZCTreeManager::UpdateHitTestingTr
     uint32_t aPaintSequenceNumber) {
   AssertOnUpdaterThread();
 
   UpdateHitTestingTreeImpl(aRootLayerTreeId, aScrollWrapper, aIsFirstPaint,
                            aOriginatingLayersId, aPaintSequenceNumber);
 }
 
 void APZCTreeManager::SampleForWebRender(wr::TransactionWrapper& aTxn,
-                                         const TimeStamp& aSampleTime) {
+                                         const TimeStamp& aSampleTime,
+                                         wr::RenderRoot aRenderRoot) {
   AssertOnSamplerThread();
   MutexAutoLock lock(mMapLock);
 
   nsTArray<wr::WrTransformProperty> transforms;
 
+  auto& apzcMap = mApzcMap;
+
   // Sample async transforms on scrollable layers.
-  for (const auto& mapping : mApzcMap) {
+  for (const auto& mapping : apzcMap) {
     AsyncPanZoomController* apzc = mapping.second;
 
     // 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)
@@ -618,17 +632,17 @@ void APZCTreeManager::SampleForWebRender
                               apzc->GetGuid().mScrollId,
                               wr::ToRoundedLayoutPoint(asyncScrollDelta));
 
     apzc->ReportCheckerboard(aSampleTime);
   }
 
   // Now collect all the async transforms needed for the scrollthumbs.
   for (const ScrollThumbInfo& info : mScrollThumbInfo) {
-    auto it = mApzcMap.find(info.mTargetGuid);
+    auto it = mApzcMap.find(APZCGuid(info.mTargetGuid, aRenderRoot));
     if (it == mApzcMap.end()) {
       // 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;
@@ -684,17 +698,17 @@ static ParentLayerIntRegion ComputeClipR
 
   return clipRegion;
 }
 
 template <class ScrollNode>
 void APZCTreeManager::PrintAPZCInfo(const ScrollNode& aLayer,
                                     const AsyncPanZoomController* apzc) {
   const FrameMetrics& metrics = aLayer.Metrics();
-  mApzcTreeLog << "APZC " << apzc->GetGuid()
+  mApzcTreeLog << "APZC " << apzc->GetAPZCGuid()
                << "\tcb=" << metrics.GetCompositionBounds()
                << "\tsr=" << metrics.GetScrollableRect()
                << (metrics.IsScrollInfoLayer() ? "\tscrollinfo" : "")
                << (apzc->HasScrollgrab() ? "\tscrollgrab" : "") << "\t"
                << aLayer.Metadata().GetContentDescription().get();
 }
 
 void APZCTreeManager::AttachNodeToTree(HitTestingTreeNode* aNode,
@@ -721,30 +735,31 @@ static EventRegions GetEventRegions(cons
     eventRegions.mDispatchToContentHitRegion = eventRegions.mHitRegion;
     return eventRegions;
   }
   return aLayer.GetEventRegions();
 }
 
 already_AddRefed<HitTestingTreeNode> APZCTreeManager::RecycleOrCreateNode(
     const RecursiveMutexAutoLock& aProofOfTreeLock, TreeBuildingState& aState,
-    AsyncPanZoomController* aApzc, LayersId aLayersId) {
+    AsyncPanZoomController* aApzc, LayersId aLayersId,
+    wr::RenderRoot aRenderRoot) {
   // Find a node without an APZC and return it. Note that unless the layer tree
   // actually changes, this loop should generally do an early-return on the
   // first iteration, so it should be cheap in the common case.
   for (int32_t i = aState.mNodesToDestroy.Length() - 1; i >= 0; i--) {
     RefPtr<HitTestingTreeNode> node = aState.mNodesToDestroy[i];
     if (node->IsRecyclable(aProofOfTreeLock)) {
       aState.mNodesToDestroy.RemoveElementAt(i);
       node->RecycleWith(aProofOfTreeLock, aApzc, aLayersId);
       return node.forget();
     }
   }
   RefPtr<HitTestingTreeNode> node =
-      new HitTestingTreeNode(aApzc, false, aLayersId);
+      new HitTestingTreeNode(aApzc, false, aLayersId, aRenderRoot);
   return node.forget();
 }
 
 template <class ScrollNode>
 static EventRegionsOverride GetEventRegionsOverride(HitTestingTreeNode* aParent,
                                                     const ScrollNode& aLayer) {
   // Make it so that if the flag is set on the layer tree, it automatically
   // propagates to all the nodes in the corresponding subtree rooted at that
@@ -756,51 +771,51 @@ static EventRegionsOverride GetEventRegi
     MOZ_ASSERT(aLayer.GetReferentId());
   }
   if (aParent) {
     result |= aParent->GetEventRegionsOverride();
   }
   return result;
 }
 
-void APZCTreeManager::StartScrollbarDrag(const ScrollableLayerGuid& aGuid,
+void APZCTreeManager::StartScrollbarDrag(const APZCGuid& aGuid,
                                          const AsyncDragMetrics& aDragMetrics) {
   APZThreadUtils::AssertOnControllerThread();
 
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   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 APZCGuid& aGuid,
                                       const ScreenPoint& aAnchorLocation) {
   APZThreadUtils::AssertOnControllerThread();
 
   RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid);
   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 APZCGuid& aGuid) {
   APZThreadUtils::AssertOnControllerThread();
 
   if (RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aGuid)) {
     apzc->StopAutoscroll();
   }
 }
 
 void APZCTreeManager::NotifyScrollbarDragInitiated(
@@ -832,17 +847,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
@@ -864,17 +880,18 @@ HitTestingTreeNode* APZCTreeManager::Pre
     aState.mZoomAnimationId = zoomAnimationId;
   }
 
   RefPtr<HitTestingTreeNode> node = nullptr;
   if (!needsApzc) {
     // Note: if layer properties must be propagated to nodes, RecvUpdate in
     // LayerTransactionParent.cpp must ensure that APZ will be notified
     // when those properties change.
-    node = RecycleOrCreateNode(aProofOfTreeLock, aState, nullptr, aLayersId);
+    node = RecycleOrCreateNode(aProofOfTreeLock, aState, nullptr, aLayersId,
+                               aRenderRoot);
     AttachNodeToTree(node, aParent, aNextSibling);
     node->SetHitTestData(GetEventRegions(aLayer), aLayer.GetVisibleRegion(),
                          aLayer.GetTransformTyped(),
                          (!parentHasPerspective && aLayer.GetClipRect())
                              ? Some(ParentLayerIntRegion(*aLayer.GetClipRect()))
                              : Nothing(),
                          GetEventRegionsOverride(aParent, aLayer),
                          aLayer.IsBackfaceHidden());
@@ -888,27 +905,29 @@ HitTestingTreeNode* APZCTreeManager::Pre
   // If we get here, aLayer is a scrollable layer and somebody
   // has registered a GeckoContentController for it, so we need to ensure
   // it has an APZC instance to manage its scrolling.
 
   // aState.mApzcMap allows reusing the exact same APZC instance for different
   // layers with the same FrameMetrics data. This is needed because in some
   // cases content that is supposed to scroll together is split into multiple
   // layers because of e.g. non-scrolling content interleaved in z-index order.
-  ScrollableLayerGuid guid(aLayersId, aMetrics.GetPresShellId(),
-                           aMetrics.GetScrollId());
+  APZCGuid guid(aLayersId, aMetrics.GetPresShellId(), aMetrics.GetScrollId(),
+                aRenderRoot);
   auto insertResult = aState.mApzcMap.insert(
       std::make_pair(guid, static_cast<AsyncPanZoomController*>(nullptr)));
   if (!insertResult.second) {
     apzc = insertResult.first->second;
     PrintAPZCInfo(aLayer, apzc);
   }
   APZCTM_LOG("Found APZC %p for layer %p with identifiers %" PRIx64 " %" PRId64
-             "\n",
-             apzc, aLayer.GetLayer(), uint64_t(guid.mLayersId), guid.mScrollId);
+             " %d\n",
+             apzc, aLayer.GetLayer(),
+             uint64_t(guid.mScrollableLayerGuid.mLayersId),
+             guid.mScrollableLayerGuid.mScrollId, (int)guid.mRenderRoot);
 
   // If we haven't encountered a layer already with the same metrics, then we
   // need to do the full reuse-or-make-an-APZC algorithm, which is contained
   // inside the block below.
   if (apzc == nullptr) {
     apzc = aLayer.GetApzc();
 
     // If the content represented by the scrollable layer has changed (which may
@@ -918,17 +937,17 @@ HitTestingTreeNode* APZCTreeManager::Pre
     // different APZCTreeManager. In these cases we don't want to reuse the same
     // APZC, so null it out so we run through the code to find another one or
     // create one.
     if (apzc && (!apzc->Matches(guid) || !apzc->HasTreeManager(this))) {
       apzc = nullptr;
     }
 
     // See if we can find an APZC from the previous tree that matches the
-    // ScrollableLayerGuid from this layer. If there is one, then we know that
+    // APZCGuid from this layer. If there is one, then we know that
     // the layout of the page changed causing the layer tree to be rebuilt, but
     // the underlying content for the APZC is still there somewhere. Therefore,
     // we want to find the APZC instance and continue using it here.
     //
     // We particularly want to find the primary-holder node from the previous
     // tree that matches, because we don't want that node to get destroyed. If
     // it does get destroyed, then the APZC will get destroyed along with it by
     // definition, but we want to keep that APZC around in the new tree.
@@ -949,26 +968,26 @@ 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);
-      node = new HitTestingTreeNode(apzc, true, aLayersId);
+      node = new HitTestingTreeNode(apzc, true, aLayersId, aRenderRoot);
     } else {
       // If we are re-using a node for this layer clear the tree pointers
       // so that it doesn't continue pointing to nodes that might no longer
       // be in the tree. These pointers will get reset properly as we continue
       // building the tree. Also remove it from the set of nodes that are going
       // to be destroyed, because it's going to remain active.
       aState.mNodesToDestroy.RemoveElement(node);
       node->SetPrevSibling(nullptr);
@@ -1032,17 +1051,17 @@ HitTestingTreeNode* APZCTreeManager::Pre
           apzc->GetCurrentAsyncScrollOffset(
               AsyncPanZoomController::eForHitTesting));
       aState.mPaintLogger.LogTestData(aMetrics.GetScrollId(),
                                       "hasAsyncKeyScrolled",
                                       apzc->TestHasAsyncKeyScrolled());
     }
 
     if (newApzc) {
-      auto it = mZoomConstraints.find(guid);
+      auto it = mZoomConstraints.find(guid.mScrollableLayerGuid);
       if (it != mZoomConstraints.end()) {
         // We have a zoomconstraints for this guid, apply it.
         apzc->UpdateZoomConstraints(it->second);
       } else if (!apzc->HasNoParentWithSameLayersId()) {
         // This is a sub-APZC, so inherit the zoom constraints from its parent.
         // This ensures that if e.g. user-scalable=no was specified, none of the
         // APZCs for that subtree allow double-tap to zoom.
         apzc->UpdateZoomConstraints(apzc->GetParent()->GetZoomConstraints());
@@ -1053,17 +1072,18 @@ HitTestingTreeNode* APZCTreeManager::Pre
 
     // Add a guid -> APZC mapping for the newly created APZC.
     insertResult.first->second = apzc;
   } else {
     // We already built an APZC earlier in this tree walk, but we have another
     // layer now that will also be using that APZC. The hit-test region on the
     // APZC needs to be updated to deal with the new layer's hit region.
 
-    node = RecycleOrCreateNode(aProofOfTreeLock, aState, apzc, aLayersId);
+    node = RecycleOrCreateNode(aProofOfTreeLock, aState, apzc, aLayersId,
+                               aRenderRoot);
     AttachNodeToTree(node, aParent, aNextSibling);
 
     // Even though different layers associated with a given APZC may be at
     // different levels in the layer tree (e.g. one being an uncle of another),
     // we require from Layout that the CSS transforms up to their common
     // ancestor be roughly the same. There are cases in which the transforms
     // are not exactly the same, for example if the parent is container layer
     // for an opacity, and this container layer has a resolution-induced scale
@@ -1513,17 +1533,18 @@ nsEventStatus APZCTreeManager::ReceiveIn
       // doesn't have a layerized scroll frame. In any case we need to dispatch
       // to content.
       if (!targetGuid) {
         APZ_KEY_LOG("Skipping key input with no current focus target\n");
         return result;
       }
 
       RefPtr<AsyncPanZoomController> targetApzc =
-          GetTargetAPZC(targetGuid->mLayersId, targetGuid->mScrollId);
+          GetTargetAPZC(targetGuid->mLayersId, targetGuid->mScrollId,
+                        mFocusState.GetRenderRoot());
 
       if (!targetApzc) {
         APZ_KEY_LOG("Skipping key input with focus target but no APZC\n");
         return result;
       }
 
       // Attach the keyboard scroll action to the input event for processing
       // by the input queue.
@@ -2026,18 +2047,18 @@ void APZCTreeManager::ProcessDynamicTool
 }
 
 void APZCTreeManager::SetKeyboardMap(const KeyboardMap& aKeyboardMap) {
   APZThreadUtils::AssertOnControllerThread();
 
   mKeyboardMap = aKeyboardMap;
 }
 
-void APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid,
-                                 const CSSRect& aRect, const uint32_t aFlags) {
+void APZCTreeManager::ZoomToRect(const APZCGuid& 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);
   if (apzc) {
     apzc->ZoomToRect(aRect, aFlags);
@@ -2046,44 +2067,43 @@ void APZCTreeManager::ZoomToRect(const S
 
 void APZCTreeManager::ContentReceivedInputBlock(uint64_t aInputBlockId,
                                                 bool aPreventDefault) {
   APZThreadUtils::AssertOnControllerThread();
 
   mInputQueue->ContentReceivedInputBlock(aInputBlockId, aPreventDefault);
 }
 
-void APZCTreeManager::SetTargetAPZC(
-    uint64_t aInputBlockId, const nsTArray<ScrollableLayerGuid>& aTargets) {
+void APZCTreeManager::SetTargetAPZC(uint64_t aInputBlockId,
+                                    const nsTArray<APZCGuid>& aTargets) {
   APZThreadUtils::AssertOnControllerThread();
 
   RefPtr<AsyncPanZoomController> target = nullptr;
   if (aTargets.Length() > 0) {
     target = GetTargetAPZC(aTargets[0]);
   }
   for (size_t i = 1; i < aTargets.Length(); i++) {
     RefPtr<AsyncPanZoomController> apzc = GetTargetAPZC(aTargets[i]);
     target = GetMultitouchTarget(target, apzc);
   }
   mInputQueue->SetConfirmedTargetApzc(aInputBlockId, target);
 }
 
 void APZCTreeManager::UpdateZoomConstraints(
-    const ScrollableLayerGuid& aGuid,
-    const Maybe<ZoomConstraints>& aConstraints) {
+    const APZCGuid& 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>>(
+        aGuid.GetAPZNodeId(),
+        NewRunnableMethod<APZCGuid, Maybe<ZoomConstraints>>(
             "APZCTreeManager::UpdateZoomConstraints", this,
             &APZCTreeManager::UpdateZoomConstraints, aGuid, aConstraints));
     return;
   }
 
   AssertOnUpdaterThread();
 
   RecursiveMutexAutoLock lock(mTreeLock);
@@ -2091,20 +2111,20 @@ void APZCTreeManager::UpdateZoomConstrai
   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();
+    mZoomConstraints[aGuid.mScrollableLayerGuid] = aConstraints.ref();
   } else {
     APZCTM_LOG("Removing constraints for guid %s\n", Stringify(aGuid).c_str());
-    mZoomConstraints.erase(aGuid);
+    mZoomConstraints.erase(aGuid.mScrollableLayerGuid);
   }
   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.
@@ -2149,23 +2169,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);
   }
 }
 
@@ -2400,49 +2413,54 @@ ParentLayerPoint APZCTreeManager::Dispat
 }
 
 bool APZCTreeManager::HitTestAPZC(const ScreenIntPoint& aPoint) {
   RefPtr<AsyncPanZoomController> target = GetTargetAPZC(aPoint, nullptr);
   return target != nullptr;
 }
 
 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetTargetAPZC(
-    const ScrollableLayerGuid& aGuid) {
+    const APZCGuid& aGuid) {
   RecursiveMutexAutoLock lock(mTreeLock);
   RefPtr<HitTestingTreeNode> node = GetTargetNode(aGuid, nullptr);
   MOZ_ASSERT(!node || node->GetApzc());  // any node returned must have an APZC
   RefPtr<AsyncPanZoomController> apzc = node ? node->GetApzc() : nullptr;
   return apzc.forget();
 }
 
-static bool GuidComparatorIgnoringPresShell(const ScrollableLayerGuid& aOne,
-                                            const ScrollableLayerGuid& aTwo) {
-  return aOne.mLayersId == aTwo.mLayersId && aOne.mScrollId == aTwo.mScrollId;
+static bool GuidComparatorIgnoringPresShell(const APZCGuid& aOne,
+                                            const APZCGuid& aTwo) {
+  return aOne.mScrollableLayerGuid.mLayersId ==
+             aTwo.mScrollableLayerGuid.mLayersId &&
+         aOne.mScrollableLayerGuid.mScrollId ==
+             aTwo.mScrollableLayerGuid.mScrollId &&
+         aOne.mRenderRoot == aTwo.mRenderRoot;
 }
 
 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetTargetAPZC(
-    const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId) {
+    const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+    wr::RenderRoot aRenderRoot) {
   MutexAutoLock lock(mMapLock);
-  ScrollableLayerGuid guid(aLayersId, 0, aScrollId);
+  APZCGuid guid(aLayersId, 0, aScrollId, aRenderRoot);
   auto it = mApzcMap.find(guid);
   RefPtr<AsyncPanZoomController> apzc =
       (it != mApzcMap.end() ? it->second : nullptr);
   return apzc.forget();
 }
 
 already_AddRefed<HitTestingTreeNode> APZCTreeManager::GetTargetNode(
-    const ScrollableLayerGuid& aGuid, GuidComparator aComparator) const {
+    const APZCGuid& aGuid, GuidComparator aComparator) const {
   mTreeLock.AssertCurrentThreadIn();
   RefPtr<HitTestingTreeNode> target =
       DepthFirstSearchPostOrder<ReverseIterator>(
           mRootNode.get(), [&aGuid, &aComparator](HitTestingTreeNode* node) {
             bool matches = false;
             if (node->GetApzc()) {
               if (aComparator) {
-                matches = aComparator(aGuid, node->GetApzc()->GetGuid());
+                matches = aComparator(aGuid, node->GetApzc()->GetAPZCGuid());
               } else {
                 matches = node->GetApzc()->Matches(aGuid);
               }
             }
             return matches;
           });
   return target.forget();
 }
@@ -2473,17 +2491,17 @@ already_AddRefed<AsyncPanZoomController>
 
 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetAPZCAtPointWR(
     const ScreenPoint& aHitTestPoint, CompositorHitTestInfo* aOutHitResult,
     HitTestingTreeNode** aOutScrollbarNode) {
   MOZ_ASSERT(aOutHitResult);
   MOZ_ASSERT(aOutScrollbarNode);
 
   RefPtr<AsyncPanZoomController> result;
-  RefPtr<wr::WebRenderAPI> wr = GetWebRenderAPI();
+  RefPtr<wr::WebRenderAPI> wr = GetWebRenderAPIAtPoint(aHitTestPoint);
   if (!wr) {
     // If WebRender isn't running, fall back to the root APZC.
     // This is mostly for the benefit of GTests which do not
     // run a WebRender instance, but gracefully falling back
     // here allows those tests which are not specifically
     // testing the hit-test algorithm to still work.
     result = FindRootApzcForLayersId(mRootLayersId);
     *aOutHitResult = CompositorHitTestFlags::eVisibleToHitTest;
@@ -2495,17 +2513,18 @@ already_AddRefed<AsyncPanZoomController>
   gfx::CompositorHitTestInfo hitInfo;
   bool hitSomething = wr->HitTest(wr::ToWorldPoint(aHitTestPoint), pipelineId,
                                   scrollId, hitInfo);
   if (!hitSomething) {
     return result.forget();
   }
 
   LayersId layersId = wr::AsLayersId(pipelineId);
-  result = GetTargetAPZC(layersId, scrollId);
+  wr::RenderRoot renderRoot = wr->GetRenderRoot();
+  result = GetTargetAPZC(layersId, scrollId, renderRoot);
   if (!result) {
     // It falls back to the root
     MOZ_ASSERT(scrollId == ScrollableLayerGuid::NULL_SCROLL_ID);
     result = FindRootApzcForLayersId(layersId);
     MOZ_ASSERT(result);
   }
 
   const bool isScrollbar =
@@ -2515,16 +2534,17 @@ already_AddRefed<AsyncPanZoomController>
   const ScrollDirection direction =
       hitInfo.contains(gfx::CompositorHitTestFlags::eScrollbarVertical)
           ? ScrollDirection::eVertical
           : ScrollDirection::eHorizontal;
   if (isScrollbar || isScrollbarThumb) {
     *aOutScrollbarNode = BreadthFirstSearch<ReverseIterator>(
         mRootNode.get(), [&](HitTestingTreeNode* aNode) {
           return (aNode->GetLayersId() == layersId) &&
+                 (aNode->GetRenderRoot() == renderRoot) &&
                  (aNode->IsScrollbarNode() == isScrollbar) &&
                  (aNode->IsScrollThumbNode() == isScrollbarThumb) &&
                  (aNode->GetScrollbarDirection() == direction) &&
                  (aNode->GetScrollTargetId() == scrollId);
         });
   }
 
   *aOutHitResult = hitInfo;
@@ -2564,18 +2584,19 @@ APZCTreeManager::BuildOverscrollHandoffC
       }
       apzc = apzc->GetParent();
       continue;
     }
 
     // Guard against a possible infinite-loop condition. If we hit this, the
     // layout code that generates the handoff parents did something wrong.
     MOZ_ASSERT(apzc->GetScrollHandoffParentId() != apzc->GetGuid().mScrollId);
-    RefPtr<AsyncPanZoomController> scrollParent = GetTargetAPZC(
-        apzc->GetGuid().mLayersId, apzc->GetScrollHandoffParentId());
+    RefPtr<AsyncPanZoomController> scrollParent =
+        GetTargetAPZC(apzc->GetGuid().mLayersId,
+                      apzc->GetScrollHandoffParentId(), apzc->GetRenderRoot());
     apzc = scrollParent.get();
   }
 
   // Now adjust the chain to account for scroll grabbing. Sorting is a bit
   // of an overkill here, but scroll grabbing will likely be generalized
   // to scroll priorities, so we might as well do it this way.
   result->SortByScrollPriority();
 
@@ -2590,45 +2611,46 @@ APZCTreeManager::BuildOverscrollHandoffC
 
 void APZCTreeManager::SetLongTapEnabled(bool aLongTapEnabled) {
   APZThreadUtils::AssertOnControllerThread();
   GestureEventListener::SetLongTapEnabled(aLongTapEnabled);
 }
 
 void APZCTreeManager::FindScrollThumbNode(
     const AsyncDragMetrics& aDragMetrics,
-    HitTestingTreeNodeAutoLock& aOutThumbNode) {
+    HitTestingTreeNodeAutoLock& aOutThumbNode, wr::RenderRoot aRenderRoot) {
   if (!aDragMetrics.mDirection) {
     // The AsyncDragMetrics has not been initialized yet - there will be
     // no matching node, so don't bother searching the tree.
     return;
   }
 
   RecursiveMutexAutoLock lock(mTreeLock);
 
   RefPtr<HitTestingTreeNode> result = DepthFirstSearch<ReverseIterator>(
-      mRootNode.get(), [&aDragMetrics](HitTestingTreeNode* aNode) {
-        return aNode->MatchesScrollDragMetrics(aDragMetrics);
+      mRootNode.get(), [&aDragMetrics, aRenderRoot](HitTestingTreeNode* aNode) {
+        return aNode->GetRenderRoot() == aRenderRoot &&
+               aNode->MatchesScrollDragMetrics(aDragMetrics);
       });
   if (result) {
     aOutThumbNode.Initialize(lock, result.forget(), mTreeLock);
   }
 }
 
 AsyncPanZoomController* APZCTreeManager::GetTargetApzcForNode(
-    HitTestingTreeNode* aNode) {
+    HitTestingTreeNode* aNode, wr::RenderRoot aRenderRoot) {
   for (const HitTestingTreeNode* n = aNode;
        n && n->GetLayersId() == aNode->GetLayersId(); n = n->GetParent()) {
     if (n->GetApzc()) {
       APZCTM_LOG("Found target %p using ancestor lookup\n", n->GetApzc());
       return n->GetApzc();
     }
     if (n->GetFixedPosTarget() != ScrollableLayerGuid::NULL_SCROLL_ID) {
       RefPtr<AsyncPanZoomController> fpTarget =
-          GetTargetAPZC(n->GetLayersId(), n->GetFixedPosTarget());
+          GetTargetAPZC(n->GetLayersId(), n->GetFixedPosTarget(), aRenderRoot);
       APZCTM_LOG("Found target APZC %p using fixed-pos lookup on %" PRIu64 "\n",
                  fpTarget.get(), n->GetFixedPosTarget());
       return fpTarget.get();
     }
   }
   return nullptr;
 }
 
@@ -2700,25 +2722,26 @@ AsyncPanZoomController* APZCTreeManager:
         if (n->GetScrollbarDirection() == ScrollDirection::eVertical) {
           *aOutHitResult += CompositorHitTestFlags::eScrollbarVertical;
         }
 
         // If we hit a scrollbar, target the APZC for the content scrolled
         // by the scrollbar. (The scrollbar itself doesn't scroll with the
         // scrolled content, so it doesn't carry the scrolled content's
         // scroll metadata).
-        RefPtr<AsyncPanZoomController> scrollTarget =
-            GetTargetAPZC(n->GetLayersId(), n->GetScrollTargetId());
+        RefPtr<AsyncPanZoomController> scrollTarget = GetTargetAPZC(
+            n->GetLayersId(), n->GetScrollTargetId(), wr::RenderRoot::Default);
         if (scrollTarget) {
           return scrollTarget.get();
         }
       }
     }
 
-    AsyncPanZoomController* result = GetTargetApzcForNode(resultNode);
+    AsyncPanZoomController* result =
+        GetTargetApzcForNode(resultNode, wr::RenderRoot::Default);
     if (!result) {
       result = FindRootApzcForLayersId(resultNode->GetLayersId());
       MOZ_ASSERT(result);
       APZCTM_LOG("Found target %p using root lookup\n", result);
     }
     APZCTM_LOG("Successfully matched APZC %p via node %p (hit result 0x%x)\n",
                result, resultNode, aOutHitResult->serialize());
     return result;
@@ -2729,16 +2752,17 @@ AsyncPanZoomController* APZCTreeManager:
 
 AsyncPanZoomController* APZCTreeManager::FindRootApzcForLayersId(
     LayersId aLayersId) const {
   mTreeLock.AssertCurrentThreadIn();
 
   HitTestingTreeNode* resultNode = BreadthFirstSearch<ReverseIterator>(
       mRootNode.get(), [aLayersId](HitTestingTreeNode* aNode) {
         AsyncPanZoomController* apzc = aNode->GetApzc();
+
         return apzc && apzc->GetLayersId() == aLayersId &&
                apzc->IsRootForLayersId();
       });
   return resultNode ? resultNode->GetApzc() : nullptr;
 }
 
 AsyncPanZoomController* APZCTreeManager::FindRootContentApzcForLayersId(
     LayersId aLayersId) const {
@@ -2968,17 +2992,18 @@ ScreenPoint APZCTreeManager::GetCurrentM
 }
 
 already_AddRefed<AsyncPanZoomController> APZCTreeManager::GetMultitouchTarget(
     AsyncPanZoomController* aApzc1, AsyncPanZoomController* aApzc2) const {
   RecursiveMutexAutoLock lock(mTreeLock);
   RefPtr<AsyncPanZoomController> apzc;
   // For now, we only ever want to do pinching on the root-content APZC for
   // a given layers id.
-  if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId()) {
+  if (aApzc1 && aApzc2 && aApzc1->GetLayersId() == aApzc2->GetLayersId() &&
+      aApzc1->GetRenderRoot() == aApzc2->GetRenderRoot()) {
     // If the two APZCs have the same layers id, find the root-content APZC
     // for that layers id. Don't call CommonAncestor() because there may not
     // be a common ancestor for the layers id (e.g. if one APZCs is inside a
     // fixed-position element).
     apzc = FindRootContentApzcForLayersId(aApzc1->GetLayersId());
   } else {
     // Otherwise, find the common ancestor (to reach a common layers id), and
     // get the root-content APZC for that layers id.
@@ -3048,18 +3073,18 @@ LayerToParentLayerMatrix4x4 APZCTreeMana
     // from its APZC.
     return aNode->GetTransform() *
            CompleteAsyncTransform(apzc->GetCurrentAsyncTransformWithOverscroll(
                AsyncPanZoomController::eForHitTesting));
   } else if (aNode->IsScrollThumbNode()) {
     // If the node represents a scrollbar thumb, compute and apply the
     // transformation that will be applied to the thumb in
     // AsyncCompositionManager.
-    ScrollableLayerGuid guid{aNode->GetLayersId(), 0,
-                             aNode->GetScrollTargetId()};
+    APZCGuid guid(aNode->GetLayersId(), 0, aNode->GetScrollTargetId(),
+                  aNode->GetRenderRoot());
     if (RefPtr<HitTestingTreeNode> scrollTargetNode =
             GetTargetNode(guid, &GuidComparatorIgnoringPresShell)) {
       AsyncPanZoomController* scrollTargetApzc = scrollTargetNode->GetApzc();
       MOZ_ASSERT(scrollTargetApzc);
       return scrollTargetApzc->CallWithLastContentPaintMetrics(
           [&](const FrameMetrics& aMetrics) {
             return ComputeTransformForScrollThumb(
                 aNode->GetTransform() * AsyncTransformMatrix(),
@@ -3068,22 +3093,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) {
   RefPtr<GeckoContentController> controller;
--- a/gfx/layers/apz/src/APZCTreeManager.h
+++ b/gfx/layers/apz/src/APZCTreeManager.h
@@ -145,39 +145,41 @@ class APZCTreeManager : public IAPZCTree
    * Notifies this APZCTreeManager that the associated compositor is now
    * responsible for managing another layers id, which got moved over from
    * some other compositor. That other compositor's APZCTreeManager is also
    * provided. This allows APZCTreeManager to transfer any necessary state
    * from the old APZCTreeManager related to that layers id.
    * This function must be called on the updater thread.
    */
   void NotifyLayerTreeAdopted(LayersId aLayersId,
-                              const RefPtr<APZCTreeManager>& aOldTreeManager);
+                              const RefPtr<APZCTreeManager>& aOldTreeManager,
+                              wr::RenderRoot aRenderRoot);
 
   /**
    * Notifies this APZCTreeManager that a layer tree being managed by the
    * associated compositor has been removed/destroyed. Note that this does
    * NOT get called during shutdown situations, when the root layer tree is
    * also getting destroyed.
    * This function must be called on the updater thread.
    */
-  void NotifyLayerTreeRemoved(LayersId aLayersId);
+  void NotifyLayerTreeRemoved(LayersId aLayersId, wr::RenderRoot aRenderRoot);
 
   /**
    * Rebuild the focus state based on the focus target from the layer tree
    * update that just occurred. This must be called on the updater thread.
    *
    * @param aRootLayerTreeId The layer tree ID of the root layer corresponding
    *                         to this APZCTreeManager
    * @param aOriginatingLayersId The layer tree ID of the layer corresponding to
    *                             this layer tree update.
    */
   void UpdateFocusState(LayersId aRootLayerTreeId,
                         LayersId aOriginatingLayersId,
-                        const FocusTarget& aFocusTarget);
+                        const FocusTarget& aFocusTarget,
+                        wr::RenderRoot aRenderRoot);
 
   /**
    * Rebuild the hit-testing tree based on the layer update that just came up.
    * Preserve nodes and APZC instances where possible, but retire those whose
    * layers are no longer in the layer tree.
    *
    * This must be called on the updater thread as it walks the layer tree.
    *
@@ -215,17 +217,18 @@ class APZCTreeManager : public IAPZCTree
    * 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.
    */
   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.
@@ -266,17 +269,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 APZCGuid& 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.
@@ -298,33 +301,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<APZCGuid>& 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 APZCGuid& 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);
 
   /**
@@ -354,17 +352,18 @@ class APZCTreeManager : public IAPZCTree
    */
   float GetDPI() const;
 
   /**
    * Find the hit testing node for the scrollbar thumb that matches these
    * drag metrics. Initializes aOutThumbNode with the node, if there is one.
    */
   void FindScrollThumbNode(const AsyncDragMetrics& aDragMetrics,
-                           HitTestingTreeNodeAutoLock& aOutThumbNode);
+                           HitTestingTreeNodeAutoLock& aOutThumbNode,
+                           wr::RenderRoot aRenderRoot);
 
   /**
    * 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.
@@ -457,23 +456,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 APZCGuid& aGuid,
                           const AsyncDragMetrics& aDragMetrics) override;
 
-  bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
+  bool StartAutoscroll(const APZCGuid& aGuid,
                        const ScreenPoint& aAnchorLocation) override;
 
-  void StopAutoscroll(const ScrollableLayerGuid& aGuid) override;
+  void StopAutoscroll(const APZCGuid& 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);
 
@@ -538,17 +537,20 @@ class APZCTreeManager : public IAPZCTree
   // Assert that the current thread is the sampler thread for this APZCTM.
   void AssertOnSamplerThread();
   // Assert that the current thread is the updater thread for this APZCTM.
   void AssertOnUpdaterThread();
 
   // Returns a pointer to the WebRenderAPI for the root layers id this
   // APZCTreeManager is for. This might be null (for example, if WebRender is
   // not enabled).
-  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI() const;
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPI(
+      wr::RenderRoot aRenderRoot) const;
+  already_AddRefed<wr::WebRenderAPI> GetWebRenderAPIAtPoint(
+      const ScreenPoint& aPoint) const;
 
  protected:
   // Protected destructor, to discourage deletion outside of Release():
   virtual ~APZCTreeManager();
 
   APZSampler* GetSampler() const;
   APZUpdater* GetUpdater() const;
 
@@ -557,17 +559,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
@@ -575,17 +578,18 @@ class APZCTreeManager : public IAPZCTree
      target APZC without worrying about it going away. These are public for
      testing code and generally should not be used by other production code.
   */
   RefPtr<HitTestingTreeNode> GetRootNode() const;
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(
       const ScreenPoint& aPoint, gfx::CompositorHitTestInfo* aOutHitResult,
       HitTestingTreeNodeAutoLock* aOutScrollbarNode = nullptr);
   already_AddRefed<AsyncPanZoomController> GetTargetAPZC(
-      const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId);
+      const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
+      wr::RenderRoot aRenderRoot);
   ScreenToParentLayerMatrix4x4 GetScreenToApzcTransform(
       const AsyncPanZoomController* aApzc) const;
   ParentLayerToScreenMatrix4x4 GetApzcToGeckoTransform(
       const AsyncPanZoomController* aApzc) const;
   ScreenPoint GetCurrentMousePosition() const;
 
   /**
    * Process a movement of the dynamic toolbar by |aDeltaY| over the time
@@ -596,36 +600,35 @@ class APZCTreeManager : public IAPZCTree
    * Note that this function expects "spatial coordinates" (i.e. toolbar
    * moves up --> negative delta).
    */
   void ProcessDynamicToolbarMovement(uint32_t aStartTimestampMs,
                                      uint32_t aEndTimestampMs,
                                      ScreenCoord aDeltaY);
 
  private:
-  typedef bool (*GuidComparator)(const ScrollableLayerGuid&,
-                                 const ScrollableLayerGuid&);
+  typedef bool (*GuidComparator)(const APZCGuid&, const APZCGuid&);
 
   /* Helpers */
   template <class ScrollNode>
   void UpdateHitTestingTreeImpl(LayersId aRootLayerTreeId,
                                 const ScrollNode& aRoot, bool aIsFirstPaint,
                                 LayersId aOriginatingLayersId,
                                 uint32_t aPaintSequenceNumber);
 
   void AttachNodeToTree(HitTestingTreeNode* aNode, HitTestingTreeNode* aParent,
                         HitTestingTreeNode* aNextSibling);
-  already_AddRefed<AsyncPanZoomController> GetTargetAPZC(
-      const ScrollableLayerGuid& aGuid);
+  already_AddRefed<AsyncPanZoomController> GetTargetAPZC(const APZCGuid& aGuid);
   already_AddRefed<HitTestingTreeNode> GetTargetNode(
-      const ScrollableLayerGuid& aGuid, GuidComparator aComparator) const;
+      const APZCGuid& aGuid, GuidComparator aComparator) const;
   HitTestingTreeNode* FindTargetNode(HitTestingTreeNode* aNode,
-                                     const ScrollableLayerGuid& aGuid,
+                                     const APZCGuid& aGuid,
                                      GuidComparator aComparator);
-  AsyncPanZoomController* GetTargetApzcForNode(HitTestingTreeNode* aNode);
+  AsyncPanZoomController* GetTargetApzcForNode(HitTestingTreeNode* aNode,
+                                               wr::RenderRoot aRenderRoot);
   AsyncPanZoomController* GetAPZCAtPoint(
       HitTestingTreeNode* aNode, const ScreenPoint& aHitTestPoint,
       gfx::CompositorHitTestInfo* aOutHitResult,
       HitTestingTreeNode** aOutScrollbarNode);
   already_AddRefed<AsyncPanZoomController> GetAPZCAtPointWR(
       const ScreenPoint& aHitTestPoint,
       gfx::CompositorHitTestInfo* aOutHitResult,
       HitTestingTreeNode** aOutScrollbarNode);
@@ -700,23 +703,25 @@ class APZCTreeManager : public IAPZCTree
   void FlushRepaintsToClearScreenToGeckoTransform();
 
   void SynthesizePinchGestureFromMouseWheel(
       const ScrollWheelInput& aWheelInput,
       const RefPtr<AsyncPanZoomController>& aTarget);
 
   already_AddRefed<HitTestingTreeNode> RecycleOrCreateNode(
       const RecursiveMutexAutoLock& aProofOfTreeLock, TreeBuildingState& aState,
-      AsyncPanZoomController* aApzc, LayersId aLayersId);
+      AsyncPanZoomController* aApzc, LayersId aLayersId,
+      wr::RenderRoot aRenderRoot);
   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;
@@ -760,26 +765,26 @@ class APZCTreeManager : public IAPZCTree
    * APZC instance in isolation (that is, if its tree pointers are not being
    * accessed or mutated). The lock also needs to be held when accessing the
    * mRootNode instance variable, as that is considered part of the APZC tree
    * management state.
    * IMPORTANT: See the note about lock ordering at the top of this file. */
   mutable mozilla::RecursiveMutex mTreeLock;
   RefPtr<HitTestingTreeNode> mRootNode;
 
-  /** A lock that protects mApzcMap and mScrollThumbInfo. */
+  /** A lock that protects mApzcMaps and mScrollThumbInfo. */
   mutable mozilla::Mutex mMapLock;
   /**
    * A map for quick access to get APZC instances by guid, without having to
    * acquire the tree lock. mMapLock must be acquired while accessing or
-   * modifying mApzcMap.
+   * modifying mApzcMaps.
    */
-  std::unordered_map<ScrollableLayerGuid, RefPtr<AsyncPanZoomController>,
-                     ScrollableLayerGuid::HashIgnoringPresShellFn,
-                     ScrollableLayerGuid::EqualIgnoringPresShellFn>
+  std::unordered_map<APZCGuid, RefPtr<AsyncPanZoomController>,
+                     APZCGuid::HashIgnoringPresShellFn,
+                     APZCGuid::EqualIgnoringPresShellFn>
       mApzcMap;
   /**
    * A helper structure to store all the information needed to compute the
    * async transform for a scrollthumb on the sampler thread.
    */
   struct ScrollThumbInfo {
     uint64_t mThumbAnimationId;
     CSSTransformMatrix mThumbTransform;
--- a/gfx/layers/apz/src/APZSampler.cpp
+++ b/gfx/layers/apz/src/APZSampler.cpp
@@ -58,43 +58,45 @@ void APZSampler::SetWebRenderWindowId(co
 
 /*static*/ void APZSampler::SetSamplerThread(const wr::WrWindowId& aWindowId) {
   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) {
+/*static*/ void APZSampler::SampleForWebRender(
+    const wr::WrWindowId& aWindowId, 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
@@ -262,13 +264,15 @@ bool APZSampler::IsSamplerThread() const
 }  // 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
@@ -82,26 +82,30 @@ void APZUpdater::SetWebRenderWindowId(co
     // 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]);
+    APZNodeId layersId = APZNodeId(
+        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);
+    APZNodeId layersId = APZNodeId(
+        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
@@ -124,17 +128,17 @@ void APZUpdater::SetWebRenderWindowId(co
   if (RefPtr<APZUpdater> updater = GetUpdater(aWindowId)) {
     updater->ProcessQueue();
   }
 }
 
 void APZUpdater::ClearTree(LayersId aRootLayersId) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
-  RunOnUpdaterThread(aRootLayersId,
+  RunOnUpdaterThread(APZNodeId(aRootLayersId),
                      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.
@@ -142,188 +146,201 @@ void APZUpdater::ClearTree(LayersId aRoo
                        if (self->mWindowId) {
                          MOZ_ASSERT(sWindowIdMap);
                          sWindowIdMap->erase(wr::AsUint64(*(self->mWindowId)));
                        }
                      }));
 }
 
 void APZUpdater::UpdateFocusState(LayersId aRootLayerTreeId,
-                                  LayersId aOriginatingLayersId,
+                                  APZNodeId aOriginatingLayersId,
                                   const FocusTarget& aFocusTarget) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RunOnUpdaterThread(aOriginatingLayersId,
-                     NewRunnableMethod<LayersId, LayersId, FocusTarget>(
+                     NewRunnableMethod<LayersId, LayersId, FocusTarget, wr::RenderRoot>(
                          "APZUpdater::UpdateFocusState", mApz,
-                         &APZCTreeManager::UpdateFocusState, aRootLayerTreeId,
-                         aOriginatingLayersId, aFocusTarget));
+                         &APZCTreeManager::UpdateFocusState,
+                         aRootLayerTreeId,
+                         aOriginatingLayersId.mLayersId,
+                         aFocusTarget,
+                         aOriginatingLayersId.mRenderRoot));
 }
 
 void APZUpdater::UpdateHitTestingTree(LayersId aRootLayerTreeId, Layer* aRoot,
                                       bool aIsFirstPaint,
                                       LayersId aOriginatingLayersId,
                                       uint32_t aPaintSequenceNumber) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   AssertOnUpdaterThread();
   mApz->UpdateHitTestingTree(aRootLayerTreeId, aRoot, aIsFirstPaint,
                              aOriginatingLayersId, aPaintSequenceNumber);
 }
 
 void APZUpdater::UpdateScrollDataAndTreeState(
-    LayersId aRootLayerTreeId, LayersId aOriginatingLayersId,
+    LayersId aRootLayerTreeId, APZNodeId aOriginatingLayersId,
     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,
       NS_NewRunnableFunction("APZUpdater::UpdateEpochRequirement", [=]() {
-        if (aRootLayerTreeId == aOriginatingLayersId) {
+        if (APZNodeId(aRootLayerTreeId) == aOriginatingLayersId) {
           self->mEpochData[aOriginatingLayersId].mIsRoot = true;
         }
         self->mEpochData[aOriginatingLayersId].mRequired = aEpoch;
       }));
   RunOnUpdaterThread(
       aOriginatingLayersId,
       NS_NewRunnableFunction(
           "APZUpdater::UpdateHitTestingTree",
           [=, aScrollData = std::move(aScrollData)]() {
-            self->mApz->UpdateFocusState(aRootLayerTreeId, aOriginatingLayersId,
-                                         aScrollData.GetFocusTarget());
+            self->mApz->UpdateFocusState(aRootLayerTreeId,
+                                         aOriginatingLayersId.mLayersId,
+                                         aScrollData.GetFocusTarget(),
+                                         aOriginatingLayersId.mRenderRoot);
 
             self->mScrollData[aOriginatingLayersId] = aScrollData;
-            auto root = self->mScrollData.find(aRootLayerTreeId);
+            auto root = self->mScrollData.find(APZNodeId(aRootLayerTreeId));
             if (root == self->mScrollData.end()) {
               return;
             }
             self->mApz->UpdateHitTestingTree(
                 aRootLayerTreeId,
-                WebRenderScrollDataWrapper(*self, &(root->second)),
-                aScrollData.IsFirstPaint(), aOriginatingLayersId,
+                WebRenderScrollDataWrapper(*self,
+                                           APZNodeId(aRootLayerTreeId),
+                                           &(root->second)),
+                aScrollData.IsFirstPaint(),
+                aOriginatingLayersId.mLayersId,
                 aScrollData.GetPaintSequenceNumber());
           }));
 }
 
 void APZUpdater::UpdateScrollOffsets(LayersId aRootLayerTreeId,
-                                     LayersId aOriginatingLayersId,
+                                     APZNodeId aOriginatingLayersId,
                                      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);
+            auto root = self->mScrollData.find(APZNodeId(aRootLayerTreeId));
             if (root == self->mScrollData.end()) {
               return;
             }
+
             self->mApz->UpdateHitTestingTree(
                 aRootLayerTreeId,
-                WebRenderScrollDataWrapper(*self, &(root->second)),
-                /*isFirstPaint*/ false, aOriginatingLayersId,
+                WebRenderScrollDataWrapper(*self,
+                                           APZNodeId(aRootLayerTreeId),
+                                           &(root->second)),
+                /*isFirstPaint*/ false,
+                aOriginatingLayersId.mLayersId,
                 aPaintSequenceNumber);
           }));
 }
 
-void APZUpdater::NotifyLayerTreeAdopted(LayersId aLayersId,
+void APZUpdater::NotifyLayerTreeAdopted(APZNodeId aLayersId,
                                         const RefPtr<APZUpdater>& aOldUpdater) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RunOnUpdaterThread(aLayersId,
-                     NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>>(
+                     NewRunnableMethod<LayersId, RefPtr<APZCTreeManager>, wr::RenderRoot>(
                          "APZUpdater::NotifyLayerTreeAdopted", mApz,
-                         &APZCTreeManager::NotifyLayerTreeAdopted, aLayersId,
-                         aOldUpdater ? aOldUpdater->mApz : nullptr));
+                         &APZCTreeManager::NotifyLayerTreeAdopted, aLayersId.mLayersId,
+                         aOldUpdater ? aOldUpdater->mApz : nullptr, aLayersId.mRenderRoot));
 }
 
-void APZUpdater::NotifyLayerTreeRemoved(LayersId aLayersId) {
+void APZUpdater::NotifyLayerTreeRemoved(APZNodeId aLayersId) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZUpdater> self = this;
   RunOnUpdaterThread(
       aLayersId,
       NS_NewRunnableFunction("APZUpdater::NotifyLayerTreeRemoved", [=]() {
         self->mEpochData.erase(aLayersId);
         self->mScrollData.erase(aLayersId);
-        self->mApz->NotifyLayerTreeRemoved(aLayersId);
+        self->mApz->NotifyLayerTreeRemoved(aLayersId.mLayersId,
+                                           aLayersId.mRenderRoot);
       }));
 }
 
-bool APZUpdater::GetAPZTestData(LayersId aLayersId, APZTestData* aOutData) {
+bool APZUpdater::GetAPZTestData(APZNodeId aLayersId, APZTestData* aOutData) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   RefPtr<APZCTreeManager> apz = mApz;
   bool ret = false;
   SynchronousTask waiter("APZUpdater::GetAPZTestData");
   RunOnUpdaterThread(
       aLayersId, NS_NewRunnableFunction("APZUpdater::GetAPZTestData", [&]() {
         AutoCompleteTask notifier(&waiter);
-        ret = apz->GetAPZTestData(aLayersId, aOutData);
+        ret = apz->GetAPZTestData(aLayersId.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,
+    APZNodeId aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
     const CSSPoint& aOffset) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZCTreeManager> apz = mApz;
   RunOnUpdaterThread(
       aLayersId,
       NS_NewRunnableFunction("APZUpdater::SetTestAsyncScrollOffset", [=]() {
         RefPtr<AsyncPanZoomController> apzc =
-            apz->GetTargetAPZC(aLayersId, aScrollId);
+            apz->GetTargetAPZC(aLayersId.mLayersId, aScrollId, aLayersId.mRenderRoot);
         if (apzc) {
           apzc->SetTestAsyncScrollOffset(aOffset);
         } else {
           NS_WARNING("Unable to find APZC in SetTestAsyncScrollOffset");
         }
       }));
 }
 
-void APZUpdater::SetTestAsyncZoom(LayersId aLayersId,
+void APZUpdater::SetTestAsyncZoom(APZNodeId aLayersId,
                                   const ScrollableLayerGuid::ViewID& aScrollId,
                                   const LayerToParentLayerScale& aZoom) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   RefPtr<APZCTreeManager> apz = mApz;
   RunOnUpdaterThread(
       aLayersId, NS_NewRunnableFunction("APZUpdater::SetTestAsyncZoom", [=]() {
         RefPtr<AsyncPanZoomController> apzc =
-            apz->GetTargetAPZC(aLayersId, aScrollId);
+            apz->GetTargetAPZC(aLayersId.mLayersId, aScrollId, aLayersId.mRenderRoot);
         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(APZNodeId aLayersId) const {
   AssertOnUpdaterThread();
   auto it = mScrollData.find(aLayersId);
   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(APZNodeId aLayersId,
                                     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
@@ -353,17 +370,17 @@ void APZUpdater::RunOnUpdaterThread(Laye
           // another WakeSceneBuilder message.
           sendWakeMessage = false;
           break;
         }
       }
       mUpdaterQueue.push_back(QueuedTask{aLayersId, task});
     }
     if (sendWakeMessage) {
-      RefPtr<wr::WebRenderAPI> api = mApz->GetWebRenderAPI();
+      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.
@@ -388,17 +405,17 @@ 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(APZNodeId aLayersId,
                                        already_AddRefed<Runnable> aTask) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
   RefPtr<Runnable> task = aTask;
 
   RunOnUpdaterThread(aLayersId,
                      NewRunnableFunction("APZUpdater::RunOnControllerThread",
                                          &APZThreadUtils::RunOnControllerThread,
--- a/gfx/layers/apz/src/AsyncPanZoomController.cpp
+++ b/gfx/layers/apz/src/AsyncPanZoomController.cpp
@@ -782,18 +782,20 @@ class SmoothScrollAnimation : public Asy
   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),
@@ -975,17 +977,17 @@ nsEventStatus AsyncPanZoomController::Ha
   if (aEvent.mType == MouseInput::MouseType::MOUSE_UP) {
     APZC_LOG("%p ending drag\n", this);
     SetState(NOTHING);
     ScrollSnap();
     return nsEventStatus_eConsumeNoDefault;
   }
 
   HitTestingTreeNodeAutoLock node;
-  GetApzcTreeManager()->FindScrollThumbNode(aDragMetrics, node);
+  GetApzcTreeManager()->FindScrollThumbNode(aDragMetrics, node, mRenderRoot);
   if (!node) {
     APZC_LOG("%p unable to find scrollthumb node with viewid %" PRIu64 "\n",
              this, aDragMetrics.mViewId);
     return nsEventStatus_eConsumeNoDefault;
   }
 
   if (aEvent.mType == MouseInput::MouseType::MOUSE_DOWN) {
     APZC_LOG("%p starting scrollbar drag\n", this);
@@ -4959,32 +4961,47 @@ void AsyncPanZoomController::PostDelayed
   // we probably don't need to run the task. It will get destroyed when the
   // RefPtr goes out of scope.
 }
 
 bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid) {
   return aGuid == GetGuid();
 }
 
+bool AsyncPanZoomController::Matches(const APZCGuid& aGuid) {
+  return aGuid == GetAPZCGuid();
+}
+
 bool AsyncPanZoomController::HasTreeManager(
     const APZCTreeManager* aTreeManager) const {
   return GetApzcTreeManager() == aTreeManager;
 }
 
 void AsyncPanZoomController::GetGuid(ScrollableLayerGuid* aGuidOut) const {
   if (aGuidOut) {
     *aGuidOut = GetGuid();
   }
 }
 
 ScrollableLayerGuid AsyncPanZoomController::GetGuid() const {
   return ScrollableLayerGuid(mLayersId, Metrics().GetPresShellId(),
                              Metrics().GetScrollId());
 }
 
+void AsyncPanZoomController::GetAPZCGuid(APZCGuid* aGuidOut) const {
+  if (aGuidOut) {
+    *aGuidOut = GetAPZCGuid();
+  }
+}
+
+APZCGuid AsyncPanZoomController::GetAPZCGuid() const {
+  return APZCGuid(mLayersId, Metrics().GetPresShellId(),
+                  Metrics().GetScrollId(), mRenderRoot);
+}
+
 void AsyncPanZoomController::UpdateSharedCompositorFrameMetrics() {
   mRecursiveMutex.AssertCurrentThreadIn();
 
   FrameMetrics* frame =
       mSharedFrameMetricsBuffer
           ? static_cast<FrameMetrics*>(mSharedFrameMetricsBuffer->memory())
           : nullptr;
 
--- a/gfx/layers/apz/src/AsyncPanZoomController.h
+++ b/gfx/layers/apz/src/AsyncPanZoomController.h
@@ -39,16 +39,17 @@ class SharedMemoryBasic;
 
 }  // namespace ipc
 
 namespace layers {
 
 class AsyncDragMetrics;
 class APZCTreeManager;
 struct ScrollableLayerGuid;
+struct APZCGuid;
 class CompositorController;
 class MetricsSharingController;
 class GestureEventListener;
 struct AsyncTransform;
 class AsyncPanZoomAnimation;
 class StackScrollerFlingAnimation;
 template <typename FlingPhysics>
 class GenericFlingAnimation;
@@ -187,16 +188,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
@@ -391,16 +393,32 @@ class AsyncPanZoomController {
   ScrollableLayerGuid GetGuid() const;
 
   /**
    * Returns true if this APZC instance is for the layer identified by the guid.
    */
   bool Matches(const ScrollableLayerGuid& aGuid);
 
   /**
+   * Populates the provided object (if non-null) with the scrollable guid of
+   * this apzc.
+   */
+  void GetAPZCGuid(APZCGuid* aGuidOut) const;
+
+  /**
+   * Returns the scrollable guid of this apzc.
+   */
+  APZCGuid GetAPZCGuid() const;
+
+  /**
+   * Returns true if this APZC instance is for the layer identified by the guid.
+   */
+  bool Matches(const APZCGuid& aGuid);
+
+  /**
    * Returns true if the tree manager of this APZC is the same as the one
    * passed in.
    */
   bool HasTreeManager(const APZCTreeManager* aTreeManager) const;
 
   void StartAnimation(AsyncPanZoomAnimation* aAnimation);
 
   /**
@@ -887,16 +905,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;
@@ -1582,16 +1601,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/FocusState.cpp
+++ b/gfx/layers/apz/src/FocusState.cpp
@@ -53,42 +53,44 @@ void FocusState::ReceiveFocusChangingEve
   }
   mLastAPZProcessedEvent += 1;
   FS_LOG("Focus changing event incremented aseq to %" PRIu64 "\n",
          mLastAPZProcessedEvent);
 }
 
 void FocusState::Update(LayersId aRootLayerTreeId,
                         LayersId aOriginatingLayersId,
-                        const FocusTarget& aState) {
+                        const FocusTarget& aState,
+                        wr::RenderRoot aRenderRoot) {
   // This runs on the updater thread, it's not worth passing around extra raw
   // pointers just to assert it.
 
   MutexAutoLock lock(mMutex);
 
   FS_LOG("Update with rlt=%" PRIu64 ", olt=%" PRIu64 ", ft=(%s, %" PRIu64 ")\n",
          aRootLayerTreeId, aOriginatingLayersId, aState.Type(),
          aState.mSequenceNumber);
   mReceivedUpdate = true;
 
+  auto& focusTree = mFocusTrees[(int)aRenderRoot];
   // Update the focus tree with the latest target
-  mFocusTree[aOriginatingLayersId] = aState;
+  focusTree[aOriginatingLayersId] = aState;
 
   // Reset our internal state so we can recalculate it
   mFocusHasKeyEventListeners = false;
   mFocusLayersId = aRootLayerTreeId;
   mFocusHorizontalTarget = ScrollableLayerGuid::NULL_SCROLL_ID;
   mFocusVerticalTarget = ScrollableLayerGuid::NULL_SCROLL_ID;
 
   // To update the focus state for the entire APZCTreeManager, we need
   // to traverse the focus tree to find the current leaf which is the global
   // focus target we can use for async keyboard scrolling
   while (true) {
-    auto currentNode = mFocusTree.find(mFocusLayersId);
-    if (currentNode == mFocusTree.end()) {
+    auto currentNode = focusTree.find(mFocusLayersId);
+    if (currentNode == focusTree.end()) {
       FS_LOG("Setting target to nil (cannot find lt=%" PRIu64 ")\n",
              mFocusLayersId);
       return;
     }
 
     const FocusTarget& target = currentNode->second;
 
     // Accumulate event listener flags on the path to the focus target
@@ -170,22 +172,22 @@ void FocusState::Update(LayersId aRootLa
 
     if (target.mData.match(
             FocusTargetDataMatcher{*this, target.mSequenceNumber})) {
       return;
     }
   }
 }
 
-void FocusState::RemoveFocusTarget(LayersId aLayersId) {
+void FocusState::RemoveFocusTarget(LayersId aLayersId, wr::RenderRoot aRenderRoot) {
   // This runs on the updater thread, it's not worth passing around extra raw
   // pointers just to assert it.
   MutexAutoLock lock(mMutex);
 
-  mFocusTree.erase(aLayersId);
+  mFocusTrees[(int)aRenderRoot].erase(aLayersId);
 }
 
 Maybe<ScrollableLayerGuid> FocusState::GetHorizontalTarget() const {
   APZThreadUtils::AssertOnControllerThread();
   MutexAutoLock lock(mMutex);
 
   // There is not a scrollable layer to async scroll if
   //   1. We aren't current
--- a/gfx/layers/apz/src/FocusState.h
+++ b/gfx/layers/apz/src/FocusState.h
@@ -91,22 +91,22 @@ class FocusState final {
    * a focus target update received from chrome or content.
    *
    * @param aRootLayerTreeId the layer tree ID of the root layer for the
                              parent APZCTreeManager
    * @param aOriginatingLayersId the layer tree ID that this focus target
                                  belongs to
    */
   void Update(LayersId aRootLayerTreeId, LayersId aOriginatingLayersId,
-              const FocusTarget& aTarget);
+              const FocusTarget& aTarget, wr::RenderRoot aRenderRoot);
 
   /**
    * Removes a focus target by its layer tree ID.
    */
-  void RemoveFocusTarget(LayersId aLayersId);
+  void RemoveFocusTarget(LayersId aLayersId, wr::RenderRoot aRenderRoot);
 
   /**
    * Gets the scrollable layer that should be horizontally scrolled for a key
    * event, if any. The returned ScrollableLayerGuid doesn't contain a
    * presShellId, and so it should not be used in comparisons.
    *
    * No scrollable layer is returned if any of the following are true:
    *   1. We don't have a current focus target
@@ -115,16 +115,21 @@ class FocusState final {
    */
   Maybe<ScrollableLayerGuid> GetHorizontalTarget() const;
   /**
    * The same as GetHorizontalTarget() but for vertical scrolling.
    */
   Maybe<ScrollableLayerGuid> GetVerticalTarget() const;
 
   /**
+   * The most recently set render root.
+   */
+  wr::RenderRoot GetRenderRoot() const { return mRenderRoot; }
+
+  /**
    * Gets whether it is safe to not increment the focus sequence number for an
    * unmatched keyboard event.
    */
   bool CanIgnoreKeyboardShortcutMisses() const;
 
  private:
   /**
    * Whether the current focus state is known to be current or else if an event
@@ -136,17 +141,18 @@ class FocusState final {
   bool IsCurrent(const MutexAutoLock& aLock) const;
 
  private:
   // All methods should hold this lock, since this class is accessed via both
   // the updater and controller threads.
   mutable Mutex mMutex;
 
   // The set of focus targets received indexed by their layer tree ID
-  std::unordered_map<LayersId, FocusTarget, LayersId::HashFn> mFocusTree;
+  Array<std::unordered_map<LayersId, FocusTarget, LayersId::HashFn>,
+        wr::kRenderRootCount> mFocusTrees;
 
   // The focus sequence number of the last potentially focus changing event
   // processed by APZ. This number starts at one and increases monotonically.
   // We don't worry about wrap around here because at a pace of 100
   // increments/sec, it would take 5.85*10^9 years before we would wrap around.
   // This number will never be zero as that is used to catch uninitialized focus
   // sequence numbers on input events.
   uint64_t mLastAPZProcessedEvent;
@@ -157,16 +163,20 @@ class FocusState final {
   // focused element
   bool mFocusHasKeyEventListeners;
   // A flag that is false until the first call to Update().
   bool mReceivedUpdate;
 
   // The layer tree ID which contains the scrollable frame of the focused
   // element
   LayersId mFocusLayersId;
+
+  // The render root of the focused element
+  wr::RenderRoot mRenderRoot;
+
   // The scrollable layer corresponding to the scrollable frame that is used to
   // scroll the focused element. This depends on the direction the user is
   // scrolling.
   ScrollableLayerGuid::ViewID mFocusHorizontalTarget;
   ScrollableLayerGuid::ViewID mFocusVerticalTarget;
 };
 
 }  // namespace layers
--- a/gfx/layers/apz/src/HitTestingTreeNode.cpp
+++ b/gfx/layers/apz/src/HitTestingTreeNode.cpp
@@ -19,21 +19,23 @@ namespace layers {
 
 using gfx::CompositorHitTestFlags;
 using gfx::CompositorHitTestInfo;
 using gfx::CompositorHitTestInvisibleToHit;
 using gfx::CompositorHitTestTouchActionMask;
 
 HitTestingTreeNode::HitTestingTreeNode(AsyncPanZoomController* aApzc,
                                        bool aIsPrimaryHolder,
-                                       LayersId aLayersId)
+                                       LayersId aLayersId,
+                                       wr::RenderRoot aRenderRoot)
     : mApzc(aApzc),
       mIsPrimaryApzcHolder(aIsPrimaryHolder),
       mLockCount(0),
       mLayersId(aLayersId),
+      mRenderRoot(aRenderRoot),
       mScrollbarAnimationId(0),
       mFixedPosTarget(ScrollableLayerGuid::NULL_SCROLL_ID),
       mIsBackfaceHidden(false),
       mOverride(EventRegionsOverride::NoOverride) {
   if (mIsPrimaryApzcHolder) {
     MOZ_ASSERT(mApzc);
   }
   MOZ_ASSERT(!mApzc || mApzc->GetLayersId() == mLayersId);
@@ -200,16 +202,18 @@ AsyncPanZoomController* HitTestingTreeNo
 }
 
 bool HitTestingTreeNode::IsPrimaryHolder() const {
   return mIsPrimaryApzcHolder;
 }
 
 LayersId HitTestingTreeNode::GetLayersId() const { return mLayersId; }
 
+wr::RenderRoot HitTestingTreeNode::GetRenderRoot() const { return mRenderRoot; }
+
 void HitTestingTreeNode::SetHitTestData(
     const EventRegions& aRegions, const LayerIntRegion& aVisibleRegion,
     const CSSTransformMatrix& aTransform,
     const Maybe<ParentLayerIntRegion>& aClipRegion,
     const EventRegionsOverride& aOverride, bool aIsBackfaceHidden) {
   mEventRegions = aRegions;
   mVisibleRegion = aVisibleRegion;
   mTransform = aTransform;
--- a/gfx/layers/apz/src/HitTestingTreeNode.h
+++ b/gfx/layers/apz/src/HitTestingTreeNode.h
@@ -59,17 +59,17 @@ class AsyncPanZoomController;
 class HitTestingTreeNode {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(HitTestingTreeNode);
 
  private:
   ~HitTestingTreeNode();
 
  public:
   HitTestingTreeNode(AsyncPanZoomController* aApzc, bool aIsPrimaryHolder,
-                     LayersId aLayersId);
+                     LayersId aLayersId, wr::RenderRoot aRenderRoot);
   void RecycleWith(const RecursiveMutexAutoLock& aProofOfTreeLock,
                    AsyncPanZoomController* aApzc, LayersId aLayersId);
   // Clears the tree pointers on the node, thereby breaking RefPtr cycles. This
   // can trigger free'ing of this and other HitTestingTreeNode instances.
   void Destroy();
 
   // Returns true if and only if the node is available for recycling as part
   // of a hit-testing tree update. Note that this node can have Destroy() called
@@ -93,16 +93,17 @@ class HitTestingTreeNode {
   bool IsAncestorOf(const HitTestingTreeNode* aOther) const;
 
   /* APZC related methods */
 
   AsyncPanZoomController* GetApzc() const;
   AsyncPanZoomController* GetNearestContainingApzc() const;
   bool IsPrimaryHolder() const;
   LayersId GetLayersId() const;
+  wr::RenderRoot GetRenderRoot() const;
 
   /* Hit test related methods */
 
   void SetHitTestData(const EventRegions& aRegions,
                       const LayerIntRegion& aVisibleRegion,
                       const CSSTransformMatrix& aTransform,
                       const Maybe<ParentLayerIntRegion>& aClipRegion,
                       const EventRegionsOverride& aOverride,
@@ -156,16 +157,17 @@ class HitTestingTreeNode {
   RefPtr<HitTestingTreeNode> mPrevSibling;
   RefPtr<HitTestingTreeNode> mParent;
 
   RefPtr<AsyncPanZoomController> mApzc;
   bool mIsPrimaryApzcHolder;
   int mLockCount;
 
   LayersId mLayersId;
+  wr::RenderRoot mRenderRoot;
 
   // This is only set to non-zero if WebRender is enabled, and only for HTTNs
   // where IsScrollThumbNode() returns true. It holds the animation id that we
   // use to move the thumb node to reflect async scrolling.
   uint64_t mScrollbarAnimationId;
 
   // This is set for scrollbar Container and Thumb layers.
   ScrollbarData mScrollbarData;
--- 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
@@ -206,32 +206,35 @@ class TestAPZCTreeManager : public APZCT
   /**
    * This function is not currently implemented.
    * See bug 1468804 for more information.
    **/
   void CancelAnimation() { EXPECT_TRUE(false); }
 
  protected:
   AsyncPanZoomController* NewAPZCInstance(
-      LayersId aLayersId, GeckoContentController* aController) override;
+      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
@@ -793,21 +796,23 @@ void APZCTesterBase::PinchWithTouchInput
                                          ? nsEventStatus_eConsumeDoDefault
                                          : nsEventStatus_eIgnore;
   EXPECT_EQ(nsEventStatus_eConsumeDoDefault, statuses[0]);
   EXPECT_EQ(expectedMoveStatus, statuses[1]);
   EXPECT_EQ(expectedMoveStatus, statuses[2]);
 }
 
 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);
 }
 
 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/test/gtest/TestEventRegions.cpp
+++ b/gfx/layers/apz/test/gtest/TestEventRegions.cpp
@@ -233,18 +233,18 @@ TEST_F(APZEventRegionsTester, HitRegionI
   mcc->RunThroughDelayedTasks();  // this runs the main-thread timeout
   check.Call("Tap pending on d-t-c region");
   mcc->RunThroughDelayedTasks();  // this runs the tap event
   check.Call("Tapped on bottom again");
 
   // Now let's do that again, but simulate a main-thread response
   uint64_t inputBlockId = 0;
   Tap(manager, ScreenIntPoint(10, 110), tapDuration, nullptr, &inputBlockId);
-  nsTArray<ScrollableLayerGuid> targets;
-  targets.AppendElement(left->GetGuid());
+  nsTArray<APZCGuid> targets;
+  targets.AppendElement(left->GetAPZCGuid());
   manager->SetTargetAPZC(inputBlockId, targets);
   while (mcc->RunThroughDelayedTasks())
     ;  // this runs the tap event
   check.Call("Tapped on left this time");
 }
 
 TEST_F(APZEventRegionsTester, HitRegionAccumulatesChildren) {
   CreateEventRegionsLayerTree2();
@@ -302,12 +302,12 @@ TEST_F(APZEventRegionsTester, Bug1117712
   Tap(manager, ScreenIntPoint(55, 5), TimeDuration::FromMilliseconds(100),
       nullptr, &inputBlockId);
   // But now we tell the APZ that really it hit layers[2], and expect the tap
   // to be delivered at the correct coordinates.
   EXPECT_CALL(*mcc, HandleTap(TapType::eSingleTap, LayoutDevicePoint(55, 5), 0,
                               apzc2->GetGuid(), _))
       .Times(1);
 
-  nsTArray<ScrollableLayerGuid> targets;
-  targets.AppendElement(apzc2->GetGuid());
+  nsTArray<APZCGuid> targets;
+  targets.AppendElement(apzc2->GetAPZCGuid());
   manager->SetTargetAPZC(inputBlockId, targets);
 }
--- a/gfx/layers/apz/test/gtest/TestTreeManager.cpp
+++ b/gfx/layers/apz/test/gtest/TestTreeManager.cpp
@@ -61,39 +61,39 @@ TEST_F(APZCTreeManagerTester, Bug1068268
 }
 
 TEST_F(APZCTreeManagerTester, Bug1194876) {
   CreateBug1194876Tree();
   ScopedLayerTreeRegistration registration(manager, LayersId{0}, root, mcc);
   manager->UpdateHitTestingTree(LayersId{0}, root, false, LayersId{0}, 0);
 
   uint64_t blockId;
-  nsTArray<ScrollableLayerGuid> targets;
+  nsTArray<APZCGuid> targets;
 
   // First touch goes down, APZCTM will hit layers[1] because it is on top of
   // layers[0], but we tell it the real target APZC is layers[0].
   MultiTouchInput mti;
   mti = CreateMultiTouchInput(MultiTouchInput::MULTITOUCH_START, mcc->Time());
   mti.mTouches.AppendElement(
       SingleTouchData(0, ParentLayerPoint(25, 50), ScreenSize(0, 0), 0, 0));
   manager->ReceiveInputEvent(mti, nullptr, &blockId);
   manager->ContentReceivedInputBlock(blockId, false);
-  targets.AppendElement(ApzcOf(layers[0])->GetGuid());
+  targets.AppendElement(ApzcOf(layers[0])->GetAPZCGuid());
   manager->SetTargetAPZC(blockId, targets);
 
   // Around here, the above touch will get processed by ApzcOf(layers[0])
 
   // Second touch goes down (first touch remains down), APZCTM will again hit
   // layers[1]. Again we tell it both touches landed on layers[0], but because
   // layers[1] is the RCD layer, it will end up being the multitouch target.
   mti.mTouches.AppendElement(
       SingleTouchData(1, ParentLayerPoint(75, 50), ScreenSize(0, 0), 0, 0));
   manager->ReceiveInputEvent(mti, nullptr, &blockId);
   manager->ContentReceivedInputBlock(blockId, false);
-  targets.AppendElement(ApzcOf(layers[0])->GetGuid());
+  targets.AppendElement(ApzcOf(layers[0])->GetAPZCGuid());
   manager->SetTargetAPZC(blockId, targets);
 
   // Around here, the above multi-touch will get processed by ApzcOf(layers[1]).
   // We want to ensure that ApzcOf(layers[0]) has had its state cleared, because
   // otherwise it will do things like dispatch spurious long-tap events.
 
   EXPECT_CALL(*mcc, HandleTap(TapType::eLongTap, _, _, _, _)).Times(0);
 }
--- a/gfx/layers/apz/util/APZCCallbackHelper.cpp
+++ b/gfx/layers/apz/util/APZCCallbackHelper.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "APZCCallbackHelper.h"
 
 #include "TouchActionHelper.h"
 #include "gfxPlatform.h"  // For gfxPlatform::UseTiling
 #include "gfxPrefs.h"
 #include "LayersLogging.h"  // For Stringify
+#include "mozilla/gfx/gfxVars.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/MouseEventBinding.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/IntegerPrintfMacros.h"
 #include "mozilla/layers/LayerTransactionChild.h"
 #include "mozilla/layers/ShadowLayers.h"
 #include "mozilla/layers/WebRenderLayerManager.h"
 #include "mozilla/layers/WebRenderBridgeChild.h"
@@ -643,20 +644,19 @@ 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);
+    const LayoutDeviceIntPoint& aRefPoint, nsTArray<APZCGuid>* aTargets) {
+  APZCGuid guid(aGuid.mLayersId, 0, ScrollableLayerGuid::NULL_SCROLL_ID,
+                wr::RenderRoot::Default);
   nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
       aWidget, aRefPoint, aRootFrame);
   EnumSet<FrameForPointOption> options;
   if (gfxPrefs::APZAllowZooming()) {
     // 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
     // need to be fixed before enabling zooming by default on desktop).
@@ -668,28 +668,34 @@ 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 (mozilla::gfx::gfxVars::UseWebRender() &&
+      gfxPrefs::WebRenderSplitRenderRoots()) {
+    // Check for content render root
+  }
+
 #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
@@ -713,17 +719,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<APZCGuid>& aTargets) {
   LayerManager* lm = aShell->GetLayerManager();
   if (!lm) {
     return;
   }
 
   if (WebRenderLayerManager* wrlm = lm->AsWebRenderLayerManager()) {
     if (WebRenderBridgeChild* wrbc = wrlm->WrBridge()) {
       wrbc->SendSetConfirmedTargetAPZC(aInputBlockId, aTargets);
@@ -743,17 +749,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<APZCGuid>& aTargets)
     : mWidget(aWidget),
       mPresShell(aPresShell),
       mInputBlockId(aInputBlockId),
       mTargets(aTargets) {}
 
 DisplayportSetListener::~DisplayportSetListener() {}
 
 bool DisplayportSetListener::Register() {
@@ -812,17 +818,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<APZCGuid> 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
@@ -31,26 +31,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<APZCGuid>& aTargets);
   virtual ~DisplayportSetListener();
   bool Register();
   void DidRefresh() override;
 
  private:
   RefPtr<nsIWidget> mWidget;
   RefPtr<nsIPresShell> mPresShell;
   uint64_t mInputBlockId;
-  nsTArray<ScrollableLayerGuid> mTargets;
+  nsTArray<APZCGuid> 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
@@ -134,20 +134,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<APZCGuid, CSSRect, uint32_t>(
             "IAPZCTreeManager::ZoomToRect", mAPZCTreeManager,
             &IAPZCTreeManager::ZoomToRect,
-            ScrollableLayerGuid(aGuid.mLayersId, presShellId, viewId),
+            APZCGuid(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
@@ -57,17 +57,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;
     }
   }
 
@@ -76,25 +77,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, 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) {
@@ -144,17 +146,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, aRenderRoot);
     mBackBuffer->SyncWithObject(GetForwarder()->GetSyncObject());
   }
 
   mBackBuffer.swap(mFrontBuffer);
 }
 
 already_AddRefed<TextureClient> CanvasClient2D::CreateTextureClientForCanvas(
     gfx::SurfaceFormat aFormat, gfx::IntSize aSize, TextureFlags aFlags,
@@ -350,18 +352,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);
@@ -468,17 +471,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;
@@ -488,17 +491,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, 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(),
+                                                  wr::kRenderRootUnknown)) {
     CompositableType type = GetImageClientType();
     if (type == CompositableType::UNKNOWN) {
       return;
     }
     TextureFlags flags = TextureFlags::DEFAULT;
     mImageClient = ImageClient::CreateImageClient(
         type, ClientManager()->AsShadowForwarder(), flags);
     if (!mImageClient) {
       return;
     }
     mImageClient->SetLayer(this);
     if (HasShadow() && !mContainer->IsAsync()) {
       mImageClient->Connect();
       ClientManager()->AsShadowForwarder()->Attach(mImageClient, this);
     }
-    if (!mImageClient->UpdateImage(mContainer, GetContentFlags())) {
+    if (!mImageClient->UpdateImage(mContainer, GetContentFlags(),
+                                   wr::kRenderRootUnknown)) {
       return;
     }
   }
   ClientManager()->Hold(this);
 }
 
 already_AddRefed<ImageLayer> ClientLayerManager::CreateImageLayer() {
   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,
+                                       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, 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,
+                             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
@@ -709,17 +709,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, wr::RenderRoot::Default);
   }
 
   // This forces a synchronous transaction, so we can swap buffers now
   // and know that we'll have sole ownership of the old front buffer
   // by the time we paint next.
   mForwarder->UpdateTextureRegion(
       this,
       ThebesBufferData(remoteBuffer->BufferRect(),
--- a/gfx/layers/client/ImageClient.cpp
+++ b/gfx/layers/client/ImageClient.cpp
@@ -58,32 +58,33 @@ using namespace mozilla::gfx;
       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,
+                                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);
+    RemoveTexture(b.mTextureClient, wr::RenderRoot::Default);
   }
   mBuffers.Clear();
 }
 
 /* static */ already_AddRefed<TextureClient>
 ImageClient::CreateTextureClientForImage(Image* aImage,
                                          KnowsCompositor* aForwarder) {
   RefPtr<TextureClient> texture;
@@ -151,17 +152,18 @@ ImageClient::CreateTextureClientForImage
     }
 
     texture->Unlock();
   }
   return texture.forget();
 }
 
 bool ImageClientSingle::UpdateImage(ImageContainer* aContainer,
-                                    uint32_t aContentFlags) {
+                                    uint32_t aContentFlags,
+                                    wr::RenderRoot aRenderRoot) {
   AutoTArray<ImageContainer::OwningImage, 4> images;
   uint32_t generationCounter;
   aContainer->GetCurrentImages(&images, &generationCounter);
 
   if (mLastUpdateGenerationCounter == generationCounter) {
     return true;
   }
   mLastUpdateGenerationCounter = generationCounter;
@@ -175,17 +177,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;
 
@@ -239,20 +241,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) {
@@ -275,17 +277,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,
+                                    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,
+                           wr::RenderRoot aRenderRoot) = 0;
 
   void SetLayer(ClientLayer* aLayer) { mLayer = aLayer; }
   ClientLayer* GetLayer() const { return mLayer; }
 
   /**
    * asynchronously remove all the textures used by the image client.
    *
    */
   virtual void FlushAllImages() {}
 
-  virtual void RemoveTexture(TextureClient* aTexture) override;
+  virtual void RemoveTexture(TextureClient* aTexture,
+                             wr::RenderRoot aRenderRoot) override;
 
   virtual ImageClientSingle* AsImageClientSingle() { return nullptr; }
 
   static already_AddRefed<TextureClient> CreateTextureClientForImage(
       Image* aImage, KnowsCompositor* aForwarder);
 
   uint32_t GetLastUpdateGenerationCounter() {
     return mLastUpdateGenerationCounter;
@@ -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,
+                           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,
+                           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 APZCGuid& 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<APZCGuid>& aTargets) {
   SendSetTargetAPZC(aInputBlockId, aTargets);
 }
 
 void APZCTreeManagerChild::UpdateZoomConstraints(
-    const ScrollableLayerGuid& aGuid,
+    const APZCGuid& 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 APZCGuid& aGuid, const AsyncDragMetrics& aDragMetrics) {
   SendStartScrollbarDrag(aGuid, aDragMetrics);
 }
 
-bool APZCTreeManagerChild::StartAutoscroll(const ScrollableLayerGuid& aGuid,
+bool APZCTreeManagerChild::StartAutoscroll(const APZCGuid& aGuid,
                                            const ScreenPoint& aAnchorLocation) {
   return SendStartAutoscroll(aGuid, aAnchorLocation);
 }
 
-void APZCTreeManagerChild::StopAutoscroll(const ScrollableLayerGuid& aGuid) {
+void APZCTreeManagerChild::StopAutoscroll(const APZCGuid& 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
@@ -23,42 +23,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 APZCGuid& 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<APZCGuid>& aTargets) override;
 
   void UpdateZoomConstraints(
-      const ScrollableLayerGuid& aGuid,
+      const APZCGuid& 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 APZCGuid& aGuid,
                           const AsyncDragMetrics& aDragMetrics) override;
 
-  bool StartAutoscroll(const ScrollableLayerGuid& aGuid,
+  bool StartAutoscroll(const APZCGuid& aGuid,
                        const ScreenPoint& aAnchorLocation) override;
 
-  void StopAutoscroll(const ScrollableLayerGuid& aGuid) override;
+  void StopAutoscroll(const APZCGuid& 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,17 +9,17 @@
 #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,
+    APZNodeId aLayersId, RefPtr<APZCTreeManager> aAPZCTreeManager,
     RefPtr<APZUpdater> aAPZUpdater)
     : mLayersId(aLayersId),
       mTreeManager(std::move(aAPZCTreeManager)),
       mUpdater(std::move(aAPZUpdater)) {
   MOZ_ASSERT(mTreeManager != nullptr);
   MOZ_ASSERT(mUpdater != nullptr);
   MOZ_ASSERT(mUpdater->HasTreeManager(mTreeManager));
 }
@@ -41,26 +41,26 @@ mozilla::ipc::IPCResult APZCTreeManagerP
       mLayersId, NewRunnableMethod<KeyboardMap>(
                      "layers::IAPZCTreeManager::SetKeyboardMap", mTreeManager,
                      &IAPZCTreeManager::SetKeyboardMap, aKeyboardMap));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvZoomToRect(
-    const ScrollableLayerGuid& aGuid, const CSSRect& aRect,
+    const APZCGuid& aGuid, const CSSRect& aRect,
     const uint32_t& aFlags) {
-  if (aGuid.mLayersId != mLayersId) {
+  if (aGuid.mScrollableLayerGuid.mLayersId != mLayersId.mLayersId) {
     // Guard against bad data from hijacked child processes
     NS_ERROR("Unexpected layers id in RecvZoomToRect; dropping message...");
     return IPC_FAIL_NO_REASON(this);
   }
 
   mUpdater->RunOnControllerThread(
-      mLayersId, NewRunnableMethod<ScrollableLayerGuid, CSSRect, uint32_t>(
+      mLayersId, NewRunnableMethod<APZCGuid, 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(
@@ -68,47 +68,48 @@ mozilla::ipc::IPCResult APZCTreeManagerP
                      "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<APZCGuid>&& aTargets) {
   for (size_t i = 0; i < aTargets.Length(); i++) {
-    if (aTargets[i].mLayersId != mLayersId) {
+    if (aTargets[i].mScrollableLayerGuid.mLayersId != mLayersId.mLayersId) {
       // Guard against bad data from hijacked child processes
       NS_ERROR(
           "Unexpected layers id in RecvSetTargetAPZC; dropping message...");
       return IPC_FAIL_NO_REASON(this);
     }
   }
   mUpdater->RunOnControllerThread(
       mLayersId,
       NewRunnableMethod<uint64_t,
-                        StoreCopyPassByRRef<nsTArray<ScrollableLayerGuid>>>(
+                        StoreCopyPassByRRef<nsTArray<APZCGuid>>>(
           "layers::IAPZCTreeManager::SetTargetAPZC", mTreeManager,
           &IAPZCTreeManager::SetTargetAPZC, aInputBlockId, aTargets));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvUpdateZoomConstraints(
-    const ScrollableLayerGuid& aGuid,
+    const APZCGuid& aGuid,
     const MaybeZoomConstraints& aConstraints) {
-  if (aGuid.mLayersId != mLayersId) {
+  if (aGuid.mScrollableLayerGuid.mLayersId != mLayersId.mLayersId) {
     // Guard against bad data from hijacked child processes
     NS_ERROR(
         "Unexpected layers id in RecvUpdateZoomConstraints; dropping "
         "message...");
     return IPC_FAIL_NO_REASON(this);
   }
 
-  mTreeManager->UpdateZoomConstraints(aGuid, aConstraints);
+  mTreeManager->UpdateZoomConstraints(aGuid,
+                                      aConstraints);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetDPI(
     const float& aDpiValue) {
   mUpdater->RunOnControllerThread(
       mLayersId,
       NewRunnableMethod<float>("layers::IAPZCTreeManager::SetDPI", mTreeManager,
@@ -125,57 +126,57 @@ mozilla::ipc::IPCResult APZCTreeManagerP
           "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) {
+    const APZCGuid& aGuid, const AsyncDragMetrics& aDragMetrics) {
+  if (aGuid.mScrollableLayerGuid.mLayersId != mLayersId.mLayersId) {
     // Guard against bad data from hijacked child processes
     NS_ERROR(
         "Unexpected layers id in RecvStartScrollbarDrag; dropping message...");
     return IPC_FAIL_NO_REASON(this);
   }
 
   mUpdater->RunOnControllerThread(
       mLayersId,
-      NewRunnableMethod<ScrollableLayerGuid, AsyncDragMetrics>(
+      NewRunnableMethod<APZCGuid, AsyncDragMetrics>(
           "layers::IAPZCTreeManager::StartScrollbarDrag", mTreeManager,
           &IAPZCTreeManager::StartScrollbarDrag, aGuid, aDragMetrics));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStartAutoscroll(
-    const ScrollableLayerGuid& aGuid, const ScreenPoint& aAnchorLocation) {
+    const APZCGuid& 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
   // sending the child process's layers id).
 
   mUpdater->RunOnControllerThread(
       mLayersId,
-      NewRunnableMethod<ScrollableLayerGuid, ScreenPoint>(
+      NewRunnableMethod<APZCGuid, ScreenPoint>(
           "layers::IAPZCTreeManager::StartAutoscroll", mTreeManager,
           &IAPZCTreeManager::StartAutoscroll, aGuid, aAnchorLocation));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvStopAutoscroll(
-    const ScrollableLayerGuid& aGuid) {
+    const APZCGuid& aGuid) {
   // See RecvStartAutoscroll() for why we don't check the layers id.
 
   mUpdater->RunOnControllerThread(
-      mLayersId, NewRunnableMethod<ScrollableLayerGuid>(
+      mLayersId, NewRunnableMethod<APZCGuid>(
                      "layers::IAPZCTreeManager::StopAutoscroll", mTreeManager,
                      &IAPZCTreeManager::StopAutoscroll, aGuid));
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult APZCTreeManagerParent::RecvSetLongTapEnabled(
     const bool& aLongTapEnabled) {
--- a/gfx/layers/ipc/APZCTreeManagerParent.h
+++ b/gfx/layers/ipc/APZCTreeManagerParent.h
@@ -12,72 +12,72 @@
 namespace mozilla {
 namespace layers {
 
 class APZCTreeManager;
 class APZUpdater;
 
 class APZCTreeManagerParent : public PAPZCTreeManagerParent {
  public:
-  APZCTreeManagerParent(LayersId aLayersId,
+  APZCTreeManagerParent(APZNodeId aLayersId,
                         RefPtr<APZCTreeManager> aAPZCTreeManager,
                         RefPtr<APZUpdater> mAPZUpdater);
   virtual ~APZCTreeManagerParent();
 
-  LayersId GetLayersId() const { return mLayersId; }
+  LayersId GetLayersId() const { return mLayersId.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) override;
 
-  mozilla::ipc::IPCResult RecvZoomToRect(const ScrollableLayerGuid& aGuid,
+  mozilla::ipc::IPCResult RecvZoomToRect(const APZCGuid& aGuid,
                                          const CSSRect& aRect,
                                          const uint32_t& aFlags) override;
 
   mozilla::ipc::IPCResult RecvContentReceivedInputBlock(
       const uint64_t& aInputBlockId, const bool& aPreventDefault) override;
 
   mozilla::ipc::IPCResult RecvSetTargetAPZC(
       const uint64_t& aInputBlockId,
-      nsTArray<ScrollableLayerGuid>&& aTargets) override;
+      nsTArray<APZCGuid>&& aTargets) override;
 
   mozilla::ipc::IPCResult RecvUpdateZoomConstraints(
-      const ScrollableLayerGuid& aGuid,
+      const APZCGuid& aGuid,
       const MaybeZoomConstraints& aConstraints) override;
 
   mozilla::ipc::IPCResult RecvSetDPI(const float& aDpiValue) override;
 
   mozilla::ipc::IPCResult RecvSetAllowedTouchBehavior(
       const uint64_t& aInputBlockId,
       nsTArray<TouchBehaviorFlags>&& aValues) override;
 
   mozilla::ipc::IPCResult RecvStartScrollbarDrag(
-      const ScrollableLayerGuid& aGuid,
+      const APZCGuid& aGuid,
       const AsyncDragMetrics& aDragMetrics) override;
 
   mozilla::ipc::IPCResult RecvStartAutoscroll(
-      const ScrollableLayerGuid& aGuid,
+      const APZCGuid& aGuid,
       const ScreenPoint& aAnchorLocation) override;
 
   mozilla::ipc::IPCResult RecvStopAutoscroll(
-      const ScrollableLayerGuid& aGuid) override;
+      const APZCGuid& aGuid) override;
 
   mozilla::ipc::IPCResult RecvSetLongTapEnabled(
       const bool& aTapGestureEnabled) override;
 
   void ActorDestroy(ActorDestroyReason aWhy) override {}
 
  private:
-  LayersId mLayersId;
+  APZNodeId mLayersId;
   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;
+                                             TextureClient* aTexture,
+                                             wr::RenderRoot aRenderRoot) = 0;
 
   struct TimedTextureClient {
     TimedTextureClient()
         : mTextureClient(nullptr), mFrameID(0), mProducerID(0) {}
 
     TextureClient* mTextureClient;
     TimeStamp mTimeStamp;
     nsIntRect mPictureRect;
     int32_t mFrameID;
     int32_t mProducerID;
   };
   /**
    * Tell the CompositableHost on the compositor side what textures to use for
    * the next composition.
+   *
+   * aRenderRoot can be ignored if not using WebRender - since webrender
+   * splits the chrome and content areas into different documents which are
+   * updated separately, we need to know which command buffer to route this
+   * into.
    */
   virtual void UseTextures(CompositableClient* aCompositable,
-                           const nsTArray<TimedTextureClient>& aTextures) = 0;
+                           const nsTArray<TimedTextureClient>& aTextures,
+                           wr::RenderRoot aRenderRoot) = 0;
   virtual void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                          TextureClient* aClientOnBlack,
                                          TextureClient* aClientOnWhite) = 0;
 
   virtual void UpdateFwdTransactionId() = 0;
   virtual uint64_t GetFwdTransactionId() = 0;
 
   virtual bool InForwarderThread() = 0;
--- a/gfx/layers/ipc/CompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CompositorBridgeParent.cpp
@@ -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(
+    wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(CompositorLoop());
-  CompositorLoop()->PostTask(
-      NewRunnableMethod("layers::CompositorBridgeParent::ScheduleComposition",
-                        this, &CompositorBridgeParent::ScheduleComposition));
+  CompositorLoop()->PostTask(NewRunnableMethod<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,44 @@ 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, APZNodeId(aId),
+                                    aFocusTarget);
       if (aHitTestUpdate) {
         mApzUpdater->UpdateHitTestingTree(
             mRootLayerTreeID, mLayerManager->GetRoot(), aIsFirstPaint, aId,
             aPaintSequenceNumber);
       }
     }
 
     mLayerManager->NotifyShadowTreeTransaction();
   }
   if (aScheduleComposite) {
     ScheduleComposition();
   }
 }
 
-void CompositorBridgeParent::ScheduleComposition() {
+void CompositorBridgeParent::ScheduleComposition(wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
   if (mPaused) {
     return;
   }
 
   if (mWrBridge) {
-    mWrBridge->ScheduleGenerateFrame();
+    if (aRenderRoot != wr::kRenderRootUnknown) {
+      mWrBridge->ScheduleGenerateFrame(aRenderRoot);
+    } else {
+      mWrBridge->ScheduleGenerateFrameAllRenderRoots();
+    }
   } else {
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 // Go down the composite layer tree, setting properties to match their
 // content-side counterparts.
 /* static */ void CompositorBridgeParent::SetShadowProperties(Layer* aLayer) {
@@ -1091,17 +1098,17 @@ 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);
+      APZNodeId(mRootLayerTreeID), mApzcTreeManager, mApzUpdater);
 
   return state.mApzcTreeManagerParent;
 }
 
 bool CompositorBridgeParent::DeallocPAPZCTreeManagerParent(
     PAPZCTreeManagerParent* aActor) {
   delete aActor;
   return true;
@@ -1109,18 +1116,18 @@ bool CompositorBridgeParent::DeallocPAPZ
 
 void CompositorBridgeParent::AllocateAPZCTreeManagerParent(
     const MonitorAutoLock& aProofOfLayerTreeStateLock,
     const LayersId& aLayersId, LayerTreeState& aState) {
   MOZ_ASSERT(aState.mParent == this);
   MOZ_ASSERT(mApzcTreeManager);
   MOZ_ASSERT(mApzUpdater);
   MOZ_ASSERT(!aState.mApzcTreeManagerParent);
-  aState.mApzcTreeManagerParent =
-      new APZCTreeManagerParent(aLayersId, mApzcTreeManager, mApzUpdater);
+  aState.mApzcTreeManagerParent = new APZCTreeManagerParent(
+      APZNodeId(aLayersId), 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();
 
@@ -1175,17 +1182,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;
 }
 
@@ -1226,17 +1234,17 @@ 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, APZNodeId(mRootLayerTreeID),
                                   aInfo.focusTarget());
 
     if (aHitTestUpdate) {
       AutoResolveRefLayers resolve(mCompositionManager);
 
       mApzUpdater->UpdateHitTestingTree(mRootLayerTreeID, root,
                                         aInfo.isFirstPaint(), mRootLayerTreeID,
                                         aInfo.paintSequenceNumber());
@@ -1347,61 +1355,75 @@ mozilla::ipc::IPCResult CompositorBridge
   return IPC_OK();
 }
 
 void CompositorBridgeParent::SetTestAsyncScrollOffset(
     const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
     const CSSPoint& aPoint) {
   if (mApzUpdater) {
     MOZ_ASSERT(aLayersId.IsValid());
-    mApzUpdater->SetTestAsyncScrollOffset(aLayersId, aScrollId, aPoint);
+    mApzUpdater->SetTestAsyncScrollOffset(APZNodeId(aLayersId), aScrollId,
+                                          aPoint);
   }
 }
 
 void CompositorBridgeParent::SetTestAsyncZoom(
     const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
     const LayerToParentLayerScale& aZoom) {
   if (mApzUpdater) {
     MOZ_ASSERT(aLayersId.IsValid());
-    mApzUpdater->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
+    mApzUpdater->SetTestAsyncZoom(APZNodeId(aLayersId), aScrollId, aZoom);
   }
 }
 
-void CompositorBridgeParent::FlushApzRepaints(const LayersId& aLayersId) {
+void CompositorBridgeParent::FlushApzRepaints(const APZNodeId& aLayersId) {
   MOZ_ASSERT(mApzUpdater);
   MOZ_ASSERT(aLayersId.IsValid());
-  mApzUpdater->RunOnControllerThread(
-      aLayersId, NS_NewRunnableFunction(
-                     "layers::CompositorBridgeParent::FlushApzRepaints",
-                     [=]() { APZCTreeManager::FlushApzRepaints(aLayersId); }));
+  if (mWrBridge) {
+    mApzUpdater->RunOnControllerThread(
+        aLayersId,
+        NS_NewRunnableFunction(
+            "layers::CompositorBridgeParent::FlushApzRepaints",
+            [=]() { APZCTreeManager::FlushApzRepaints(aLayersId.mLayersId); }));
+  } else {
+    mApzUpdater->RunOnControllerThread(
+        aLayersId,
+        NS_NewRunnableFunction(
+            "layers::CompositorBridgeParent::FlushApzRepaints",
+            [=]() { APZCTreeManager::FlushApzRepaints(aLayersId.mLayersId); }));
+  }
 }
 
 void CompositorBridgeParent::GetAPZTestData(const LayersId& aLayersId,
                                             APZTestData* aOutData) {
   if (mApzUpdater) {
     MOZ_ASSERT(aLayersId.IsValid());
-    mApzUpdater->GetAPZTestData(aLayersId, aOutData);
+    mApzUpdater->GetAPZTestData(APZNodeId(aLayersId), aOutData);
   }
 }
 
 void CompositorBridgeParent::SetConfirmedTargetAPZC(
     const LayersId& aLayersId, const uint64_t& aInputBlockId,
-    const nsTArray<ScrollableLayerGuid>& aTargets) {
+    const nsTArray<APZCGuid>& aTargets) {
   if (!mApzcTreeManager || !mApzUpdater) {
     return;
   }
   // Need to specifically bind this since it's overloaded.
   void (APZCTreeManager::*setTargetApzcFunc)(
-      uint64_t, const nsTArray<ScrollableLayerGuid>&) =
-      &APZCTreeManager::SetTargetAPZC;
-  RefPtr<Runnable> task = NewRunnableMethod<
-      uint64_t, StoreCopyPassByConstLRef<nsTArray<ScrollableLayerGuid>>>(
-      "layers::CompositorBridgeParent::SetConfirmedTargetAPZC",
-      mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets);
-  mApzUpdater->RunOnControllerThread(aLayersId, task.forget());
+      uint64_t, const nsTArray<APZCGuid>&) = &APZCTreeManager::SetTargetAPZC;
+  RefPtr<Runnable> task =
+      NewRunnableMethod<uint64_t, StoreCopyPassByConstLRef<nsTArray<APZCGuid>>>(
+          "layers::CompositorBridgeParent::SetConfirmedTargetAPZC",
+          mApzcTreeManager.get(), setTargetApzcFunc, aInputBlockId, aTargets);
+  wr::RenderRoot renderRoot = wr::RenderRoot::Default;
+  if (!aTargets.IsEmpty()) {
+    renderRoot = aTargets[0].mRenderRoot;
+  }
+  mApzUpdater->RunOnControllerThread(APZNodeId(aLayersId, renderRoot),
+                                     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)) {
@@ -1684,21 +1706,22 @@ 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;
+    mWrBridge->GetWebRenderAPIs(apis);
     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
@@ -1709,17 +1732,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(
+        APZNodeId(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
@@ -1739,31 +1763,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);
@@ -1788,26 +1824,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 {
@@ -1829,17 +1867,18 @@ void EraseLayerState(LayersId aId) {
       if (parent) {
         apz = parent->GetAPZUpdater();
       }
       sIndirectLayerTrees.erase(iter);
     }
   }
 
   if (apz) {
-    apz->NotifyLayerTreeRemoved(aId);
+    apz->NotifyLayerTreeRemoved(
+        APZNodeId(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.
   // Checking the elements of sIndirectLayerTrees exist or not before using.
@@ -2019,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,
@@ -2235,17 +2274,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
@@ -123,22 +123,22 @@ class CompositorBridgeParentBase : publi
   virtual void ApplyAsyncProperties(LayerTransactionParent* aLayerTree,
                                     TransformsToSkip aSkip) = 0;
   virtual void SetTestAsyncScrollOffset(
       const LayersId& aLayersId, const ScrollableLayerGuid::ViewID& aScrollId,
       const CSSPoint& aPoint) = 0;
   virtual void SetTestAsyncZoom(const LayersId& aLayersId,
                                 const ScrollableLayerGuid::ViewID& aScrollId,
                                 const LayerToParentLayerScale& aZoom) = 0;
-  virtual void FlushApzRepaints(const LayersId& aLayersId) = 0;
+  virtual void FlushApzRepaints(const APZNodeId& aLayersId) = 0;
   virtual void GetAPZTestData(const LayersId& aLayersId,
                               APZTestData* aOutData) {}
-  virtual void SetConfirmedTargetAPZC(
-      const LayersId& aLayersId, const uint64_t& aInputBlockId,
-      const nsTArray<ScrollableLayerGuid>& aTargets) = 0;
+  virtual void SetConfirmedTargetAPZC(const LayersId& aLayersId,
+                                      const uint64_t& aInputBlockId,
+                                      const nsTArray<APZCGuid>& aTargets) = 0;
   virtual void UpdatePaintTime(LayerTransactionParent* aLayerTree,
                                const TimeDuration& aPaintTime) {}
   virtual void RegisterPayload(
       LayerTransactionParent* aLayerTree,
       const InfallibleTArray<CompositionPayload>& aPayload) {}
 
   ShmemAllocator* AsShmemAllocator() override { return this; }
 
@@ -292,22 +292,22 @@ class CompositorBridgeParent final : pub
                             TransformsToSkip aSkip) override;
   CompositorAnimationStorage* GetAnimationStorage();
   void SetTestAsyncScrollOffset(const LayersId& aLayersId,
                                 const ScrollableLayerGuid::ViewID& aScrollId,
                                 const CSSPoint& aPoint) override;
   void SetTestAsyncZoom(const LayersId& aLayersId,
                         const ScrollableLayerGuid::ViewID& aScrollId,
                         const LayerToParentLayerScale& aZoom) override;
-  void FlushApzRepaints(const LayersId& aLayersId) override;
+  void FlushApzRepaints(const APZNodeId& aLayersId) override;
   void GetAPZTestData(const LayersId& aLayersId,
                       APZTestData* aOutData) override;
-  void SetConfirmedTargetAPZC(
-      const LayersId& aLayersId, const uint64_t& aInputBlockId,
-      const nsTArray<ScrollableLayerGuid>& aTargets) override;
+  void SetConfirmedTargetAPZC(const LayersId& aLayersId,
+                              const uint64_t& aInputBlockId,
+                              const nsTArray<APZCGuid>& aTargets) override;
   AsyncCompositionManager* GetCompositionManager(
       LayerTransactionParent* aLayerTree) override {
     return mCompositionManager;
   }
 
   PTextureParent* AllocPTextureParent(
       const SurfaceDescriptor& aSharedData, const ReadLockDescriptor& aReadLock,
       const LayersBackend& aLayersBackend, const TextureFlags& aFlags,
@@ -320,17 +320,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 {}
@@ -346,27 +347,28 @@ 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(
+      wr::RenderRoot aRenderRoot = wr::kRenderRootUnknown) 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(wr::RenderRoot aRenderRoot = wr::kRenderRootUnknown);
   void NotifyShadowTreeTransaction(LayersId aId, bool aIsFirstPaint,
                                    const FocusTarget& aFocusTarget,
                                    bool aScheduleComposite,
                                    uint32_t aPaintSequenceNumber,
                                    bool aIsRepeatTransaction,
                                    bool aHitTestUpdate);
 
   void UpdatePaintTime(LayerTransactionParent* aLayerTree,
--- a/gfx/layers/ipc/CrossProcessCompositorBridgeParent.cpp
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.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,17 +138,19 @@ CrossProcessCompositorBridgeParent::Allo
   // 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(
+        APZNodeId(aLayersId, gfxUtils::GetContentRenderRoot()), temp,
+        tempUpdater);
   }
 
   state.mParent->AllocateAPZCTreeManagerParent(lock, aLayersId, state);
   return state.mApzcTreeManagerParent;
 }
 bool CrossProcessCompositorBridgeParent::DeallocPAPZCTreeManagerParent(
     PAPZCTreeManagerParent* aActor) {
   APZCTreeManagerParent* parent = static_cast<APZCTreeManagerParent*>(aActor);
@@ -220,40 +223,39 @@ CrossProcessCompositorBridgeParent::Allo
     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;
   if (root) {
-    api = root->GetWebRenderAPI();
+    root->GetWebRenderAPIs(apis);
   }
 
-  if (!root || !api) {
+  if (!root || apis.IsEmpty()) {
     // 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].mCrossProcessParent = this;
     sIndirectLayerTrees[layersId].mWrBridge = parent;
   }
@@ -506,20 +508,20 @@ void CrossProcessCompositorBridgeParent:
     return;
   }
 
   MOZ_ASSERT(state->mParent);
   state->mParent->SetTestAsyncZoom(aLayersId, aScrollId, aZoom);
 }
 
 void CrossProcessCompositorBridgeParent::FlushApzRepaints(
-    const LayersId& aLayersId) {
+    const APZNodeId& 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 CrossProcessCompositorBridgeParent::GetAPZTestData(
@@ -531,17 +533,17 @@ void CrossProcessCompositorBridgeParent:
     return;
   }
 
   state->mParent->GetAPZTestData(aLayersId, aOutData);
 }
 
 void CrossProcessCompositorBridgeParent::SetConfirmedTargetAPZC(
     const LayersId& aLayersId, const uint64_t& aInputBlockId,
-    const nsTArray<ScrollableLayerGuid>& aTargets) {
+    const nsTArray<APZCGuid>& 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/CrossProcessCompositorBridgeParent.h
+++ b/gfx/layers/ipc/CrossProcessCompositorBridgeParent.h
@@ -116,22 +116,22 @@ class CrossProcessCompositorBridgeParent
   void ApplyAsyncProperties(LayerTransactionParent* aLayerTree,
                             TransformsToSkip aSkip) override;
   void SetTestAsyncScrollOffset(const LayersId& aLayersId,
                                 const ScrollableLayerGuid::ViewID& aScrollId,
                                 const CSSPoint& aPoint) override;
   void SetTestAsyncZoom(const LayersId& aLayersId,
                         const ScrollableLayerGuid::ViewID& aScrollId,
                         const LayerToParentLayerScale& aZoom) override;
-  void FlushApzRepaints(const LayersId& aLayersId) override;
+  void FlushApzRepaints(const APZNodeId& aLayersId) override;
   void GetAPZTestData(const LayersId& aLayersId,
                       APZTestData* aOutData) override;
-  void SetConfirmedTargetAPZC(
-      const LayersId& aLayersId, const uint64_t& aInputBlockId,
-      const nsTArray<ScrollableLayerGuid>& aTargets) override;
+  void SetConfirmedTargetAPZC(const LayersId& aLayersId,
+                              const uint64_t& aInputBlockId,
+                              const nsTArray<APZCGuid>& 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,17 @@ struct CompositableTransaction {
 struct AutoEndTransaction {
   explicit AutoEndTransaction(CompositableTransaction* aTxn) : mTxn(aTxn) {}
   ~AutoEndTransaction() { mTxn->End(); }
   CompositableTransaction* mTxn;
 };
 
 void ImageBridgeChild::UseTextures(
     CompositableClient* aCompositable,
-    const nsTArray<TimedTextureClient>& aTextures) {
+    const nsTArray<TimedTextureClient>& aTextures, wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aCompositable->GetIPCHandle());
   MOZ_ASSERT(aCompositable->IsConnected());
 
   AutoTArray<TimedTexture, 4> textures;
 
   for (auto& t : aTextures) {
     MOZ_ASSERT(t.mTextureClient);
@@ -315,17 +315,18 @@ 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,
+                      wr::kRenderRootUnknown);
   EndTransaction();
 }
 
 void ImageBridgeChild::UpdateAsyncCanvasRendererSync(
     SynchronousTask* aTask, AsyncCanvasRenderer* aWrapper) {
   AutoCompleteTask complete(aTask);
 
   UpdateAsyncCanvasRendererNow(aWrapper);
@@ -354,17 +355,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);
 
@@ -923,17 +925,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,
+    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
@@ -261,19 +261,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,
+                           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);
 
@@ -290,18 +290,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,
+      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
@@ -777,34 +777,34 @@ mozilla::ipc::IPCResult LayerTransaction
   }
 
   mCompositorBridge->SetTestAsyncZoom(GetId(), aScrollID,
                                       LayerToParentLayerScale(aValue));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult LayerTransactionParent::RecvFlushApzRepaints() {
-  mCompositorBridge->FlushApzRepaints(GetId());
+  mCompositorBridge->FlushApzRepaints(APZNodeId(GetId()));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult LayerTransactionParent::RecvGetAPZTestData(
     APZTestData* aOutData) {
   mCompositorBridge->GetAPZTestData(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<APZCGuid>&& aTargets) {
   mCompositorBridge->SetConfirmedTargetAPZC(GetId(), aBlockId, aTargets);
   return IPC_OK();
 }
 
 bool LayerTransactionParent::Attach(Layer* aLayer,
                                     CompositableHost* aCompositable,
                                     bool aIsAsync) {
   if (!aCompositable || !aLayer) {
--- a/gfx/layers/ipc/LayerTransactionParent.h
+++ b/gfx/layers/ipc/LayerTransactionParent.h
@@ -126,18 +126,17 @@ class LayerTransactionParent final : pub
       const float& aY) override;
   mozilla::ipc::IPCResult RecvSetAsyncZoom(
       const ScrollableLayerGuid::ViewID& aId, const float& aValue) override;
   mozilla::ipc::IPCResult RecvFlushApzRepaints() override;
   mozilla::ipc::IPCResult RecvGetAPZTestData(APZTestData* aOutData) override;
   mozilla::ipc::IPCResult RecvRequestProperty(const nsString& aProperty,
                                               float* aValue) override;
   mozilla::ipc::IPCResult RecvSetConfirmedTargetAPZC(
-      const uint64_t& aBlockId,
-      nsTArray<ScrollableLayerGuid>&& aTargets) override;
+      const uint64_t& aBlockId, nsTArray<APZCGuid>&& aTargets) override;
   mozilla::ipc::IPCResult RecvRecordPaintTimes(
       const PaintTiming& aTiming) override;
   mozilla::ipc::IPCResult RecvGetTextureFactoryIdentifier(
       TextureFactoryIdentifier* aIdentifier) override;
 
   bool SetLayerAttributes(const OpSetLayerAttributes& aOp);
 
   void ActorDestroy(ActorDestroyReason why) override;
--- a/gfx/layers/ipc/LayersMessageUtils.h
+++ b/gfx/layers/ipc/LayersMessageUtils.h
@@ -9,26 +9,28 @@
 
 #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/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
@@ -478,16 +480,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::APZCGuid> {
+  typedef mozilla::layers::APZCGuid 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);
--- 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::APZCGuid 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(APZCGuid 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, APZCGuid[] Targets);
 
-  async UpdateZoomConstraints(ScrollableLayerGuid aGuid, MaybeZoomConstraints aConstraints);
+  async UpdateZoomConstraints(APZCGuid 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(APZCGuid aGuid, AsyncDragMetrics aDragMetrics);
 
-  async StartAutoscroll(ScrollableLayerGuid aGuid, ScreenPoint aAnchorLocation);
+  async StartAutoscroll(APZCGuid aGuid, ScreenPoint aAnchorLocation);
 
-  async StopAutoscroll(ScrollableLayerGuid aGuid);
+  async StopAutoscroll(APZCGuid 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::APZCGuid 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, APZCGuid[] 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
@@ -11,73 +11,80 @@ 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::APZCGuid 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 {
 
 sync protocol PWebRenderBridge
 {
   manager PCompositorBridge;
 
 parent:
-  sync EnsureConnected()
+  sync EnsureConnected(RenderRoot aRenderRoot)
     returns (TextureFactoryIdentifier textureFactoryIdentifier, MaybeIdNamespace maybeIdNamespace);
 
   async NewCompositable(CompositableHandle handle, TextureInfo info);
   async ReleaseCompositable(CompositableHandle compositable);
 
   async DeleteCompositorAnimations(uint64_t[] aIds);
-  async SetDisplayList(IntSize aSize, WebRenderParentCommand[] commands, OpDestroy[] toDestroy, uint64_t fwdTransactionId, TransactionId transactionId,
-                       LayoutSize aContentSize, ByteBuf aDL, BuiltDisplayListDescriptor aDLDesc,
-                       WebRenderScrollData aScrollData,
-                       OpUpdateResource[] aResourceUpdates, RefCountedShmem[] aSmallShmems, Shmem[] aLargeShmems,
-                       IdNamespace aIdNamespace, bool containsSVGGroup, 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, 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);
+                         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, APZCGuid[] 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,17 @@ void ShadowLayerForwarder::UpdateTexture
 
   mTxn->AddNoSwapPaint(CompositableOperation(
       aCompositable->GetIPCHandle(),
       OpPaintTextureRegion(aThebesBufferData, aUpdatedRegion)));
 }
 
 void ShadowLayerForwarder::UseTextures(
     CompositableClient* aCompositable,
-    const nsTArray<TimedTextureClient>& aTextures) {
+    const nsTArray<TimedTextureClient>& aTextures, wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
   AutoTArray<TimedTexture, 4> textures;
 
@@ -478,17 +478,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,
+    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,
+      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,
+                           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
@@ -89,16 +89,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/MetricsSharingController.h',
     # exporting things from apz/src is temporary until we extract a
     # proper interface for the code there
     'apz/src/APZUtils.h',
@@ -237,16 +238,17 @@ EXPORTS.mozilla.layers += [
     'TextureSourceProvider.h',
     'TextureWrapperImage.h',
     'TransactionIdAllocator.h',
     'UpdateImageHelper.h',
     'wr/AsyncImagePipelineManager.h',
     'wr/ClipManager.h',
     'wr/IpcResourceUpdateQueue.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',
@@ -482,16 +484,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
@@ -35,51 +35,59 @@ AsyncImagePipelineManager::AsyncImagePip
 AsyncImagePipelineManager::PipelineUpdates::PipelineUpdates(
     RefPtr<wr::WebRenderPipelineInfo> aPipelineInfo,
     const uint64_t aUpdatesCount, const bool aRendered)
     : mPipelineInfo(aPipelineInfo),
       mUpdatesCount(aUpdatesCount),
       mRendered(aRendered) {}
 
 AsyncImagePipelineManager::AsyncImagePipelineManager(
-    already_AddRefed<wr::WebRenderAPI>&& aApi)
-    : 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 (int i = 0; i < wr::kRenderRootCount; ++i) {
+    SetWillGenerateFrame((wr::RenderRoot)i);
+  }
 }
 
-bool AsyncImagePipelineManager::GetAndResetWillGenerateFrame() {
+void AsyncImagePipelineManager::SetWillGenerateFrame(
+    wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
 
-  bool ret = mWillGenerateFrame;
-  mWillGenerateFrame = false;
+  mWillGenerateFrame[(int)aRenderRoot] = true;
+}
+
+bool AsyncImagePipelineManager::GetAndResetWillGenerateFrame(
+    wr::RenderRoot aRenderRoot) {
+  MOZ_ASSERT(CompositorThreadHolder::IsInCompositorThread());
+
+  bool ret = mWillGenerateFrame[(int)aRenderRoot];
+  mWillGenerateFrame[(int)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 +148,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) {
@@ -277,17 +287,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(wrTexture);
     // Ensure frame generation.
-    SetWillGenerateFrame();
+    SetWillGenerateFrame(aPipeline->mRenderRoot);
   } else {
     if (useWrTextureWrapper) {
       aPipeline->mWrTextureWrapper = new WebRenderTextureHostWrapper(this);
       aPipeline->mWrTextureWrapper->UpdateWebRenderTextureHost(wrTexture);
     }
     Range<wr::ImageKey> keys(&aKeys[0], aKeys.Length());
     auto externalImageKey =
         aPipeline->mWrTextureWrapper
@@ -337,35 +347,36 @@ AsyncImagePipelineManager::UpdateWithout
   }
 
   dSurf->Unmap();
 
   return Some(aOp);
 }
 
 void AsyncImagePipelineManager::ApplyAsyncImagesOfImageBridge(
-    wr::TransactionBuilder& aSceneBuilderTxn,
-    wr::TransactionBuilder& aFastTxn) {
+    nsTArray<wr::TransactionBuilder>& aSceneBuilderTxns,
+    nsTArray<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[(int)pipeline->mRenderRoot],
+                               aFastTxns[(int)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;
@@ -424,16 +435,17 @@ void AsyncImagePipelineManager::ApplyAsy
     }
 
     if (aPipeline->mUseExternalImage) {
       MOZ_ASSERT(aPipeline->mCurrentTexture->AsWebRenderTextureHost());
       Range<wr::ImageKey> range_keys(&keys[0], keys.Length());
       aPipeline->mCurrentTexture->PushDisplayItems(
           builder, wr::ToRoundedLayoutRect(rect), wr::ToRoundedLayoutRect(rect),
           aPipeline->mFilter, range_keys);
+
       HoldExternalImage(aPipelineId, aEpoch,
                         aPipeline->mCurrentTexture->AsWebRenderTextureHost());
     } else {
       MOZ_ASSERT(keys.Length() == 1);
       builder.PushImage(wr::ToRoundedLayoutRect(rect),
                         wr::ToRoundedLayoutRect(rect), true, aPipeline->mFilter,
                         keys[0]);
     }
@@ -448,24 +460,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[(int)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
@@ -662,21 +675,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,
@@ -83,56 +84,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(
+      nsTArray<wr::TransactionBuilder>& aSceneBuilderTxns,
+      nsTArray<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();
@@ -187,16 +192,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;
@@ -219,26 +225,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;
+  Array<bool, wr::kRenderRootCount> 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,52 @@ 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, size_t aChunkSize,
+    wr::RenderRoot aRenderRoot)
+    : mWriter(aAllocator, aChunkSize), mRenderRoot(aRenderRoot) {}
 
 IpcResourceUpdateQueue::IpcResourceUpdateQueue(
     IpcResourceUpdateQueue&& aOther) noexcept
     : mWriter(std::move(aOther.mWriter)),
-      mUpdates(std::move(aOther.mUpdates)) {}
+      mUpdates(std::move(aOther.mUpdates)),
+      mRenderRoot(aOther.mRenderRoot) {
+  for (uint32_t i = 0; i < wr::kRenderRootCount - 1; ++i) {
+    mSubQueues[i] = std::move(aOther.mSubQueues[i]);
+  }
+}
 
 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 (uint32_t i = 0; i < wr::kRenderRootCount - 1; ++i) {
+    mSubQueues[i] = std::move(aOther.mSubQueues[i]);
+  }
   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 +423,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, size_t aChunkSize = 57328,
+      wr::RenderRoot aRenderRoot = wr::RenderRoot::Default);
+
+  // Although resource updates don't belong to a particular document/render root
+  // in any concrete way, they still end up being tied to a render root because
+  // we need to know which WR document to generate a frame for when they change.
+  IpcResourceUpdateQueue& SubQueue(wr::RenderRoot aRenderRoot) {
+    MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default);
+    if (aRenderRoot == wr::RenderRoot::Default) {
+      MOZ_ASSERT(mRenderRoot == wr::RenderRoot::Default);
+      return *this;
+    }
+    int renderRootIndex = (int)aRenderRoot - 1;
+    if (!mSubQueues[renderRootIndex]) {
+      mSubQueues[renderRootIndex] = MakeUnique<IpcResourceUpdateQueue>(
+          mWriter.WrBridge(), mWriter.ChunkSize(), aRenderRoot);
+    }
+    return *mSubQueues[renderRootIndex];
+  }
+
+  bool HasAnySubQueue() {
+    for (int i = 1; i < wr::kRenderRootCount; ++i) {
+      if (mSubQueues[i - 1]) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  bool HasSubQueue(wr::RenderRoot aRenderRoot) {
+    return aRenderRoot == wr::RenderRoot::Default || !!mSubQueues[(int)aRenderRoot - 1];
+  }
+
+  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;
+  Array<UniquePtr<IpcResourceUpdateQueue>, wr::kRenderRootCount - 1> mSubQueues;
+  wr::RenderRoot mRenderRoot;
 };
 
 }  // namespace wr
 }  // namespace mozilla
 
 #endif
--- 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(); }
@@ -72,17 +66,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 +169,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);
 }
 
 wr::FontInstanceKey RenderRootStateManager::GetFontKeyForScaledFont(
     gfx::ScaledFont* aScaledFont, wr::IpcResourceUpdateQueue* aResources) {
-  return WrBridge()->GetFontKeyForScaledFont(aScaledFont, aResources);
+  return WrBridge()->GetFontKeyForScaledFont(aScaledFont, mRenderRoot,
+                                             aResources);
 }
 
 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
\ No newline at end of file
--- 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);
   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,67 @@
+#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, aParam.mDL);
+    WriteIPDLParam(aMsg, aActor, aParam.mDLDesc);
+    WriteIPDLParam(aMsg, aActor, aParam.mResourceUpdates);
+    WriteIPDLParam(aMsg, aActor, aParam.mSmallShmems);
+    WriteIPDLParam(aMsg, aActor, 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, aParam.mLargeShmems);
+}
+
+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)) {
+        return true;
+    }
+    return false;
+}
+
+} // namespace ipc
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/gfx/layers/wr/RenderRootTypes.h
@@ -0,0 +1,68 @@
+/* -*- 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;
+};
+
+}  // 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
@@ -86,16 +86,22 @@ StackingContextHelper::StackingContextHe
       mDeferredAncestorTransform = aParentSC.GetDeferredTransformMatrix();
     } else {
       // We are not deferring another transform, so we can just inherit the
       // parent stacking context's deferred data without any modification.
       mDeferredTransformItem = aParentSC.mDeferredTransformItem;
       mDeferredAncestorTransform = aParentSC.mDeferredAncestorTransform;
     }
   }
+
+  if (aParentSC.mSubContextHelper) {
+    mSubContextHelper = MakeUnique<StackingContextHelper>(
+        *aParentSC.mSubContextHelper, aAsr, aContainerFrame, aContainerItem,
+        *aParentSC.mSubContextHelper->mBuilder, aParams, aBounds);
+  }
 }
 
 StackingContextHelper::~StackingContextHelper() {
   if (mBuilder) {
     mSpaceAndClipChainHelper.reset();
     mBuilder->PopStackingContext(mReferenceFrameId.isSome());
   }
 }
--- a/gfx/layers/wr/StackingContextHelper.h
+++ b/gfx/layers/wr/StackingContextHelper.h
@@ -20,17 +20,17 @@ namespace mozilla {
 struct ActiveScrolledRoot;
 
 namespace layers {
 
 /**
  * This is a helper class that pushes/pops a stacking context, and manages
  * some of the coordinate space transformations needed.
  */
-class MOZ_RAII StackingContextHelper {
+class StackingContextHelper {
  public:
   StackingContextHelper(const StackingContextHelper& aParentSC,
                         const ActiveScrolledRoot* aAsr,
                         nsIFrame* aContainerFrame,
                         nsDisplayItem* aContainerItem,
                         wr::DisplayListBuilder& aBuilder,
                         const wr::StackingContextParams& aParams,
                         const LayoutDeviceRect& aBounds = LayoutDeviceRect());
@@ -42,16 +42,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; }
 
+  // We create a StackingContextHelper for the content render root to parallel
+  // the StackingContextHelper for the chrome render root. This results in an
+  // equivalent chain of SCs when we actually transition to the content render
+  // root in an nsDisplayRenderRoot.
+  void CreateSubContextHelper(const StackingContextHelper& aParentSC,
+                              const wr::StackingContextParams& aParams,
+                              wr::RenderRoot aRenderRoot) {
+    MOZ_ASSERT(!mSubContextHelper);
+    mSubContextHelper = MakeUnique<StackingContextHelper>(
+        aParentSC, nullptr, nullptr, nullptr, mBuilder->SubBuilder(aRenderRoot),
+        aParams);
+  }
+
+  const StackingContextHelper& SubContextHelper() const {
+    return *mSubContextHelper;
+  }
+
+  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; }
@@ -111,14 +132,15 @@ class MOZ_RAII StackingContextHelper {
   // behaviour of forcing a WebRenderLayerScrollData item to be generated when
   // the ASR changes is implemented in
   // WebRenderCommandBuilder::CreateWebRenderCommandsFromDisplayList.
   Maybe<nsDisplayTransform*> mDeferredTransformItem;
   Maybe<gfx::Matrix4x4> mDeferredAncestorTransform;
 
   bool mIsPreserve3D;
   bool mRasterizeLocally;
+  UniquePtr<StackingContextHelper> mSubContextHelper;
 };
 
 }  // namespace layers
 }  // namespace mozilla
 
 #endif /* GFX_STACKINGCONTEXTHELPER_H */
--- a/gfx/layers/wr/WebRenderBridgeChild.cpp
+++ b/gfx/layers/wr/WebRenderBridgeChild.cpp
@@ -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,146 +66,150 @@ 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::Count);
+  mParentCommands[(int)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, largeShmems);
+  this->SendUpdateResources(resourceUpdates, smallShmems, 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) {
+    renderRoot.mCommands =
+        std::move(mParentCommands[(int)renderRoot.mRenderRoot]);
+  }
 
-  this->SendSetDisplayList(aSize, mParentCommands, mDestroyedActors,
-                           GetFwdTransactionId(), aTransactionId, aContentSize,
-                           dlData, aDL.dl_desc, aScrollData, resourceUpdates,
-                           smallShmems, largeShmems, mIdNamespace,
+  this->SendSetDisplayList(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,
+    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) {
+    update.mCommands = std::move(mParentCommands[(int)update.mRenderRoot]);
   }
 
   this->SendEmptyTransaction(
-      aFocusTarget, aUpdates, aPaintSequenceNumber, mParentCommands,
-      mDestroyedActors, GetFwdTransactionId(), aTransactionId, resourceUpdates,
-      smallShmems, largeShmems, mIdNamespace, aVsyncId, aVsyncStartTime,
-      aRefreshStartTime, aTxnStartTime, aTxnURL, fwdTime);
-  mParentCommands.Clear();
+      aFocusTarget, aUpdates, aPaintSequenceNumber, 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 (int i = 0; i < wr::kRenderRootCount; ++i) {
+    if (!mParentCommands[i].IsEmpty()) {
+      this->SendParentCommands(mParentCommands[i], (wr::RenderRoot)i);
+      mParentCommands[i].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;
 };
 
@@ -234,40 +238,43 @@ 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);
 
-  wr::WrFontInstanceKey key = GetFontKeyForScaledFont(aFont);
+  wr::WrFontInstanceKey key =
+      GetFontKeyForScaledFont(aFont, aBuilder.GetRenderRoot());
   MOZ_ASSERT(key.mNamespace.mHandle && key.mHandle);
 
   aBuilder.PushText(aBounds, aClip, aBackfaceVisible, aColor, key, aGlyphs,
                     aGlyphOptions);
 }
 
 wr::FontInstanceKey WebRenderBridgeChild::GetFontKeyForScaledFont(
-    gfx::ScaledFont* aScaledFont, wr::IpcResourceUpdateQueue* aResources) {
+    gfx::ScaledFont* aScaledFont, wr::RenderRoot aRenderRoot,
+    wr::IpcResourceUpdateQueue* aResources) {
   MOZ_ASSERT(!mDestroyed);
   MOZ_ASSERT(aScaledFont);
   MOZ_ASSERT(aScaledFont->CanSerialize());
 
+  auto& fontInstanceKeys = mFontInstanceKeys[(int)aRenderRoot];
   wr::FontInstanceKey instanceKey = {wr::IdNamespace{0}, 0};
-  if (mFontInstanceKeys.Get(aScaledFont, &instanceKey)) {
+  if (fontInstanceKeys.Get(aScaledFont, &instanceKey)) {
     return instanceKey;
   }
 
   Maybe<wr::IpcResourceUpdateQueue> resources =
       aResources ? Nothing() : Some(wr::IpcResourceUpdateQueue(this));
   aResources = resources.ptrOr(aResources);
 
-  wr::FontKey fontKey =
-      GetFontKeyForUnscaledFont(aScaledFont->GetUnscaledFont(), aResources);
+  wr::FontKey fontKey = GetFontKeyForUnscaledFont(
+      aScaledFont->GetUnscaledFont(), aRenderRoot, aResources);
   wr::FontKey nullKey = {wr::IdNamespace{0}, 0};
   if (fontKey == nullKey) {
     return instanceKey;
   }
 
   instanceKey = GetNextFontInstanceKey();
 
   Maybe<wr::FontInstanceOptions> options;
@@ -276,70 +283,80 @@ wr::FontInstanceKey WebRenderBridgeChild
   aScaledFont->GetWRFontInstanceOptions(&options, &platformOptions,
                                         &variations);
 
   aResources->AddFontInstance(
       instanceKey, fontKey, aScaledFont->GetSize(), options.ptrOr(nullptr),
       platformOptions.ptrOr(nullptr),
       Range<const FontVariation>(variations.data(), variations.size()));
   if (resources.isSome()) {
-    UpdateResources(resources.ref());
+    UpdateResources(resources.ref(), aRenderRoot);
   }
 
-  mFontInstanceKeys.Put(aScaledFont, instanceKey);
+  fontInstanceKeys.Put(aScaledFont, instanceKey);
 
   return instanceKey;
 }
 
 wr::FontKey WebRenderBridgeChild::GetFontKeyForUnscaledFont(
-    gfx::UnscaledFont* aUnscaled, wr::IpcResourceUpdateQueue* aResources) {
+    gfx::UnscaledFont* aUnscaled, wr::RenderRoot aRenderRoot,
+    wr::IpcResourceUpdateQueue* aResources) {
   MOZ_ASSERT(!mDestroyed);
 
+  auto& fontKeys = mFontKeys[(int)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 fontKey;
     }
 
     if (resources.isSome()) {
-      UpdateResources(resources.ref());
+      UpdateResources(resources.ref(), aRenderRoot);
     }
 
-    mFontKeys.Put(aUnscaled, fontKey);
+    fontKeys.Put(aUnscaled, fontKey);
   }
 
   return fontKey;
 }
 
 void WebRenderBridgeChild::RemoveExpiredFontKeys(
     wr::IpcResourceUpdateQueue& aResourceUpdates) {
+  auto& fontInstanceKeys =
+      mFontInstanceKeys[(int)aResourceUpdates.GetRenderRoot()];
+  auto& fontKeys = mFontKeys[(int)aResourceUpdates.GetRenderRoot()];
+  auto& fontInstanceKeysDeleted =
+      mFontInstanceKeysDeleted[(int)aResourceUpdates.GetRenderRoot()];
+  auto& fontKeysDeleted =
+      mFontKeysDeleted[(int)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();
       }
     }
   }
 }
 
@@ -413,35 +430,37 @@ bool WebRenderBridgeChild::DestroyInTran
 }
 
 bool WebRenderBridgeChild::DestroyInTransaction(
     const CompositableHandle& aHandle) {
   return AddOpDestroy(OpDestroy(aHandle));
 }
 
 void WebRenderBridgeChild::RemoveTextureFromCompositable(
-    CompositableClient* aCompositable, TextureClient* aTexture) {
+    CompositableClient* aCompositable, TextureClient* aTexture,
+    wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(aCompositable);
   MOZ_ASSERT(aTexture);
   MOZ_ASSERT(aTexture->GetIPDLActor());
   MOZ_RELEASE_ASSERT(aTexture->GetIPDLActor()->GetIPCChannel() ==
                      GetIPCChannel());
   if (!aCompositable->IsConnected() || !aTexture->GetIPDLActor()) {
     // We don't have an actor anymore, don't try to use it!
     return;
   }
 
-  AddWebRenderParentCommand(CompositableOperation(
-      aCompositable->GetIPCHandle(),
-      OpRemoveTexture(nullptr, aTexture->GetIPDLActor())));
+  AddWebRenderParentCommand(
+      CompositableOperation(aCompositable->GetIPCHandle(),
+                            OpRemoveTexture(nullptr, aTexture->GetIPDLActor())),
+      aRenderRoot);
 }
 
 void WebRenderBridgeChild::UseTextures(
     CompositableClient* aCompositable,
-    const nsTArray<TimedTextureClient>& aTextures) {
+    const nsTArray<TimedTextureClient>& aTextures, wr::RenderRoot aRenderRoot) {
   MOZ_ASSERT(aCompositable);
 
   if (!aCompositable->IsConnected()) {
     return;
   }
 
   AutoTArray<TimedTexture, 4> textures;
 
@@ -454,17 +473,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();
@@ -484,18 +504,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 (int i = 0; i < wr::kRenderRootCount; ++i) {
+    mFontInstanceKeys[i].Clear();
+    mFontKeys[i].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
@@ -57,33 +57,32 @@ typedef ThreadSafeWeakPtrHashKey<gfx::Sc
 
 class WebRenderBridgeChild final : public PWebRenderBridgeChild,
                                    public CompositableForwarder {
   NS_INLINE_DECL_THREADSAFE_REFCOUNTING(WebRenderBridgeChild, override)
 
  public:
   explicit WebRenderBridgeChild(const wr::PipelineId& aPipelineId);
 
-  void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd);
+  void AddWebRenderParentCommand(const WebRenderParentCommand& aCmd,
+                                 wr::RenderRoot aRenderRoot);
 
-  void UpdateResources(wr::IpcResourceUpdateQueue& aResources);
+  void UpdateResources(wr::IpcResourceUpdateQueue& aResources,
+                       wr::RenderRoot aRenderRoot);
   void BeginTransaction();
-  void EndTransaction(const wr::LayoutSize& aContentSize,
-                      wr::BuiltDisplayList& dl,
-                      wr::IpcResourceUpdateQueue& aResources,
-                      const gfx::IntSize& aSize, 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);
+                      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();
@@ -96,24 +95,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; }
@@ -140,20 +143,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);
 
   wr::FontInstanceKey GetFontKeyForScaledFont(
-      gfx::ScaledFont* aScaledFont,
+      gfx::ScaledFont* aScaledFont, wr::RenderRoot aRenderRoot,
       wr::IpcResourceUpdateQueue* aResources = nullptr);
   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);
 
@@ -191,19 +194,21 @@ class WebRenderBridgeChild final : publi
       const SurfaceDescriptorTiles& aTiledDescriptor) override;
   void UpdateTextureRegion(CompositableClient* aCompositable,
                            const ThebesBufferData& aThebesBufferData,
                            const nsIntRegion& aUpdatedRegion) override;
   void ReleaseCompositable(const CompositableHandle& aHandle) override;
   bool DestroyInTransaction(PTextureChild* aTexture) override;
   bool DestroyInTransaction(const CompositableHandle& aHandle);
   void RemoveTextureFromCompositable(CompositableClient* aCompositable,
-                                     TextureClient* aTexture) override;
+                                     TextureClient* aTexture,
+                                     wr::RenderRoot aRenderRoot) override;
   void UseTextures(CompositableClient* aCompositable,
-                   const nsTArray<TimedTextureClient>& aTextures) override;
+                   const nsTArray<TimedTextureClient>& aTextures,
+                   wr::RenderRoot aRenderRoot) override;
   void UseComponentAlphaTextures(CompositableClient* aCompositable,
                                  TextureClient* aClientOnBlack,
                                  TextureClient* aClientOnWhite) override;
   void UpdateFwdTransactionId() override;
   uint64_t GetFwdTransactionId() override;
   bool InForwarderThread() override;
 
   void ActorDestroy(ActorDestroyReason why) override;
@@ -224,34 +229,34 @@ class WebRenderBridgeChild final : publi
   void ReleaseIPDLReference() {
     MOZ_ASSERT(mIPCOpen == true);
     mIPCOpen = false;
     Release();
   }
 
   bool AddOpDestroy(const OpDestroy& aOp);
 
-  nsTArray<WebRenderParentCommand> mParentCommands;
   nsTArray<OpDestroy> mDestroyedActors;
+  Array<nsTArray<WebRenderParentCommand>, wr::kRenderRootCount> 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;
+  Array<uint32_t, wr::kRenderRootCount> mFontKeysDeleted;
+  Array<nsDataHashtable<UnscaledFontHashKey, wr::FontKey>, wr::kRenderRootCount> mFontKeys;
 
-  uint32_t mFontInstanceKeysDeleted;
-  nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey> mFontInstanceKeys;
+  Array<uint32_t, wr::kRenderRootCount> mFontInstanceKeysDeleted;
+  Array<nsDataHashtable<ScaledFontHashKey, wr::FontInstanceKey>, wr::kRenderRootCount> mFontInstanceKeys;
 
   UniquePtr<ActiveResourceTracker> mActiveResourceTracker;
 
   RefCountedShmem mResourceShm;
 };
 
 }  // namespace layers
 }  // namespace mozilla
--- a/gfx/layers/wr/WebRenderBridgeParent.cpp
+++ b/gfx/layers/wr/WebRenderBridgeParent.cpp
@@ -271,31 +271,34 @@ 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::kRenderRootUnknown),
       mPaused(false),
       mDestroyed(false),
       mReceivedDisplayList(false),
       mIsFirstPaint(true),
       mSkippedComposite(false) {
   MOZ_ASSERT(mAsyncImageManager);
   MOZ_ASSERT(mAnimStorage);
   mAsyncImageManager->AddPipeline(mPipelineId, this);
@@ -307,40 +310,45 @@ WebRenderBridgeParent::WebRenderBridgePa
 
 WebRenderBridgeParent::WebRenderBridgeParent(const wr::PipelineId& aPipelineId)
     : mCompositorBridge(nullptr),
       mPipelineId(aPipelineId),
       mChildLayersObserverEpoch{0},
       mParentLayersObserverEpoch{0},
       mWrEpoch{0},
       mIdNamespace{0},
+      mRenderRootRectMutex(nullptr),
+      mRenderRoot(wr::kRenderRootUnknown),
       mPaused(false),
       mDestroyed(true),
       mReceivedDisplayList(false),
       mIsFirstPaint(false),
       mSkippedComposite(false) {}
 
 /* static */ WebRenderBridgeParent* WebRenderBridgeParent::CreateDestroyed(
     const wr::PipelineId& aPipelineId) {
   return new WebRenderBridgeParent(aPipelineId);
 }
 
 WebRenderBridgeParent::~WebRenderBridgeParent() {}
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvEnsureConnected(
+    const wr::RenderRoot& aRenderRoot,
     TextureFactoryIdentifier* aTextureFactoryIdentifier,
     MaybeIdNamespace* aMaybeIdNamespace) {
   if (mDestroyed) {
     *aTextureFactoryIdentifier =
         TextureFactoryIdentifier(LayersBackend::LAYERS_NONE);
     *aMaybeIdNamespace = Nothing();
     return IPC_OK();
   }
 
+  MOZ_ASSERT(mRenderRoot == wr::kRenderRootUnknown);
   MOZ_ASSERT(mIdNamespace.mHandle != 0);
+  mRenderRoot = aRenderRoot;
   *aTextureFactoryIdentifier = GetTextureFactoryIdentifier();
   *aMaybeIdNamespace = Some(mIdNamespace);
 
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvShutdown() {
   return HandleShutdown();
@@ -700,17 +708,17 @@ 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();
   }
 
   wr::TransactionBuilder txn;
   txn.SetLowPriority(!IsRootWebRenderBridgeParent());
@@ -719,17 +727,17 @@ mozilla::ipc::IPCResult WebRenderBridgeP
       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();
@@ -802,43 +810,49 @@ 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, APZNodeId(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(
+        rootLayersId,
+        APZNodeId(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(rootLayersId,
+                             APZNodeId(GetLayersId(), aRenderRoot),
+                             std::move(aUpdates), aPaintSequenceNumber);
   }
 }
 
 void WebRenderBridgeParent::SetAPZSampleTime() {
   CompositorBridgeParent* cbp = GetRootCompositorBridgeParent();
   if (!cbp) {
     return;
   }
@@ -850,26 +864,89 @@ 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[(int)aRenderRoot - 1] = aRect;
+      }
+      LayoutDeviceIntSize widgetSize = mWidget->GetClientSize();
+      LayoutDeviceIntRect rect = LayoutDeviceIntRect::FromUnknownRect(aRect);
+      rect.SetWidth(
+          std::max(0, std::min(widgetSize.width - rect.X(), rect.Width())));
+      rect.SetHeight(
+          std::max(0, std::min(widgetSize.height - rect.Y(), rect.Height())));
+      aTxn.SetWindowParameters(widgetSize, rect);
+    }
+    gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
+    aTxn.SetDisplayList(clearColor, aWrEpoch,
+                        LayerSize(aRect.width, aRect.height), mPipelineId,
+                        aContentSize, aDLDesc, dlData);
+
+    if (aObserveLayersUpdate) {
+      aTxn.Notify(wr::Checkpoint::SceneBuilt,
+                  MakeUnique<ScheduleObserveLayersUpdate>(
+                      mCompositorBridge, GetLayersId(),
+                      mChildLayersObserverEpoch, true));
+    }
+
+    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);
     }
@@ -887,83 +964,53 @@ mozilla::ipc::IPCResult WebRenderBridgeP
   // 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");
-  }
-
-  if (!UpdateResources(aResourceUpdates, aSmallShmems, aLargeShmems, txn)) {
-    return IPC_FAIL(this, "Failed to deserialize resource updates");
-  }
-
-  mReceivedDisplayList = true;
-
-  if (aScrollData.IsFirstPaint()) {
+  if (aDisplayLists[0].mScrollData.IsFirstPaint()) {
     mIsFirstPaint = true;
   }
 
   // aScrollData is moved into this function but that is not reflected by the
   // function signature due to the way the IPDL generator works. We remove the
   // const so that we can move this structure all the way to the desired
   // destination.
   // Also note that this needs to happen before the display list transaction is
   // sent to WebRender, so that the UpdateHitTestingTree call is guaranteed to
   // be in the updater queue at the time that the scene swap completes.
-  UpdateAPZScrollData(wrEpoch,
-                      std::move(const_cast<WebRenderScrollData&>(aScrollData)));
-
-  wr::Vec<uint8_t> dlData(std::move(dl));
-
-  bool observeLayersUpdate = ShouldParentObserveEpoch();
+  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.SetWindowParameters(widgetSize, docRect);
-    }
-    gfx::Color clearColor(0.f, 0.f, 0.f, 0.f);
-    txn.SetDisplayList(clearColor, wrEpoch,
-                       LayerSize(aSize.width, aSize.height), mPipelineId,
-                       aContentSize, dlDesc, dlData);
+  bool validTransaction = aIdNamespace == mIdNamespace;
 
-    if (observeLayersUpdate) {
-      txn.Notify(wr::Checkpoint::SceneBuilt,
-                 MakeUnique<ScheduleObserveLayersUpdate>(
-                     mCompositorBridge, GetLayersId(),
-                     mChildLayersObserverEpoch, true));
+  wr::TransactionBuilder txns[wr::kRenderRootCount];
+  Maybe<wr::AutoTransactionSender> senders[wr::kRenderRootCount];
+  for (auto& displayList : aDisplayLists) {
+    if (!SetDisplayList(displayList.mRenderRoot, displayList.mRect,
+                        displayList.mCommands, displayList.mContentSize,
+                        std::move(displayList.mDL), displayList.mDLDesc,
+                        displayList.mResourceUpdates, displayList.mSmallShmems,
+                        displayList.mLargeShmems, aTxnStartTime,
+                        txns[(int)displayList.mRenderRoot],
+                        senders[(int)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;
@@ -973,30 +1020,30 @@ 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 FocusTarget& aFocusTarget, const ScrollUpdatesMap& aScrollUpdates,
     const uint32_t& aPaintSequenceNumber,
-    InfallibleTArray<WebRenderParentCommand>&& aCommands,
+    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();
@@ -1009,177 +1056,216 @@ mozilla::ipc::IPCResult WebRenderBridgeP
   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;
+  bool scheduleComposite[wr::kRenderRootCount] = {};
 
   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
+  if (!aScrollUpdates.empty()) {
+    // aScrollUpdates 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);
+    UpdateAPZScrollOffsets(
+        std::move(const_cast<ScrollUpdatesMap&>(aScrollUpdates)),
+        aPaintSequenceNumber,
+        // TODO: split these out by actual render root
+        wr::RenderRoot::Default);
   }
 
-  wr::TransactionBuilder txn;
-  txn.SetLowPriority(!IsRootWebRenderBridgeParent());
+  wr::TransactionBuilder txns[wr::kRenderRootCount];
+  for (auto& update : aRenderRootUpdates) {
+    txns[(int)update.mRenderRoot].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[(int)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[(int)update.mRenderRoot],
+                                          update.mRenderRoot)) {
+        return IPC_FAIL(this, "Invalid parent command found");
+      }
+    }
+
+    if (ShouldParentObserveEpoch()) {
+      txns[(int)update.mRenderRoot].Notify(
+          wr::Checkpoint::SceneBuilt, MakeUnique<ScheduleObserveLayersUpdate>(
+                                          mCompositorBridge, GetLayersId(),
+                                          mChildLayersObserverEpoch, true));
+    }
   }
 
-  if (txn.IsResourceUpdatesEmpty()) {
+  bool rollbackEpoch = true;
+  for (auto& update : aRenderRootUpdates) {
+    auto& txn = txns[(int)update.mRenderRoot];
+    if (!txn.IsResourceUpdatesEmpty()) {
+      // There are resource updates, then we update Epoch of transaction.
+      txn.UpdateEpoch(mPipelineId, mWrEpoch);
+      scheduleComposite[(int)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;
+  bool scheduleAllComposite = true;
+
+  for (int i = 0; i < wr::kRenderRootCount; ++i) {
+    if (!txns[i].IsEmpty()) {
+      Api((wr::RenderRoot)i)->SendTransaction(txns[i]);
+    }
+    if (scheduleComposite[i]) {
+      scheduleAnyComposite = true;
+    } else {
+      scheduleAllComposite = false;
+    }
   }
 
+  // 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 (int i = 0; i < wr::kRenderRootCount; ++i) {
+    if (scheduleComposite[i]) {
+      mAsyncImageManager->SetWillGenerateFrame((wr::RenderRoot)i);
+    }
+  }
+
+  if (scheduleAnyComposite) {
+    ScheduleGenerateFrame(wr::kRenderRootUnknown);
   } 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;
       }
@@ -1189,17 +1275,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;
           }
         }
@@ -1216,31 +1303,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
@@ -1256,17 +1343,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);
@@ -1309,31 +1396,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[(int)aRenderRoot];
+
+  MOZ_ASSERT(asyncCompositables.find(wr::AsUint64(aPipelineId)) ==
+             asyncCompositables.end());
 
   RefPtr<CompositableHost> host;
   if (aAsync) {
     RefPtr<ImageBridgeParent> imageBridge =
         ImageBridgeParent::GetInstance(OtherPid());
     if (!imageBridge) {
       return;
     }
@@ -1353,47 +1444,51 @@ void WebRenderBridgeParent::AddPipelineI
   }
 
   if (!wrHost) {
     return;
   }
 
   wrHost->SetWrBridge(this);
   wrHost->EnableUseAsyncImagePipeline();
-  mAsyncCompositables.emplace(wr::AsUint64(aPipelineId), wrHost);
-  mAsyncImageManager->AddAsyncImagePipeline(aPipelineId, wrHost);
+  asyncCompositables.emplace(wr::AsUint64(aPipelineId), wrHost);
+  mAsyncImageManager->AddAsyncImagePipeline(aPipelineId, wrHost,
+                                            RenderRootForExternal(aRenderRoot));
 
   // If this is being called from WebRenderBridgeParent::RecvSetDisplayList,
   // then aTxn might contain a display list that references pipelines that
   // we just added to the async image manager.
   // If we send the display list alone then WR will not yet have the content for
   // the pipelines and so it will emit errors; the SetEmptyDisplayList call
   // below ensure that we provide its content to WR as part of the same
   // transaction.
   mAsyncImageManager->SetEmptyDisplayList(aPipelineId, aTxn,
                                           aTxnForImageBridge);
   return;
 }
 
 void WebRenderBridgeParent::RemovePipelineIdForCompositable(
-    const wr::PipelineId& aPipelineId, wr::TransactionBuilder& aTxn) {
+    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[(int)aRenderRoot];
+
+  auto it = asyncCompositables.find(wr::AsUint64(aPipelineId));
+  if (it == asyncCompositables.end()) {
     return;
   }
   RefPtr<WebRenderImageHost>& wrHost = it->second;
 
   wrHost->ClearWrBridge();
   mAsyncImageManager->RemoveAsyncImagePipeline(aPipelineId, aTxn);
   aTxn.RemovePipeline(aPipelineId);
-  mAsyncCompositables.erase(wr::AsUint64(aPipelineId));
+  asyncCompositables.erase(wr::AsUint64(aPipelineId));
   return;
 }
 
 void WebRenderBridgeParent::DeleteImage(const ImageKey& aKey,
                                         wr::TransactionBuilder& aUpdates) {
   if (mDestroyed) {
     return;
   }
@@ -1435,56 +1530,64 @@ 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 (int i = 0; i < wr::kRenderRootCount; ++i) {
+    if (i == 0 || (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((wr::RenderRoot)i)->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
@@ -1492,46 +1595,53 @@ 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 (int i = 0; i < wr::kRenderRootCount; ++i) {
+    if (i == 0 || (IsRootWebRenderBridgeParent() &&
+                   gfxPrefs::WebRenderSplitRenderRoots())) {
+      wr::TransactionBuilder fastTxn(/* aUseSceneBuilderThread */ false);
+      fastTxn.InvalidateRenderedFrame();
+      Api((wr::RenderRoot)i)->SendTransaction(fastTxn);
+    }
+  }
 
-  ScheduleGenerateFrame();
+  ScheduleGenerateFrameAllRenderRoots();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvCapture() {
   if (!mDestroyed) {
-    mApi->Capture();
+    for (auto& api : mApis) {
+      api->Capture();
+    }
   }
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSyncWithCompositor() {
   FlushSceneBuilds();
   if (RefPtr<WebRenderBridgeParent> root = GetRootWebRenderBridgeParent()) {
     root->FlushFrameGeneration();
@@ -1542,17 +1652,17 @@ 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<APZCGuid>&& aTargets) {
   if (mDestroyed) {
     return IPC_OK();
   }
   mCompositorBridge->SetConfirmedTargetAPZC(GetLayersId(), aBlockId, aTargets);
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvSetTestSampleTime(
@@ -1605,17 +1715,17 @@ mozilla::ipc::IPCResult WebRenderBridgeP
                                       LayerToParentLayerScale(aZoom));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvFlushApzRepaints() {
   if (mDestroyed) {
     return IPC_OK();
   }
-  mCompositorBridge->FlushApzRepaints(GetLayersId());
+  mCompositorBridge->FlushApzRepaints(APZNodeId(GetLayersId(), mRenderRoot));
   return IPC_OK();
 }
 
 mozilla::ipc::IPCResult WebRenderBridgeParent::RecvGetAPZTestData(
     APZTestData* aOutData) {
   mCompositorBridge->GetAPZTestData(GetLayersId(), aOutData);
   return IPC_OK();
 }
@@ -1644,30 +1754,35 @@ 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) {
+    Array<nsTArray<wr::WrOpacityProperty>, wr::kRenderRootCount>&
+        aOpacityArrays,
+    Array<nsTArray<wr::WrTransformProperty>, wr::kRenderRootCount>&
+        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[(int)renderRoot];
+      auto& opacityArray = aOpacityArrays[(int)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;
 }
 
@@ -1690,18 +1805,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) {
@@ -1726,67 +1841,90 @@ 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);
-
+  AutoTArray<wr::TransactionBuilder, wr::kRenderRootCount> fastTxns;
   // Handle transaction that is related to DisplayList.
-  wr::TransactionBuilder sceneBuilderTxn;
-  wr::AutoTransactionSender sender(mApi, &sceneBuilderTxn);
+  AutoTArray<wr::TransactionBuilder, wr::kRenderRootCount> sceneBuilderTxns;
+  Maybe<wr::AutoTransactionSender> senders[wr::kRenderRootCount];
+  for (uint32_t i = 0; i < mApis.Length(); ++i) {
+    // 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.AppendElement(false /* useSceneBuilderThread */);
+    auto sceneBuilderTxn = sceneBuilderTxns.AppendElement();
+    senders[i].emplace(mApis[i], sceneBuilderTxn);
+  }
 
   // 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;
+  bool generateFrame[wr::kRenderRootCount];
+  for (uint32_t i = 0; i < mApis.Length(); ++i) {
+    generateFrame[i] =
+        mAsyncImageManager->GetAndResetWillGenerateFrame((wr::RenderRoot)i) ||
+        !fastTxns[i].IsEmpty() || aForceGenerateFrame;
+    if (generateFrame[i]) {
+      framesGenerated++;
+    }
+  }
+
+  if (framesGenerated == 0) {
     // Could skip generating frame now.
     mPreviousFrameTimeStamp = TimeStamp();
     return;
   }
 
-  nsTArray<wr::WrOpacityProperty> opacityArray;
-  nsTArray<wr::WrTransformProperty> transformArray;
+  Array<nsTArray<wr::WrOpacityProperty>, wr::kRenderRootCount> opacityArrays;
+  Array<nsTArray<wr::WrTransformProperty>, wr::kRenderRootCount>
+      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 (uint32_t i = 0; i < mApis.Length(); ++i) {
+    fastTxns[i].UpdateDynamicProperties(opacityArrays[i], transformArrays[i]);
+  }
 
   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 (uint32_t i = 0; i < mApis.Length(); ++i) {
+    if (generateFrame[i]) {
+      fastTxns[i].GenerateFrame();
+      mApis[i]->SendTransaction(fastTxns[i]);
+    }
+  }
   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,
@@ -1794,16 +1932,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 (int i = 1; i < wr::kRenderRootCount; ++i) {
+    if (mRenderRootRects[i - 1].Contains(aPoint)) {
+      return do_AddRef(Api((wr::RenderRoot)i));
+    }
+  }
+  return do_AddRef(Api(wr::RenderRoot::Default));
+}
+
 TransactionId WebRenderBridgeParent::LastPendingTransactionId() {
   TransactionId id{0};
   if (!mPendingTransactionIds.empty()) {
     id = mPendingTransactionIds.back().mId;
   }
   return id;
 }
 
@@ -1813,23 +1962,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 ||
@@ -1895,17 +2044,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(
@@ -1932,19 +2082,28 @@ 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(wr::RenderRoot aRenderRoot) {
+  if (mCompositorScheduler) {
+    if (aRenderRoot != wr::kRenderRootUnknown) {
+      mAsyncImageManager->SetWillGenerateFrame(aRenderRoot);
+    }
     mCompositorScheduler->ScheduleComposition();
   }
 }
 
 void WebRenderBridgeParent::FlushRendering(bool aWaitForPresent) {
   if (mDestroyed) {
     return;
   }
@@ -1959,92 +2118,103 @@ void WebRenderBridgeParent::FlushRenderi
 }
 
 void WebRenderBridgeParent::Pause() {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 #ifdef MOZ_WIDGET_ANDROID
   if (!IsRootWebRenderBridgeParent() || mDestroyed) {
     return;
   }
-  mApi->Pause();
+
+  for (int i = 0; i < mApis.Length(); ++i) {
+    Api((wr::RenderRoot)i)->Pause();
+  }
 #endif
   mPaused = true;
 }
 
 bool WebRenderBridgeParent::Resume() {
   MOZ_ASSERT(IsRootWebRenderBridgeParent());
 #ifdef MOZ_WIDGET_ANDROID
   if (!IsRootWebRenderBridgeParent() || mDestroyed) {
     return false;
   }
 
-  if (!mApi->Resume()) {
-    return false;
+  for (int i = 0; i < mApis.Length(); ++i) {
+    if (!Api((wr::RenderRoot)i)->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();
-    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);
+
+  wr::TransactionBuilder txn;
+  txn.SetLowPriority(true);
+  txn.ClearDisplayList(wrEpoch, mPipelineId);
 
-  mApi->SendTransaction(txn);
+  for (uint32_t i = 0; i < mApis.Length(); ++i) {
+    if (i > 0 && !IsRootWebRenderBridgeParent()) {
+      continue;
+    }
+    for (const auto& entry : mAsyncCompositables[i]) {
+      wr::PipelineId pipelineId = wr::AsPipelineId(entry.first);
+      RefPtr<WebRenderImageHost> host = entry.second;
+      host->ClearWrBridge();
+      mAsyncImageManager->RemoveAsyncImagePipeline(pipelineId, txn);
+      txn.RemovePipeline(pipelineId);
+    }
+    mAsyncCompositables[i].clear();
+    txn.RemovePipeline(mPipelineId);
+    mApis[i]->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;
   }
 
@@ -2096,22 +2266,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
@@ -50,97 +50,105 @@ class WebRenderBridgeParent final : publ
                                     public CompositorVsyncSchedulerOwner,
                                     public CompositableParentManager,
                                     public layers::FrameRecorder {
  public:
   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);
+
+  void GetWebRenderAPIs(nsTArray<RefPtr<wr::WebRenderAPI>>& aOutAPIs) {
+    for (auto& api : mApis) {
+      aOutAPIs.AppendElement(api->Clone());
+    }
   }
+
+  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;
   }
 
   mozilla::ipc::IPCResult RecvEnsureConnected(
+      const wr::RenderRoot& aRenderRoot,
       TextureFactoryIdentifier* aTextureFactoryIdentifier,
       MaybeIdNamespace* aMaybeIdNamespace) override;
 
   mozilla::ipc::IPCResult RecvNewCompositable(
       const CompositableHandle& aHandle, const TextureInfo& aInfo) override;
   mozilla::ipc::IPCResult RecvReleaseCompositable(
       const CompositableHandle& aHandle) override;
 
   mozilla::ipc::IPCResult RecvShutdown() override;
   mozilla::ipc::IPCResult RecvShutdownSync() override;
   mozilla::ipc::IPCResult RecvDeleteCompositorAnimations(
       InfallibleTArray<uint64_t>&& aIds) override;
   mozilla::ipc::IPCResult RecvUpdateResources(
       nsTArray<OpUpdateResource>&& aUpdates,
       nsTArray<RefCountedShmem>&& aSmallShmems,
-      nsTArray<ipc::Shmem>&& aLargeShmems) override;
+      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 ScrollUpdatesMap& aUpdates,
+      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;
+      const uint64_t& aBlockId, nsTArray<APZCGuid>&& 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,
@@ -210,30 +218,33 @@ class WebRenderBridgeParent final : publ
    * 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(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
@@ -242,28 +253,68 @@ class WebRenderBridgeParent final : publ
    * 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;
+  APZNodeId GetAPZNodeId() const;
 
  private:
   class ScheduleSharedSurfaceRelease;
 
   explicit WebRenderBridgeParent(const wr::PipelineId& aPipelineId);
   virtual ~WebRenderBridgeParent();
 
+  wr::WebRenderAPI* Api(wr::RenderRoot aRenderRoot) {
+    if (IsRootWebRenderBridgeParent()) {
+      MOZ_ASSERT(aRenderRoot < wr::kRenderRootUnknown);
+      return mApis[(int)aRenderRoot];
+    } else {
+      MOZ_ASSERT(aRenderRoot == wr::RenderRoot::Default);
+      return mApis[(int)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(
@@ -277,36 +328,40 @@ class WebRenderBridgeParent final : publ
                                    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(Array<nsTArray<wr::WrOpacityProperty>,
+                              wr::kRenderRootCount>& aOpacityArrays,
+                        Array<nsTArray<wr::WrTransformProperty>,
+                              wr::kRenderRootCount>& aTransformArrays);
 
   CompositorBridgeParent* GetRootCompositorBridgeParent() const;
 
   RefPtr<WebRenderBridgeParent> GetRootWebRenderBridgeParent() const;
 
   // Tell APZ what the subsequent sampling's timestamp should be.
   void SetAPZSampleTime();
 
@@ -375,25 +430,27 @@ class WebRenderBridgeParent final : publ
 
     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;
+  Array<std::unordered_map<uint64_t, RefPtr<WebRenderImageHost>>,
+        wr::kRenderRootCount>
+      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
@@ -404,16 +461,22 @@ class WebRenderBridgeParent final : publ
   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;
+  Array<IntRect, wr::kRenderRootCount - 1> 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
@@ -294,17 +294,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) {
@@ -325,17 +325,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) {
@@ -626,17 +626,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;
@@ -645,17 +645,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) {
                 BlobFont font = {
                     aWrManager->WrBridge()->GetFontKeyForScaledFont(
-                        scaled, &aResources),
+                        scaled, aBuilder.GetRenderRoot(), &aResources),
                     scaled};
                 aStream.write((const char*)&font, sizeof(font));
               }
               fonts = std::move(aScaledFonts);
             });
 
     RefPtr<gfx::DrawTarget> dummyDt = gfx::Factory::CreateDrawTarget(
         gfx::BackendType::SKIA, gfx::IntSize(1, 1), format);
@@ -668,64 +668,68 @@ 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);
     Range<uint8_t> bytes((uint8_t*)recorder->mOutputStream.mData,
                          recorder->mOutputStream.mLength);
     if (!mKey) {
       if (!hasItems)  // we don't want to send a new image that doesn't have any
                       // items in it
         return;
       wr::BlobImageKey key =
           wr::BlobImageKey{aWrManager->WrBridge()->GetNextImageKey()};
       GP("No previous key making new one %d\n", key._0.mHandle);
       wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
       MOZ_RELEASE_ASSERT(bytes.length() > sizeof(size_t));
       if (!aResources.AddBlobImage(key, descriptor, bytes)) {
         return;
       }
-      mKey = Some(key);
+      mKey = Some(MakePair(aBuilder.GetRenderRoot(), key));
     } else {
       wr::ImageDescriptor descriptor(dtSize, 0, dt->GetFormat(), opacity);
       auto bottomRight = mInvalidRect.BottomRight();
       GP("check invalid %d %d - %d %d\n", bottomRight.x, bottomRight.y,
          dtSize.width, dtSize.height);
       MOZ_RELEASE_ASSERT(bottomRight.x <= dtSize.width &&
                          bottomRight.y <= dtSize.height);
       GP("Update Blob %d %d %d %d\n", mInvalidRect.x, mInvalidRect.y,
          mInvalidRect.width, mInvalidRect.height);
-      if (!aResources.UpdateBlobImage(mKey.value(), descriptor, bytes,
+      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);
@@ -741,17 +745,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;
@@ -991,16 +995,68 @@ void Grouper::PaintContainerItem(DIGroup
 
     default:
       aGroup->PaintItemRange(this, aChildren->GetBottom(), nullptr, aContext,
                              aRecorder);
       break;
   }
 }
 
+void WebRenderScrollDataCollection::AppendRoot(
+    Maybe<wr::WrAnimationProperty>& aZoomProp,
+    Maybe<ScrollMetadata>& aRootMetadata,
+    nsTArray<WebRenderScrollData>& aScrollDatas) {
+  mSeenRenderRoot[(int)wr::RenderRoot::Default] = true;
+
+  for (int i = 0; i < wr::kRenderRootCount; ++i) {
+    if (mSeenRenderRoot[i]) {
+      auto& layerScrollData = mInternalScrollDatas[i];
+      layerScrollData.emplace_back();
+      layerScrollData.back().InitializeRoot(layerScrollData.size() - 1);
+
+      if (aZoomProp) {
+        layerScrollData.back().SetZoomAnimationId(aZoomProp->id);
+      }
+
+      if (aRootMetadata) {
+        layerScrollData.back().AppendScrollMetadata(aScrollDatas[i],
+                                                    aRootMetadata.ref());
+      }
+    }
+  }
+}
+
+void WebRenderScrollDataCollection::AppendNode(
+    const wr::DisplayListBuilder& aBuilder, WebRenderLayerManager* aManager,
+    nsDisplayItem* aItem,
+    const Array<size_t, wr::kRenderRootCount>& aLayerCounts,
+    const ActiveScrolledRoot* aStopAtAsr,
+    const Maybe<gfx::Matrix4x4>& aAncestorTransform) {
+  wr::RenderRoot renderRoot = aBuilder.GetRenderRoot();
+  mSeenRenderRoot[(int)renderRoot] = true;
+  AppendNode(renderRoot, aManager, aItem, aLayerCounts, aStopAtAsr,
+             aAncestorTransform);
+}
+
+void WebRenderScrollDataCollection::AppendNode(
+    wr::RenderRoot aRenderRoot, WebRenderLayerManager* aManager,
+    nsDisplayItem* aItem,
+    const Array<size_t, wr::kRenderRootCount>& aLayerCounts,
+    const ActiveScrolledRoot* aStopAtAsr,
+    const Maybe<gfx::Matrix4x4>& aAncestorTransform) {
+  int renderRootIndex = (int)aRenderRoot;
+  int descendents = mInternalScrollDatas[renderRootIndex].size() -
+                    aLayerCounts[renderRootIndex];
+
+  mInternalScrollDatas[renderRootIndex].emplace_back();
+  mInternalScrollDatas[renderRootIndex].back().Initialize(
+      aManager->GetScrollData(aRenderRoot), aItem, descendents, aStopAtAsr,
+      aAncestorTransform, aRenderRoot);
+}
+
 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; }
@@ -1094,29 +1150,30 @@ void Grouper::ConstructGroups(nsDisplayL
       {
         auto spaceAndClipChain = mClipManager.SwitchItem(item, aSc);
         wr::SpaceAndClipChainHelper saccHelper(aBuilder, spaceAndClipChain);
 
         sIndent++;
         // Note: this call to CreateWebRenderCommands can recurse back into
         // this function.
         RenderRootStateManager* manager =
-            aCommandBuilder->mManager->GetRenderRootStateManager();
+            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 building the display list for.
       if (!groupData->mFollowingGroup.mGroupBounds.IsEqualEdges(
               currentGroup->mGroupBounds) ||
@@ -1130,17 +1187,18 @@ void Grouper::ConstructGroups(nsDisplayL
           GP("app unit change following: %d %d\n",
              groupData->mFollowingGroup.mAppUnitsPerDevPixel,
              currentGroup->mAppUnitsPerDevPixel);
         }
         // The group changed size
         GP("Inner group size change\n");
         groupData->mFollowingGroup.ClearItems();
         groupData->mFollowingGroup.ClearImageKey(
-            aCommandBuilder->mManager->GetRenderRootStateManager());
+            aCommandBuilder->mManager->GetRenderRootStateManager(
+                aBuilder.GetRenderRoot()));
       }
       groupData->mFollowingGroup.mGroupBounds = currentGroup->mGroupBounds;
       groupData->mFollowingGroup.mAppUnitsPerDevPixel =
           currentGroup->mAppUnitsPerDevPixel;
       groupData->mFollowingGroup.mLayerBounds = currentGroup->mLayerBounds;
       groupData->mFollowingGroup.mImageBounds = currentGroup->mImageBounds;
       groupData->mFollowingGroup.mClippedImageBounds =
           currentGroup->mClippedImageBounds;
@@ -1270,39 +1328,41 @@ static mozilla::gfx::IntRect ScaleToOuts
           aOffset.x),
       NSToIntCeil(
           NSAppUnitsToFloatPixels(aRect.YMost(), float(aAppUnitsPerPixel)) *
               aYScale +
           aOffset.y));
   return rect;
 }
 
-RenderRootStateManager* WebRenderCommandBuilder::GetRenderRootStateManager() {
-  return mManager->GetRenderRootStateManager();
+RenderRootStateManager* WebRenderCommandBuilder::GetRenderRootStateManager(
+    wr::RenderRoot aRenderRoot) {
+  return mManager->GetRenderRootStateManager(aRenderRoot);
 }
 
 void WebRenderCommandBuilder::DoGroupingForDisplayList(
     nsDisplayList* aList, nsDisplayItem* aWrappingItem,
     nsDisplayListBuilder* aDisplayListBuilder, const StackingContextHelper& aSc,
     wr::DisplayListBuilder& aBuilder, wr::IpcResourceUpdateQueue& aResources) {
   if (!aList->GetBottom()) {
     return;
   }
 
   GP("DoGroupingForDisplayList\n");
 
-  mClipManager.BeginList(aSc);
+  mCurrentClipManager->BeginList(aSc);
   Grouper g(mClipManager);
 
   int32_t appUnitsPerDevPixel =
       aWrappingItem->Frame()->PresContext()->AppUnitsPerDevPixel();
 
   g.mDisplayListBuilder = aDisplayListBuilder;
   RefPtr<WebRenderGroupData> groupData =
-      CreateOrRecycleWebRenderUserData<WebRenderGroupData>(aWrappingItem);
+      CreateOrRecycleWebRenderUserData<WebRenderGroupData>(
+          aWrappingItem, aBuilder.GetRenderRoot());
 
   bool snapped;
   nsRect groupBounds = aWrappingItem->GetBounds(aDisplayListBuilder, &snapped);
   // We don't want to restrict the size of the blob to the building rect of the
   // display item, since that will change when we scroll and trigger a resize
   // invalidation of the blob (will be fixed by blob recoordination).
   // Instead we retrieve the bounds of the overflow clip on the <svg> and use
   // that to restrict our size and prevent invisible content from affecting
@@ -1349,17 +1409,18 @@ void WebRenderCommandBuilder::DoGrouping
     }
 
     if (group.mResidualOffset != residualOffset) {
       GP(" Residual Offset %f %f -> %f %f\n", group.mResidualOffset.x,
          group.mResidualOffset.y, residualOffset.x, residualOffset.y);
     }
 
     group.ClearItems();
-    group.ClearImageKey(mManager->GetRenderRootStateManager());
+    group.ClearImageKey(
+        mManager->GetRenderRootStateManager(aBuilder.GetRenderRoot()));
   }
 
   ScrollableLayerGuid::ViewID scrollId = ScrollableLayerGuid::NULL_SCROLL_ID;
   if (const ActiveScrolledRoot* asr = aWrappingItem->GetActiveScrolledRoot()) {
     scrollId = asr->GetViewId();
   }
 
   g.mAppUnitsPerDevPixel = appUnitsPerDevPixel;
@@ -1383,17 +1444,17 @@ void WebRenderCommandBuilder::DoGrouping
   // adjustment.
   group.mPaintRect = group.mPaintRect - group.mLayerBounds.TopLeft();
   g.mTransform = Matrix::Scaling(scale.width, scale.height)
                      .PostTranslate(residualOffset.x, residualOffset.y);
   group.mScale = scale;
   group.mScrollId = scrollId;
   g.ConstructGroups(aDisplayListBuilder, this, aBuilder, aResources, &group,
                     aList, aSc);
-  mClipManager.EndList(aSc);
+  mCurrentClipManager->EndList(aSc);
 }
 
 void WebRenderCommandBuilder::Destroy() {
   mLastCanvasDatas.Clear();
   ClearCachedResources();
 }
 
 void WebRenderCommandBuilder::EmptyTransaction() {
@@ -1409,27 +1470,40 @@ void WebRenderCommandBuilder::EmptyTrans
 
 bool WebRenderCommandBuilder::NeedsEmptyTransaction() {
   return !mLastCanvasDatas.IsEmpty();
 }
 
 void WebRenderCommandBuilder::BuildWebRenderCommands(
     wr::DisplayListBuilder& aBuilder,
     wr::IpcResourceUpdateQueue& aResourceUpdates, nsDisplayList* aDisplayList,
-    nsDisplayListBuilder* aDisplayListBuilder, WebRenderScrollData& aScrollData,
-    wr::LayoutSize& aContentSize, nsTArray<wr::FilterOp>&& aFilters) {
+    nsDisplayListBuilder* aDisplayListBuilder,
+    nsTArray<WebRenderScrollData>& aScrollDatas,
+    nsTArray<wr::FilterOp>&& aFilters) {
   StackingContextHelper sc;
-  aScrollData = WebRenderScrollData(mManager);
-  MOZ_ASSERT(mLayerScrollData.empty());
+  StackingContextHelper sc2;